# 세션 연장 Confirm 적용 가이드 ## 1) 목적과 원칙 JWT 만료 **5분 전**에 `"로그인을 연장하시겠습니까?"` confirm을 띄우고, 선택에 따라 세션을 연장하는 UX를 서비스 앱에 적용하기 위한 가이드입니다. 이 문서는 [서비스별 인증 처리 리팩토링 가이드](./auth-service-refactoring-guide.md)의 **후속 단계**입니다. 핵심 원칙: - confirm UX는 `@common/components` **Header**의 `useSessionExtendConfirm`이 담당한다. - confirm **확인** → `authService.silentRefresh()` - confirm **취소** → `authService.declineSessionExtend()` - 앱은 `ConfirmProvider` 계층과 `authService` 주입을 맞춘다. **적용 완료**: `cs`, `as`, `st` --- ## 2) 선행 조건 [리팩토링 가이드 체크리스트](./auth-service-refactoring-guide.md#5-적용-체크리스트)를 통과한 서비스만 진행한다. | 항목 | 리팩토링 가이드 | | --- | --- | | `authService` (`silentRefresh`, `declineSessionExtend`) | [4-4](./auth-service-refactoring-guide.md#44-인증-서비스) | | `BaseLayout`에 `authService` 주입 | [4-7](./auth-service-refactoring-guide.md#47-레이아웃provider-연결) | --- ## 3) 목표 아키텍처 ### 3-1. 레이어 책임 | 레이어 | 위치 | 역할 | | --- | --- | --- | | JWT 시간 계산 | `@common/auth` `jwt-utils.ts` | `getRemainingMs`, confirm 버퍼 상수 | | 토큰·정리 | `@common/auth` `token-manager.ts` | `setToken`, `clearRefreshTimer`, `clearAuthToken` | | API 오케스트레이션 | `entities/auth/api/auth-service.ts` | `silentRefresh`, `declineSessionExtend` | | confirm UX | `@common/components` Header | 카운트다운 + 5분 전 confirm | | 앱 진입점 | `entities/auth/lib/auth-wrapper.tsx` | auth 체크, `auth:logout` | | 레이아웃 | layout / BaseLayout wrapper | `authService` → Header | | UI 쉘 | `app/providers/app-provider.tsx` | `ConfirmProvider`, `ConfirmDialog` | ### 3-2. 런타임 흐름 ```mermaid sequenceDiagram participant Login as authService.login participant Header as Header participant User as 사용자 participant API as authService.silentRefresh Login->>Login: setToken Header->>Header: confirm 예약(exp - 5분) Note over Header: 만료 5분 전 Header->>User: 로그인을 연장하시겠습니까? alt 확인 User->>API: silentRefresh() API->>API: setToken Header->>Header: confirm 타이머 재설정 else 취소 Header->>Header: declineSessionExtend() end ``` | 시점 | 동작 | 담당 | | --- | --- | --- | | 로그인 / silentRefresh 성공 | 토큰 저장 | `authService` | | 만료 5분 전 | confirm 표시 | Header → `useSessionExtendConfirm` | | confirm **확인** | `silentRefresh` + confirm 타이머 재설정 | Header → `authService.silentRefresh` | | confirm **취소** | `declineSessionExtend` | Header → `authService.declineSessionExtend` | | 토큰 완전 만료 | logout | Header | --- ## 4) 공통 계약 ### 4-1. 시간 버퍼 (`getRemainingMs`) 파일: `packages/common/auth/jwt-utils.ts` ```ts export const SESSION_EXTEND_CONFIRM_BUFFER_MS = 5 * 60_000; // confirm: 만료 5분 전 getRemainingMs(token, SESSION_EXTEND_CONFIRM_BUFFER_MS); // → confirm 타이머 delay getRemainingMs(token, 0) > 0; // → 토큰 미만료 여부 ``` ### 4-2. Header ↔ AuthService 공통 Header가 아래처럼 연결한다. ```ts useSessionExtendConfirm({ enabled: mounted && isLoggedIn && !viewOnly && !!authService && !isAtLoginRedirectUrl(getAuthLoginRedirectUrl()), getAccessToken: () => getCookie(appConfig.CLAIM_NAME ?? ""), silentRefresh: authService?.silentRefresh ?? (async () => {}), declineSessionExtend: authService?.declineSessionExtend ?? (() => {}), }); ``` ### 4-3. Provider 계층 ``` ConfirmProvider └─ AuthWrapper └─ … → BaseLayout → Header └─ ConfirmDialog ``` --- ## 5) 적용 순서 ### 5-1. AppProvider — ConfirmProvider 마운트 파일: `app/providers/app-provider.tsx` ```tsx {children} ``` 참고: `apps/cs/src/app/providers/app-provider.tsx` ### 5-2. BaseLayout — authService 전달 ```tsx import { authService } from "@/entities/auth"; {children} ``` ### 5-3. authService — declineSessionExtend 리팩토링 [4-4](./auth-service-refactoring-guide.md#44-인증-서비스)에서 이미 구현했다면 확인만 한다. ```ts export const authService: AuthService = { login, silentRefresh, declineSessionExtend: clearRefreshTimer, logout, }; ``` --- ## 6) 엣지 케이스 | 상황 | 동작 | | --- | --- | | 토큰 유효 시간 < 5분 | 진입 즉시 confirm | | confirm **취소** | `declineSessionExtend()` + 동일 JWT에 재표시 안 함 | | confirm **확인** | `silentRefresh()` → 새 토큰 기준 confirm 타이머 재설정 | | `silentRefresh` 실패 | `auth:logout` → AuthWrapper 리다이렉트 | | 로그아웃 | `clearAuthToken`, 훅 cleanup | | 페이지 새로고침 | Header가 confirm 타이머 재예약 | --- ## 7) 적용 체크리스트 - [ ] `ConfirmProvider` > `AuthWrapper` > `BaseLayout` > `Header` 계층 - [ ] `ConfirmDialog`가 `ConfirmProvider` 하위에 마운트됨 - [ ] `BaseLayout`에 `authService` 전달 - [ ] `authService.declineSessionExtend` 구현 - [ ] 만료 5분 전 confirm 노출 - [ ] 확인 클릭 시 `SILENT_REFRESH` 호출 - [ ] 취소 후 세션이 연장되지 않음 --- ## 8) 검증 방법 1. 로그인 후 JWT `exp`가 5분 이내가 되도록 대기하거나 테스트 토큰 사용 2. confirm 문구 `"로그인을 연장하시겠습니까?"` 표시 확인 3. **확인** → Network에서 `SILENT_REFRESH` PUT, 쿠키 갱신, confirm 재예약 4. **취소** → 만료 시 로그아웃/리다이렉트 5. 새로고침 후 confirm 타이머 복구 --- ## 9) 참고 구현 | 앱 | auth-service | BaseLayout / layout | AppProvider | | --- | --- | --- | --- | | as (기준) | `apps/as/src/entities/auth/api/auth-service.ts` | `apps/as/src/app/layouts/base-layout.tsx` | (동등 Provider) | | cs | `apps/cs/src/entities/auth/api/auth-service.ts` | `apps/cs/app/(main)/(with-base-layout)/layout.tsx` | `apps/cs/src/app/providers/app-provider.tsx` | | st | `apps/st/src/entities/auth/api/auth-service.ts` | `apps/st/app/(main)/(with-base-layout)/layout.tsx` | (동등 Provider) | 공통: - `packages/common/components/src/ui/header/index.tsx` - `packages/common/components/src/lib/use-session-extend-confirm.ts` --- ## 10) 관련 문서 - [서비스별 인증 처리 리팩토링 가이드](./auth-service-refactoring-guide.md) - `packages/common/auth/README.md`