본문으로 건너뛰기

세션 관리 및 로그아웃 연동 가이드 (웹 프런트엔드)

본 문서는 Kakao OAuth 로그인 이후 SleepZ 웹 프런트엔드에서 세션 유지, 토큰 갱신, 로그아웃, 소셜 연결 해제를 처리하는 방법을 정의합니다. 2025년 게스트 계정 도입으로 identityLevel=GUEST 세션이 추가되었으며, Step-up 흐름과의 연계는 게스트 계정 연동 가이드를 참고하세요. 기본 도메인 규칙은 다음을 참조하세요.

  • /domains/common/core-domains/auth/endpoints.md
  • /domains/kr/core-domains/auth/endpoints.md
  • /domains/kr/core-domains/auth/business-rules.md

1. 용어

  • Access Token: 사용자의 인증 상태를 나타내는 JWT. HttpOnly 쿠키(sleepz_access_token)에 저장.
  • Refresh Token: Access Token 갱신 용도의 JWT. HttpOnly 쿠키(sleepz_refresh_token)에 저장, /auth/refresh 경로에 한정.
  • Session TTL: Access Token은 1시간, Refresh Token은 30일 유효(도메인 기본값). TimeMachine 시계를 기준으로 판단.

2. 관련 엔드포인트 요약

기능HTTP엔드포인트인증비고
토큰 갱신POST/auth/token/refreshRefresh JWT 쿠키Access/Refresh 재발급
토큰 검증POST/auth/token/validateAccess JWT클라이언트 세션 확인용
로그아웃POST/auth/logoutAccess JWTRefresh 토큰 폐기, 쿠키 삭제
Kakao 연결 해제POST/auth/oauth/kakao/unlinkAccess JWT + CSRF사용자가 요청한 경우만
게스트 세션 정보GET/users/meAccess JWTaccountType, identityLevel 제공

3. 토큰 갱신 흐름

3.1. 호출 조건

  • 라우트 보호용 미들웨어(Server Components 또는 Route Handler)에서 Access Token 만료 (401 AUTH_TOKEN_EXPIRED) 가 감지된 경우.
  • 백엔드 규칙상 Refresh 시도는 만료 5분 전 1회만 허용되므로, 프런트는 중복 호출을 피하기 위해 Promise 캐싱 또는 상태 플래그를 사용.

3.2. 요청/응답

  • 요청: 바디 없음. Refresh 쿠키 자동 첨부.
  • 성공 응답 (200)
{
"accessToken": "new-access-jwt",
"refreshToken": "new-refresh-jwt",
"expiresIn": 3600
}
  • Set-Cookie
    • sleepz_access_token=...; HttpOnly; Secure; SameSite=Strict
    • sleepz_refresh_token=...; HttpOnly; Secure; SameSite=Strict
  • 오류
    • 401 AUTH_REFRESH_TOKEN_EXPIRED: Refresh 만료 → /login 리다이렉트
    • 400 AUTH_REFRESH_SESSION_LOCKED: 동시 세션 제어 → 재로그인 유도

3.3. 구현 노트

  • Next.js 15 App Router에서 서버 액션 또는 Route Handler(app/api/auth/refresh/route.ts)로 래핑하여 호출.
  • 로컬 컴포넌트에서는 fetch("/auth/token/refresh", { method: "POST", credentials: "include" }) 형태 사용.
  • Refresh 중 기존 요청은 큐잉 또는 재시도 전략을 적용.

4. 세션 검증

  • 초기 렌더링 시 POST /auth/token/validate 호출로 Access Token 유효성 체크 가능하며, 응답의 identityLevel 필드로 게스트 여부를 판별한다.
  • 응답 예시:
{
"subject": "user-uuid",
"identityLevel": "GUEST",
"expiresAt": "2025-01-20T11:30:00.000Z",
"roles": ["ROLE_OPERATOR"]
}
  • 401 AUTH_TOKEN_INVALID → Refresh 시도 후 실패 시 /login.
  • Server Component에서는 cookies().get("sleepz_access_token") 여부를 우선 확인하되, 직접 JWT 파싱 금지(도메인 규칙 위배).

5. 로그아웃 처리

5.1. 요청

POST /auth/logout
Authorization: Bearer <access>
X-CSRF-Token: <csrf>
  • 바디는 선택적으로 {"reason":"USER_REQUEST"} 필드 허용 (분석 용도).
  • 성공 시 204 No Content, 쿠키 삭제 헤더(Set-Cookie: ...; Max-Age=0) 응답.
  • 가장 빠른 UX를 위해 로그아웃 요청은 POST fetch 후, /login으로 클라이언트 이동.

5.2. 프런트 UX 요구사항

  • 사용자에게 "로그아웃하시겠습니까?" 확인 모달 제공.
  • 성공 메시지: "정상적으로 로그아웃되었습니다."
  • 실패 시 (예: 네트워크 오류) “세션 종료에 실패했습니다. 다시 시도해주세요.” 토스트 노출 후 상태 유지.

6. 단일 세션 정책

  • SleepZ 웹은 동시에 하나의 활성 세션만 허용한다. 사용자가 새로운 브라우저/디바이스에서 로그인하면 기존 Refresh 토큰과 세션은 즉시 폐기된다.
  • 기존 세션에서 API 호출 시 401 AUTH_REFRESH_TOKEN_EXPIRED 또는 400 AUTH_REFRESH_SESSION_LOCKED가 반환될 수 있으므로, 프런트는 "다른 기기에서 로그인되어 세션이 종료되었습니다."와 같은 메시지를 노출하고 로그인 페이지로 이동시켜야 한다.
  • /v1/auth/sessions 등 다중 세션 조회/종료 API는 웹 프런트엔드에서 호출하지 않는다. 필요한 경우 운영 도구에서만 사용한다.

7. 게스트 세션 추가 고려 사항

  • 오류 코드 매핑
    • 409 AUTH_GUEST_LINK_CONFLICT: Step-up 동시 요청. 사용자에게 잠시 후 재시도 또는 새 창 이용 안내.
    • 409 AUTH_GUEST_ACCESS_DENIED: 제한 기능 접근. 잠금 UI와 Step-up 유도 메시지 노출.
  • GET /users/me 응답의 groupIdspatients.guest.* 패턴이 포함된 경우에만 게스트 UX 배너를 렌더링하고, Step-up을 유도한다. (v1.2.0부터 IAM Group 기반 식별 권장)
  • 게스트 세션 로그아웃 완료 메시지는 “게스트 체험이 종료되었습니다.”로 분기하고, 재온보딩 또는 정식 회원가입 CTA를 함께 제공한다.
  • 다중 탭에서 Step-up 완료 이벤트를 동기화하기 위해 storage 이벤트 또는 Broadcast Channel을 사용한다.

8. Kakao 연결 해제 (선택 기능)

  • 엔드포인트: POST /auth/oauth/kakao/unlink
  • 요청 바디:
{
"reason": "USER_REQUEST"
}
  • 성공 응답:
{
"status": "unlinked",
"unlinkedAt": "2025-01-20T10:40:00.000Z"
}
  • 프런트 처리:
    • 성공 후 즉시 /login 이동 또는 "계정 연결이 해제되었습니다." 안내.
    • 만약 이후 동일 계정으로 재연결 필요 시 로그인 플로우를 다시 수행.
  • 오류 코드:
    • 403 AUTH_CSRF_TOKEN_MISSING: CSRF 헤더 누락 → 재시도
    • 409 AUTH_KAKAO_NOT_LINKED: 연결된 Kakao 계정 없음 → UI에서 버튼 비활성화 고려

9. CSRF 및 보안 헤더

  • X-CSRF-Token은 Next.js 서버에서 생성한 값을 쿠키(sleepz_csrf_token)와 헤더로 함께 전송해 검증.
  • 로그아웃과 unlink 요청은 반드시 CSRF 헤더를 포함해야 하며, fetch 요청 시 credentials: "include" 설정.
  • 모든 시간 필드는 TimeMachine 기준으로 반환되므로, UI 표시 시 사용자 시간대 변환이 필요할 수 있음. Step-up 관련 안내에서도 동일한 규칙을 적용한다.

10. 테스트 체크리스트

  1. Access Token 만료 시 POST /auth/token/refresh 호출로 세션 연장되는지 확인.
  2. Refresh 만료(401 AUTH_REFRESH_TOKEN_EXPIRED) 후 로그인 페이지로 리다이렉트 되는지 검증.
  3. 로그아웃 성공 시 쿠키 삭제 및 보호된 페이지 접근 차단 여부 확인.
  4. CSRF 토큰 누락 상태에서 요청 시 403 응답이 발생하는지 테스트.
  5. Kakao unlink 후 재로그인 시 신규 연결 흐름이 정상 작동하는지 확인.
  6. 게스트 세션에서 제한 기능 접근 시 AUTH_GUEST_ACCESS_DENIED 응답을 사용자 메시지로 매핑하는지 확인.
  7. 게스트 Step-up 완료 후 기존 탭이 자동 로그아웃되지 않고 정규 세션으로 갱신되는지 검증.

11. 향후 TODO

  • 단일 세션 종료 시 사용자 안내 문구/토스트 표준화
  • Refresh 자동화 훅(React Query/ SWR) 공통 유틸 제공
  • 로그아웃 후 남은 비공개 캐시(React Query 등) 초기화 패턴 문서화
  • 게스트 온보딩·Step-up 오류 코드 시나리오를 포함한 Playwright 회귀 테스트 통합