문제는 로그인이 계속 풀려보이는 현상이 발생했다는 것이다.
원래 로그인 상태일때는 오른쪽, 로그인을 하지 않은 유저는 왼쪽 화면이 보여야 한다. 그런데 access token이 만료되고나서 어쩔 때는 로그인이 풀려있고, 어쩔 때는 로그인이 풀려있지 않은 것처럼 보였다. (항상 로그인이 풀려있는 것도 아니였다)
특정 상황에 항상 로그인이 풀려있는 것이 아니다보니, 원인을 찾아내는 것 이 굉장히 어려웠다.
개발이 되어있던 로직을 짚고 넘어가자면..
axios interceptors
를 이용해서 access token이 만료된 경우 refresh token을 사용하여 새로운 access token을 발급받고, 기존의 요청을 다시 실행시키는 방식으로 로그인 상태유지를 해왔다.
또한, 로그인 상태관련 Context를 만들어 인증된 유저인지를 확인하는 로직을 구현했다. 토큰이 있으면서 프로필을 가져왔다면(프로필 요청) isAuthenticated
가 true가 된다.
문제를 해결하기 위해서는 문제에 대한 원인을 알아내야 했고, 2가지 단서로 원인을 찾아내기 시작했다.
단서 1. '로그인 후 이용해주세요' 화면을 클릭해 로그인 화면으로 넘어갔다가 다시 뒤로 돌아가면 갑자기 데이터가 다 뜬다.
단서 2. 아래 이미지를 보면 챌린지 리스트 데이터는 뜨지 않아 _'로그인 후 이용해주세요'_가 보이지만, 날짜 위에 색이 있는 부분과 상태바를 보면 색상이 들어가 있는 것을 보아 그 두 가지 요청은 제대로 가고 있다.
이 두 가지 단서를 가지고 원인을 생각해보았고, 단서 1
에 따르면 실제로 로그인은 풀려있지만 로그인 후 이용해주세요
가 뜨는 것이므로 refresh token
을 발급받는 로직에 오류가 있을 수 있다고 판단하였다.
그래서 일단 토큰을 임의로 유효하지 않은 토큰으로 바꿔치기 하는 버튼 하나를 만들어서 interceptors로 refresh token을 사용하여 새로운 access token을 발급받을 수 있도록 확인해보았다.
이렇게 두 번 말고도 여러 번 버튼을 눌러 확인을 해본 결과 문제를 2가지 발견하게 되었다.
401 → 리이슈 요청 → 다시 401이 뜬 요청을 보냄
이 과정을 해야 하는데 이전 요청을 상황에 따라 다 보낼 때도 있고, 한두 개만 보낼 때도 있고 시도할 때마다 달라졌다.여기서 로그인이 풀려보이는 정확한 원인을 파악할 수 있었다.
2번에 쓴 것처럼 리이슈 이후 다시 요청을 보내야 하는데 만약 프로필을 가져오는 요청을 다시 하지 않았다면, auth context에서 인증 상태를 프로필을 가져와야 true로 설정하였기 때문에, 토큰은 있음에도 불구하고 isAuthenticated
가 false가 되어 _'로그인 후 이용해주세요'_가 보이게 된다.
첫 화면에서 나의 경우 5개 정도의 요청을 보내는데 여러 요청을 동시에 보내는 상황에서 access token이 만료된 경우 interceptors 실행한다. 이때, 5개의 요청에서 모두 저 과정이 발생하면서 5번 리이슈 요청을 보내고 실패한 요청을 다시 전송하는 과정에서 꼬이게된다.
예를 들면
- 동시에 만료된 토큰으로 요청 A와 B를 보냄
이런식으로 반복되면 무한 루프를 돌 수 있으며, 5개의 요청을 보냈다고 해서 실패했던 5개의 요청이 다시 200으로 성공할 것이라는 보장이 사라진다.
참고 블로그 이 블로그에서 해결 방법을 찾을 수 있었다.
리이슈 요청이 여러번 가는 것을 방지하기 위해 isRefreshing 플래그를 두어 이미 interceptors로 인한 리이슈 요청을 하고있을 경우 요청이 가지 않도록 막고, 모든 실패 요청을 큐에 저장해놓고 토큰이 재발급 된 이후 그 큐에 담긴 요청들을 모두 다시 요청을 보낸다.
대략적으로 위와 같은 코드를 사용함으로써 이미지처럼 리이슈 요청은 1번만 가고 실패햇던 요청들이 모두 다 다시 요청이 가서 성공하는 모습을 확인할 수 있었다.
큐 기반 재발급–재시도 로직을 적용하게되면서 리프레시 요청 1회 → 큐에 쌓인 모든 요청 일괄 재시도라는 명확한 흐름으로 로그인이 풀려보이는 이슈를 해결 할 수 있었다.
원인을 찾는 것이 조금 힘들었지만, 큐 기반 재발급–재시도 로직이라는 새로운 방법을 알 수 있어서 좋은 경험이 된 것 같고, 앞으로도 이와 같은 큐 기반 패턴을 활용해 API 통신의 안정성을 유지할 수 있을 것 같다. 🐛