TIL(Today I Learned)/스파르타 내배캠

[TIL] 23/12/04 React 심화: 개인과제 이슈 모음(인증서비스가 들어간 팬레터함, 새로고침 이슈 등)

개발자먼지 2023. 12. 4. 23:37
반응형

강의를 다시 들으면서

하나하나 구현하니 오래 걸리긴하지만.. 하나도 기억이 안났는 걸..?

그래도 조금 많이 이해 완료

큰 이슈는 새로고침 이슈 빼고 모두 구현 완료! 👏
(TIL 쓸 때도 css 연습..ㅋ)

TIL 💡

Big Issue 1) 새로고침 이슈  

requirement :

1) 로그인 상태인 경우 홈, 상세, 프로필 화면에만 접근,

 로그아웃 상태에서는 로그인 화면에만 접근 할 수 있게 하기.

2) 새로고침시에도 로그인 상태가 유지되도록 로컬스토리지를 이용.

solution for 1) : 

로그인을 알 수 있는 state 값에 따라

(예를 들어 isLogin, 혹은 accessToken이 있는지 없는지, 처음에는 전자로 구현했다가 후자로 바꿈) 

라우터들을 분기 처리하고

다른 주소가 들어올 때는 

로그아웃 상태에서는 "/login"으로,

로그인 상태에서는 "/" (홈)으로 리다이렉팅 하기.

예시 페이지와 동일하게 너무 잘했다. 1) 번은..

 

그런데 말입니다..

여기서 몇시간 허공에 뿌린 새로고침 딜레마는 뭐냐면,

Issue:
1) redux 전역 state로 분기를 해주면 새로고침 하면 state값이 날아가면서 로그인 상태를 확인 할 수 없게 되어 로그인 화면으로 튕기게 된다.


2) 이를 해결 해 주기 위해 local storage를 반영하라고 했는데.. local storage에 값이 있는지 없는지로 router 분기를 하게 되면 로그인, 로그아웃 등 로그인 상태의 변경사항 발생 시 바로바로 화면에 반영이 안되기 때문에 오히려 새로고침을 해주어야 한다. 

Try 1:   
local storage에 써준 값을 다시 redux로 채워주는데

useEffect가 발동하기 전까지는 state가 비어있긴하다. 즉 페이지가 왔다갔다 해버림.

어느 타이밍에 채워줘야하느냐? ^.^ 

(위치도 애매해서 login 에서만 저걸 해주는 중, 로그인 후 화면들은 새로고침 시 문제 있겠네)

useEffect(() => {
    const localUser = localStorage.getItem("user");
    if (localUser)
      dispatch(setLogin(JSON.parse(localUser)));
  }, []);

 

Try 2:

비어있을 때에도 local storage 값을 유지 하도록 reudux initial 값에 local storage의 값을 써준다.

잘되는 줄 알았는데 로그인화면에서 local에 값 없을 때 에러뜨고 못고쳐서 망함.

해설 강의를 보자~~~

solution for 2) : 

결국엔 try2 가 방향성은 맞긴 한데, 강의를 보고 확신을 갖고 보니 해결 할 수 있었다.

나의 문제는 현재 accessToken이 있는지 없는지로 로그인 여부를 판단하는것에서 시작한다.

복잡해서 포기해버린 것이나 다름 없다.

 

1) 처음에는 단순하게 isLogin : true 처럼 boolean 값으로 했었는데, 

다른 동료분들과 얘기하다 보니 서버에서 받아온 값을 넣어주는 코드를 깔끔하게 두고 싶어서, 

user 객체를 한번에 가져와서 redux와 local storage 에 저장을 해준 것이다. 

여기local storage는 객체로 저장할 경우 넣고 빼는게 복잡해진다.

그래서 redux에서 써야하는 코드가 불편해졌다.

// 넣을 때
localStorage.setItem("user", JSON.stringify(userData));

// 뺄 때
const localUser = JSON.parse(localStorage.getItem("user"));

//그래서 redux의 initialState 에서는 이렇게 해줘야했고..
const initialState = { 
accessToken: localUser?.accessToken,
}

 

2) Router에서 accessToken이 local storage에 없을 때 뭐가 나오는지 몰랐다. undefined...

//라우터 컴포넌트 내부 (전체구조 생략하였음)
const accessToken = useSelector((state) => state.auth.accessToken);

//return문 안쪽
{
accessToken != undefined ? 
    <Route path="/" element={Home/}>
    :
    <Route path="login" element={<Login />
}

강의 그대로 깔끔하게 수정하고자 하면

1) 로그인 시 userdata 객체로부터 뜯어서 토큰을 넣어주고

localStorage.setItem(" accessToken ", userdata.accessToken)


2) 리덕스 initialState에서는 isLogin을 여전히 true, false로 받는다.

const initialState = { 
  isLogin: !!localStorage.getItem("accessToken")  // 느낌표두개로 boolean 타입으로 변경된다 핳...
}

 

3) Router에서 isLogin을 이용하여 삼항연산자를 깔끔하게 써준다.

isLogin ? <Route> : <Route>

 

- 끝- 

 

Big Issue 2) accessToken 만료 시 로그아웃 시키기

여기서 또 몇시간 공중에 흩뿌린 문제가 나오는데...

interceptor로 accessToken 확인하는 통신을 추가했는데,
이 안에서는 dispatch안되지, navigate 안되지,

모든 react hook을 사용 할 수 없다는 에러가 뜬다는 것이다~ 랄라.

임시 방편으로 localStorage는 접근이 가능해서 로컬 스토리지의 accessToken과 user 정보를 지우고, window.location.replace('/login'); 

을 사용했지만 이유를 몰라서 세상 찝찝하다.

(강의는 아직 다 못봄)

 

Small Issues

1) 귀신이 곡할 코드 👻

configStore.js 에 reducer 추가 안해주면 에러는 안나지만 계속 state값 못 받아와서 undefined, 
귀신이 곡할 코드가 완성된다. 괜히 동기 분 괴롭힘^^;


2) 똥코드 💩 🤎
reducer 에서 mutable한 함수를 써줘도 불변성을 유지하게 해준다는 immer js만 믿고  
state가 객체일 때 

state.content = action.payload; 이런게 가능하길래 
객체 자체를 통째로 변경하고자 

state = action.payload; return state; 라고 써줬다가 망한 썰 (이걸 해준다고 한게 아닌데..)
action.payload는 여기서 reducer를 벗어나면 없어지고
state는 action.payload를 가리키고 있어서 null이 나오는 것 같다. 나이스^.^ 

반응형