@startuml user-로그아웃 !theme mono title User Service - 로그아웃 내부 시퀀스 (UFR-USER-040) actor Client participant "UserController" as Controller <> participant "AuthenticationService" as AuthService <> participant "JwtTokenProvider" as JwtProvider <> participant "Redis\nCache" as Redis <> note over Controller, Redis **UFR-USER-040: 로그아웃** - JWT 토큰 검증 - Redis 세션 삭제 - 클라이언트 측 토큰 삭제 (프론트엔드 처리) end note Client -> Controller: POST /api/users/logout\nAuthorization: Bearer {JWT} activate Controller Controller -> Controller: @AuthenticationPrincipal\n(JWT에서 userId 추출) Controller -> Controller: JWT 토큰 추출\n(Authorization 헤더에서) Controller -> AuthService: logout(token, userId) activate AuthService == 1단계: JWT 토큰 검증 == AuthService -> JwtProvider: validateToken(token) activate JwtProvider JwtProvider -> JwtProvider: JWT 서명 검증\n(만료 시간 확인) JwtProvider --> AuthService: boolean (유효 여부) deactivate JwtProvider alt JWT 토큰 무효 AuthService --> Controller: throw InvalidTokenException\n("유효하지 않은 토큰입니다") Controller --> Client: 401 Unauthorized\n{"code": "AUTH_002",\n"error": "유효하지 않은 토큰입니다"} deactivate AuthService deactivate Controller else JWT 토큰 유효 == 2단계: Redis 세션 삭제 == AuthService -> Redis: 세션 삭제\n(캐시키: user:session:{token}) activate Redis Redis --> AuthService: 삭제된 키 개수 (0 또는 1) deactivate Redis alt 세션 없음 (이미 로그아웃됨) note right of AuthService **멱등성 보장** - 세션이 없어도 로그아웃 성공으로 처리 - 중복 로그아웃 요청에 안전 end note else 세션 있음 (정상 로그아웃) note right of AuthService **세션 삭제 완료** - Redis에서 세션 정보 제거 - JWT 토큰 무효화 (Blacklist 방식) end note end == 3단계: JWT 토큰 Blacklist 추가 (선택적) == note right of AuthService **JWT Blacklist 전략** - 만료되지 않은 JWT 토큰을 강제로 무효화 - Redis에 토큰을 Blacklist에 추가 (TTL: 남은 만료 시간) - API Gateway에서 Blacklist 확인 **API Gateway 연계 시나리오** 1. 로그아웃: AuthService가 Blacklist에 토큰 추가 2. 후속 API 요청: API Gateway가 Blacklist 확인 - Redis GET jwt:blacklist:{token} - 존재하면: 401 Unauthorized 즉시 반환 - 존재하지 않으면: 백엔드 서비스로 라우팅 3. 만료 시간 도달: Redis TTL 만료로 자동 삭제 end note AuthService -> JwtProvider: getRemainingExpiration(token) activate JwtProvider JwtProvider -> JwtProvider: JWT Claims에서\nexp(만료 시간) 추출\n(현재 시간과 비교) JwtProvider --> AuthService: remainingSeconds deactivate JwtProvider alt 남은 만료 시간 > 0 AuthService -> Redis: 블랙리스트에 토큰 추가\n(캐시키: jwt:blacklist:{token},\n값: "revoked", TTL: 남은초) activate Redis Redis --> AuthService: Blacklist 추가 완료 deactivate Redis end == 4단계: 로그아웃 이벤트 발행 (선택적) == note right of AuthService **로그아웃 로깅 및 이벤트** - 감사 로그 기록: userId, timestamp, IP - 이벤트 발행: LOGOUT_SUCCESS - 분석 데이터 수집: 세션 지속 시간, 활동 통계 end note AuthService -> AuthService: 로그아웃 성공 로그 기록\n(userId, timestamp, sessionDuration) AuthService ->> AuthService: publishEvent(LOGOUT_SUCCESS) note right of AuthService **이벤트 활용** - 비동기 이벤트 처리 - 분석 시스템 연동 - 감사 로그 저장소 전송 end note == 5단계: 응답 반환 == AuthService --> Controller: LogoutResponse\n(success: true) deactivate AuthService Controller --> Client: 200 OK\n{"success": true,\n"message": "안전하게 로그아웃되었습니다"} deactivate Controller end note over Controller, Redis **보안 처리** - JWT 토큰 Blacklist: 만료 전 토큰 강제 무효화 - 멱등성 보장: 중복 로그아웃 요청에 안전 - 세션 완전 삭제: Redis에서 세션 정보 제거 - 감사 로그: userId, timestamp, IP, sessionDuration 기록 **API Gateway 연계** - Blacklist 확인: GET jwt:blacklist:{token} - 존재 시: 401 Unauthorized 즉시 반환 - TTL 자동 관리: 만료 시간 도달 시 자동 삭제 **클라이언트 측 처리** - 프론트엔드: LocalStorage 또는 Cookie에서 JWT 토큰 삭제 - 로그인 화면으로 리다이렉트 - 모든 인증 헤더 제거 **성능 목표** - Redis 삭제 연산: O(1) 시간 복잡도 - 평균 응답 시간: 0.1초 이내 - P95 응답 시간: 0.2초 이내 **이벤트 처리** - LOGOUT_SUCCESS 이벤트 발행 (비동기) - 감사 로그 저장소 전송 - 분석 데이터 수집 **에러 코드** - AUTH_002: JWT 토큰 무효 end note @enduml