[react] 이중 호출 문제(rtr 적용 중)
문제 발생
RTR 적용 중 발생한 문제입니다. 하지만 RTR 에 국한된 문제가 아니라서 따로 포스팅하려고 합니다.
RTR 을 local 에서 확인하던 중 다음과 같은 문제가 발생했습니다.
한번씩 Redis 에 저장된 refreshToken 값이 갑자기 바뀜
- 바뀌는 이유를 모르겠음. debug 를 찍어봐도 refreshToken 값이 특정 메서드에서가 아니라 특정 구간 사이에서 랜덤하게 갑자기 변경됨
- 두 번 실행하면 한번은 성공함. 하지만 두번 다 실패하는 경우도 있음
이 문제 때문에 2시간 정도 고생했습니다.
문제 원인
처음에는 문제의 원인이 당연히 서버에 있는줄 알았는데요. 그게 아니라 프론트쪽이었습니다. (그래서 TIL-react 입니다… ㅎ)
다음 코드가 문제였습니다.
...
const [nickname, setNickname] = useState('');
let isLogin = authCtx.isLoggedIn;
let isGet = authCtx.isGetSuccess;
const callback = (str) => {
setNickname(str);
}
useEffect(() => {
if (isLogin) {
authCtx.getUser();
}
}, [isLogin]);
useEffect(() => {
if (isGet) {
callback(authCtx.userObj.nickname);
}
}, [isGet]);
여기서 authCtx.getUser()
는 "/member/me"
로 user 정보를 요청해서 받는 함수입니다. 위 코드가 MainNavigation 에서 선언되어 있어서 모든 페이지에서 login 시 호출되게 되어있습니다.
이때 authCtx.isLoggedIn
는 localStorage 의 accessToken 여부로 판단하게 되는데, 있으면 true, 없으면 false 입니다.
중요한 건 AccessToken 을 호출할 때 발생하는데요. AccessToken 이 만료될 때마다 새로 LocalStorage 에 저장하는 과정에서 isLogginedIn
이 true -> false -> true 로 변경됩니다.
이렇게 변경되면 isLogin
이 변경되면서 authCtx.getUser()
를 다시 호출하게 됩니다.
문제점
"/player/list"
호출하면서"member/me"
함께 호출- 두 요청이 비동기로 처리되면서 server 로 동시에 호출됨
- server 에서는 다음과 같이 동작됨
/player/list
호출에 대해/refreshToken
으로 token 갱신 요구/refreshToken
갱신하면서 Redis 에 있는 refreshToken 을 갱신함- 해당 refreshToken 이 도착하기 전에
member/me
호출에 대한 token 갱신도 시작됨 member/me
에 대한 갱신도 cookie 안의 refreshToken 으로 이루어지는데, 해당 refreshToken 은 이미/player/list
에 대한 갱신으로 인해 변경된 상태임- 따라서 refreshToken 이 맞지 않으므로 401 에러를 반환
- 클라이언트는 401 에러를 받고 재로그인 요구
글로 설명하기가 어렵네요. 결론적으로 server 로 호출을 2번하면서 refreshToken 갱신도 2번 요구하게 되고, 첫번째 갱신과 두번째 갱신이 동시에 이루어지다보니 Redis 에 저장된 값과 맞지 않게 된 겁니다.
해결
해결 방법은 useEffect 에서 isLogin 값이 아닌 다른 값을 사용하거나, cookie 에 userObj 를 저장하는 방법 등이 있겠지만 결론적으로 navigation 에서 member/me
호출을 뺐습니다.
그 이유는 어쨋든 실행 페이지의 호출과 navigation 의 호출이 겹칠 위험이 있고, 한 동작에서 호출이 2번 일어나면 안되기 때문입니다.(사실 제 생각입니다 ㅎㅎ)
다음에 react, typescript 를 좀 더 배우면 ui/ux 를 제대로 변경해보겠습니다.
댓글남기기