1. 개요
안녕하세요! DoEatFit (v2.0) 웹 플랫폼의 심장부이자, 침입자의 위협으로부터 회원들의 소중한 정보를 철벽 사수하는 다중 계층 인증 및 JWT 생명주기 설계서입니다.
모바일 PWA 환경과 웹 생태계 전반을 아우르는 헬스케어 서비스를 제공하다 보면, 해커의 교묘한 토큰 탈취 시나리오(XSS, CSRF, Replay Attack 등)에 직면하게 마련이에요. DoEatFit v2.0은 이러한 보안 위협을 철저히 차단하기 위해 BFF(Backend for Frontend) 패턴과 Refresh Token Rotation (RTR) 기법을 유기적으로 융합한 최첨단 인증 구조를 확립했답니다. 클라이언트와 서버, 인메모리 캐시를 종횡무진 넘나드는 토큰 라이프사이클과, 개발 과정에서 마주했던 까다로운 리다이렉트 루프 트러블슈팅의 흥미진진한 해결 여정을 정갈하게 전해드릴게요.
2. 핵심 내용
2-1. 🔑 BFF 패턴과 다중 저장소(LocalStorage & Cookies) 인증 구조
DoEatFit v2.0은 Next.js의 서버 사이드 렌더링(SSR) 이점과 브라우저의 보안 한계를 모두 고려하여, BFF(Backend for Frontend) 사상에 기반한 이중 저장소 관리 전략을 취하고 있습니다. 서버 레이어와 클라이언트 레이어의 역할을 똑똑하게 양분하여 보안 시너지를 낸 구조를 살펴볼까요?
- 다중 저장소 역할 분담:
- Cookies (서버 사이드 영역): Next.js 미들웨어(Middleware)나 Server Actions가 사용자의 로그인 세션을 즉각 판별할 수 있도록 저장해요. 특히 해커가 악성 스크립트를 주입해 토큰을 갈취하는 XSS 공격을 물리적 수준으로 원천 봉쇄하기 위해,
RefreshToken은 브라우저 JS API가 전혀 접근할 수 없는 HttpOnly, Secure, SameSite=Lax/Strict 속성을 부여하여 절대적인 보안 방어막 안에 안착시켰답니다. - LocalStorage (클라이언트 사이드 영역): 브라우저가 화면을 렌더링할 때 “사용자가 현재 로그인 상태인지”, “어떤 프로필 정보를 지니고 있는지” 즉각 판단할 수 있도록
AccessToken과 유저 메타 정보(UserInfo)를 보관하여 쾌적한 UX를 보장합니다.
- Cookies (서버 사이드 영역): Next.js 미들웨어(Middleware)나 Server Actions가 사용자의 로그인 세션을 즉각 판별할 수 있도록 저장해요. 특히 해커가 악성 스크립트를 주입해 토큰을 갈취하는 XSS 공격을 물리적 수준으로 원천 봉쇄하기 위해,
- 2중 보안 가드 레이어:
- 클라이언트 가드 (
withAuthHOC): 비로그인 사용자가 개인 일지 등 보호된 영역으로 접근하면 즉시/login으로 튕겨냅니다. - 서버 미들웨어 가드 (
middleware.ts): 브라우저의 JS 리다이렉션을 우회하려는 악의적인 조작을 막기 위해, 서버 초입의 미들웨어 단계에서 쿠키에 심긴 토큰들의 실질 유효 시간을 크로스 체크하여 권한 없는 진입을 철저히 불허합니다.
- 클라이언트 가드 (
아래 시퀀스 다이어그램은 토큰이 만료되어 비인증 상태(401)가 감지되었을 때, BFF 패턴 하에서 작동하는 무중단 토큰 갱신(Silent Refresh)의 긴밀한 교신 흐름을 아주 선명하게 보여줍니다.
sequenceDiagram participant User as 사용자/브라우저 participant Front as 프론트엔드 (Client) participant NextAction as Next.js Server Action (BFF) participant Back as 백엔드 (API Server) Note over User, Back: [토큰 만료 및 자동 갱신 흐름] User->>Front: API 요청 (Expired AccessToken) Front->>Front: interceptor: Authorization 헤더 생략 Front->>Back: 요청 (Token 없음) Back-->>Front: 401 Unauthorized Front->>NextAction: reissueTokenAction() 호출 NextAction->>Back: POST /api/auth/reissue-token (with RefreshToken Cookie) Back-->>NextAction: New Tokens (Access/Refresh) NextAction->>NextAction: Set-Cookie (New Tokens) NextAction-->>Front: Success (New AccessToken) Front->>Front: LocalStorage 갱신 (AccessToken, ExpiresAt) Front->>Back: 원래 요청 재시도 (with New AccessToken) Back-->>User: 최종 데이터 응답
2-2. 🚨 JWT 생명주기와 Redis 기반 RTR(Refresh Token Rotation) 보안 전략
토큰이 탈취되는 비상 상황이 발생하더라도 해커가 장기간 토큰을 오용하지 못하도록 방지하는 것이 매우 중요합니다. DoEatFit v2.0은 유기적인 생명주기와 RTR(Refresh Token Rotation) 보안 체계를 작동시켜 이를 사수해요.
- 엄격한 토큰 생명주기:
- Access Token(AT): 오직 30분 동안만 살아 숨 쉬는 단기 토큰이에요. 매 요청 시 HTTP
Authorization: Bearer <token>헤더로 넘어가죠. - Refresh Token(RT): 24시간의 수명을 가집니다. UUID 형식으로 발급되어 보안성을 높였고 Redis 인메모리 저장소에 철저하게 관리된답니다.
- Access Token(AT): 오직 30분 동안만 살아 숨 쉬는 단기 토큰이에요. 매 요청 시 HTTP
- Refresh Token Rotation (RTR)과 공격 즉시 무효화:
- 사용자가 토큰 갱신을 요청할 때마다 기존 RT는 **즉시 폐기(Blacklist 등록)**되고, 완전히 신규로 생성된 AT/RT 쌍이 재교부됩니다.
- 만약 해커가 이미 사용되어 폐기된 과거의 Refresh Token을 들고 기습 재발급을 시도하는 순간, 보안 엔진은 이를 “토큰 재사용 침투 공격(Replay Attack)“으로 간주해요. 즉시 해당 회원 명의로 활성화된 Redis의 모든 활성 세션 토큰을 3ms 만에 불태워 버려(전역 로그아웃), 해커뿐만 아니라 피해자 브라우저까지 전면 즉시 차단함으로써 보안 피해를 원천 차단해 냅니다.
- Grace Period (수신 지연 유예): 모바일 PWA 환경의 불완전한 네트워크 렉으로 인해 정상적인 갱신 요청이 미세한 시간차로 중복 도달했을 때 억울하게 강제 로그아웃되는 대참사를 예방하고자, 60초가량의 폐기 유예 버퍼 시간을 두어 정상적인 수렴 동작이 이루어지도록 디테일한 사용성을 설계했습니다.
2-3. 🛠️ 무한 리다이렉트 루프 해결 및 추천 보안 라이브러리 검토
- 무한 리다이렉트 루프(Redirect Loop) 장애 원인과 해결책:
- 원인: 로그인 세션이 장시간 지나 만료되었음에도 불구하고, 브라우저 내부에는 과거 발급된 쿠키(
AccessToken,ExpiresAt) 잔재가 남아 있었어요. 서버 미들웨어는 쿠키의 ‘존재’만 보고 “로그인한 유저네!”라며 안심했지만, 실제 로그인 페이지(/login)에 접근할 때는 “이미 로그인했으니 로그인 페이지에 올 필요 없어!”라며 다시 메인 홈(/)으로 리다이렉트 시켰죠. 메인 홈에서는 또 만료된 토큰이라 API 통신이 실패해 사용자를/login으로 보내는 악순환이 발생했고, 결국 브라우저는 무한 리다이렉트를 반복하다 503 에러나 메모리 과부하로 크래시를 냈습니다. - 해결책: 인증의 판별 기준을 쿠키의 단순 ‘존재’에서 **‘시간적 실질 유효성’**으로 지능화했습니다. 미들웨어가
AccessToken뿐만 아니라ExpiresAt쿠키를 연동 대조하게 했으며,Number.isNaN()방어 필터를 적용하여 악의적인 날짜 조작이나NaN데이터 유입 시 이를 즉각 만료 상태로 판정해 사용자가 튕김 없이 매끄럽게/login페이지에 도달하도록 완벽하게 매듭지었답니다.
- 원인: 로그인 세션이 장시간 지나 만료되었음에도 불구하고, 브라우저 내부에는 과거 발급된 쿠키(
- 추천 오픈소스 라이브러리 검토:
- jose
- 평가: 백엔드 서버에 다녀오는 오버헤드 없이 Next.js 미들웨어단에서 자체적으로 JWT 서명을 1ms 이내로 즉각 복호화하고 암호화 위변조를 판독하고자 할 때, 아주 강력하고 경량화된 웹 전용 암호화 라이브러리로 적극 도입을 추천해요.
- Auth.js (Next-Auth)
- 평가: 향후 Kakao, Google 등 복잡한 소셜 간편 로그인 스키마와 다중 OAuth2 인가 프로토콜로 서비스 확장이 본격적으로 이루어질 때, 번잡한 소셜 연동 규격을 간소화하고 표준화된 세션 프로바이더를 수립해 주는 최고의 상태 유지 라이브러리로 도입 검토를 적극 제안합니다.
- jose