오늘 배운 것

자동으로 액세스토큰 갱신하는 것은 어제 잘 해결된 이슈인 줄 알았는데 아니었다. 왜냐하면 어제 당시 작업할 때 액세스 토큰이 만료되는 데 30분이나 걸려서, 개발서버에서 값을 1분으로 두고 과연 1분 뒤에 액세스토큰이 만료되어도 API 호출이 잘 되는지를 실험해 보았었다. 그런데 그 당시에 잘 되어서, 그러면 handleRequest() 함수의 에러 처리 로직이 잘 동작하는 게 아닐까 하고 넘겼는데, 알고보니 애초에 로컬 서버가 아니라 개발 서버(액세스토큰 lifetime을 30분으로 하고 배포했었음)에 요청을 보내고 있었던 것이다. 즉 멀쩡히 동작한 이유는 애초에 액세스토큰이 만료되지 않아서였던 것이다. 그래서 다시 BASE_URL 값을 로컬 URL로 바꾸고 시도하니 401 오류가 났다. 

 

원인은 401 에러가 발생했을 때 API 서버에서 내려주는 메시지가 에러 핸들링 로직의 메시지와 정확히 일치하지 않아서, 즉 if 조건문으로 토큰이 만료되어서 에러가 난 경우가 정확히 잡히고 있지 않기 때문이었다. 그래서 에러 메시지 자체를 콘솔 로그로 찍어서 확인해 보았고, 그 결과 토큰 만료로 인한 에러는 if문에서 잘 잡혔다. 즉 토큰 만료로 에러가 났을 때 작성해둔 if문을 타고 토큰 갱신 API를 한번 호출한 뒤, 다시 시도하는 로직이 실행되었다.

 

그런데 여기서도 에러가 났다. 서버의 로그를 보니 refreshToken API에서 400 에러가 나고 있었다. 클라이언트에서 해당 API에 요청을 잘못 보내고 있는 것이었다. 이 refreshToken API는 simplejwt 라이브러리에서 기본적으로 제공하는 view여서 왜 400 에러를 리턴하는지 파악하기 위해 뷰 내부의 소스코드를 살펴보았다. 

 

Serializer 코드를 보니 필드명이 refresh, access 였다. 나는 refreshToken, accessToken으로 보내주고 있었어서 이 부분을 수정하였더니 정상적으로 응답하였다. 

 

그런데 그러고 난 다음에도 토큰을 갱신한 다음에 다시 시도하는, 즉 토큰이 필요했던 기존 요청에서는 여전히 401 에러가 떴다. 비동기 저장소(AsyncStorage)에 새로 받아온 액세스 토큰의 값을 잘 저장했는데도 오류가 나는 것이다. 알고보니 에러 핸들링 로직에서는 이전에 했었던 요청을 그대로 반복하고 있었다. 즉 아무리 새로 액세스 토큰값을 받아왔어도, 그 값이 아닌 이전 액세스토큰 값으로 요청을 보내고 있어서 401 에러를 받았던 것이다. 

 

그러면 매번 액세스토큰의 최근 값을 참조하게 하면 되지 않나? 라고 생각할 수 있는데, 그러려면 방법이 있긴 하다. 바로 metadata() 함수를 수정해서, 매번 리액트에서 사용하는 비동기 저장소인 AsyncStorage에서 accessToken 값을 가져오는 것이다. 그런데 이 방법은 요청을 보낼 때마다 비동기 저장소를 조회해야 해서 비효율적이라는 단점이 있다. 

 

그래서 해당 metadata 함수에서 useContext API를 사용하려고 시도해 보았다. 그런데 이렇게 시도하니까 위와 같은 에러가 났다. 즉 useContext는 컴포넌트 안이나 리액트 훅 함수 내부에서만 사용할 수 있기 때문에 사용할 수 없다는 것이었다. 여기서 막힌 상황이다. 

 

팀원들과 같이 방법을 찾아보다가 일단은 AsyncStorage를 계속 참고하는 방법으로 제일 간단히 문제가 해결될 것 같아서 일단은 그 방법으로 해 보기로 했다. 코드를 깊게 파 보지는 못했는데, 라이브러리 단의 코드만 봤을 때는 window의 localStorage를 조회하는 식으로 코드가 작성된 것 같았다. 


이상한 점이 있다. 위의 방법을 사용하니 몇 분째 (이번엔 로컬 서버에 연결된 것도 확인했고, 액세스 토큰의 lifetime이 1분인 것도 확인했다) 에러 없이 여러 요청들이 앱 화면에도 잘 반영되고, API 서버에도 API 호출 로그가 잘 찍힌다. 그런데 분명 액세스 토큰이 만료되면 토큰 갱신 API(/auth/token/refresh)를 호출하도록 설정해 두었는데 로그가 없다. 무언가가 잘못된 것인지, 아니면 토큰의 액세스 lifetime이 1분으로 반영되지 않은 것인지는 확인해 보아야 할 것 같다. 하지만 잘 동작한다...!

 

 궁금한 점

1. window.localStorage() 로직을 실행할 때의 비용(로직의 복잡도나 시간)은 어느 정도일까? 그리고 내부 로직은 어떻게 또 구현되어있을지도 궁금하다. 

2. simplejwt 라이브러리에서 그냥 AccessToken과 SlidingToken의 차이는 무엇일까?

 

+ Recent posts