# 세션 연장 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`