이번 이슈를 해결하면서, 자동 로그인은 분명 사용자 경험을 높여주는 편리한 기능이지만 서버 인증 상태와 어긋나는 상황이 생겼을 때 이를 막아줄 안전 장치가 꼭 필요하다는 걸 느꼈습니다. 또 단일 Refresh Token 정책을 쓰는 시스템에서는 여러 디바이스에서 동시에 로그인하면 구조적으로 충돌이 생길 수밖에 없다는 점, 이런 부분에 대한 명확한 UX 전략과 예외 처리 흐름이 함께 설계되어야 한다는 걸 배웠습니다.
결국 프론트엔드가 인증 여부를 임의로 판단해서는 안 되고 서버 상태를 신뢰할 수 있는 단일 기준으로 삼아야 한다는 점을 다시 한 번 확인했습니다. 예외 상황을 고려하지 않은 채 겉보기만 잘 돌아가는 자동화는 오히려 큰 문제를 만들 수 있다는 것도 알게 되었습니다.
문제 현상
최근 프로젝트에서 Access Token 재발급 요청이 무한 루프에 빠지는 현상을 경험했습니다.
상황은 다음과 같았습니다.
- 사용자가 PC에서 로그인한 상태에서, 동일 계정으로 모바일에서 다시 로그인
- 새로운 Refresh Token(RT)이 발급되면서 기존 PC 세션의 RT는 만료 처리되고, 모바일용 RT가 새롭게 발급됨. 이 때 Redis에는 최신 RT만 저장되므로 기존 RT는 만료 처리
- 이후 PC에서 Access Token(AT)이 만료되어 RT를 통해 토큰 재발급 요청 시, 서버는 해당 RT가 Redis에 존재하지 않아 400 REFRESH_TOKEN_INVALID 에러를 반환합니다.
이때 프론트는 에러를 감지하고 /login으로 리다이렉트했지만, localStorage에는 여전히 로그인을 유지하기 위한 accessToken과 hasLoggedIn=true 값이 남아 있었습니다.
결과적으로 자동 로그인 로직이 다시 실행되며 /home으로 이동했고, /home → /login → /home으로 이어지는 무한 리다이렉트가 발생했습니다.

원인 분석하기
1. Refresh Token 단일 정책
- 하나의 계정으로 로그인할 때마다 기존 RT는 만료되며 새로운 RT가 발급됨
- Redis에는 최신 RT만 저장 → 이전 RT는 무효 처리되어 더 이상 AT 발급이 불가능함
- 다중 디바이스 환경에서 기존 세션은 토큰 갱신 불가
2. 자동 로그인 조건의 불완전함
- accessToken 존재 여부 + hasLoggedIn === 'true' 만으로 자동 로그인 여부 판단중
- 그러나 AT는 이미 만료, RT는 무효 상태 → 무한 루프 발생
3. httpOnly 쿠키 한계
- 브라우저에 남아있는 RT는 httpOnly 쿠키에 저장되므로 클라이언트 JS에서는 직접 삭제 불가
- 서버는 무효 RT로 판단하지만, 클라이언트는 이를 감지하지 못함
해결 방안
해결 방법은 비교적 단순했습니다.
REFRESH_TOKEN_INVALID 응답이 오면 /login으로 단순 리다이렉트하지 않고, 자동 로그인 조건 자체를 무효화하기 위해 `localStorage.setItem('hasLoggedIn', 'false')`으로 코드를 수정했습니다.
// 예외 응답 처리 로직
if (response.code === 'REFRESH_TOKEN_INVALID') {
localStorage.setItem('hasLoggedIn', 'false'); // 자동 로그인 해제
router.replace('/login');
}
이제 로그인 페이지에서는 hasLoggedIn 값이 false이므로, 자동 로그인 로직이 비활성화됩니다.
결과적으로 무한 리다이렉트 없이 정상적으로 로그인 화면을 사용자에게 표시할 수 있게 되었습니다.
이번 프로젝트에서는 Next.js SSR 환경에서 Refresh Token 만료로 인한 무한 리다이렉트 문제를 직접 경험했습니다. 모바일과 PC에서 동시에 로그인할 때 기존 세션이 삭제되고 새로운 세션이 생성되면서, 프론트에서 무한 새로고침이 발생하는 상황을 디버깅하면서 토큰 재발급과 세션 관리 로직의 복잡성을 몸소 체감했습니다. 실사용자가 존재하는 배포중인 프로젝트인데, 이슈가 발생했다는 얘기를 전달받았지만 구글링을 해도 같은 이슈를 겪는 글이 많이 없었고, 따라서 원인 파악에서부터 오래 걸려 밤을 새서 진행해 기억에 남는 이슈였습니다.
이 과정에서 Next.js의 SSR 라우팅 분기와 클라이언트 상태 관리를 면밀히 점검하게 되었고, 백엔드와의 인증 플로우가 서비스 안정성에 얼마나 큰 영향을 미치는지도 깨달았습니다. 특히 다양한 환경에서의 동시 접속 시나리오를 고려하지 않으면 예상치 못한 오류가 발생할 수 있다는 점이 인상깊었습니다.
앞으로는 토큰 만료 처리, 세션 충돌 방지, 인증 상태 관리를 더 명확하게 설계하고, 다양한 환경에서 테스트하여 문제를 사전에 예방하는 방향으로 개선할 계획입니다. 이번 경험을 통해 SSR 기반 인증 로직 구현 시 주의할 점과 디버깅 포인트를 직접 체험한 점이 좋았습니다.
'👩🏻💻 Develop > TroubleShooting' 카테고리의 다른 글
| [Next.js] ENUM 키워드 매핑 실패로 인해 발생한 한글 변환 오류 해결하기 (0) | 2025.11.25 |
|---|---|
| [Next.js] 중복 ENUM 값 충돌로 인해 발생한 키워드 매핑 오류 해결하기 (2) | 2025.11.24 |
| [Next.js] useSearchParams 사용 시 Next.js 15 빌드 실패 이슈 (0) | 2025.09.06 |
| [Next.js] next-pwa와 Turbopack 호환성 문제 해결하기 (0) | 2025.09.05 |
| [Next.js] SSE 브라우저 연결 오류 해결하기 (Nginx) (1) | 2025.09.02 |