188 lines
5.5 KiB
Markdown
188 lines
5.5 KiB
Markdown
# 서비스별 인증 처리 리팩토링 가이드
|
|
|
|
## 1) 문서 목적
|
|
|
|
여러 서비스 앱에서 중복 구현된 인증 로직을 공통 모듈(`@common/auth`) 기반으로 통일하기 위한 표준 가이드
|
|
|
|
핵심 원칙:
|
|
|
|
- 인증 정책은 공통 모듈에 모은다.
|
|
- 서비스별 차이는 설정(`authConfig`)으로만 표현한다.
|
|
- 토큰 갱신 타이머는 JWT `exp` 기준으로 계산한다.
|
|
|
|
## 2) 공통 아키텍처
|
|
|
|
인증 관련 책임을 아래처럼 분리한다.
|
|
|
|
- `shared/config/env.ts`: 환경 변수 노출
|
|
- `shared/config/auth.ts`: 서비스별 인증 키/도메인 설정
|
|
- `entities/auth/lib/token-manager.ts`: 공통 토큰 매니저 생성/re-export
|
|
- `entities/auth/api/*`: 로그인/토큰 갱신 API 오케스트레이션
|
|
- 레이아웃: BaseLayout 컴포넌트에 `tokenManager` 주입
|
|
|
|
## 3) 단계별 리팩토링 방법
|
|
|
|
### 3-1. 환경 변수 정의 통일 (`shared/config/env.ts`)
|
|
|
|
서비스마다 이름이 다른 인증 환경 변수를 한 곳에서 노출한다.
|
|
|
|
예시 코드:
|
|
|
|
```ts
|
|
export const CLAIM_NAME = process.env.NEXT_PUBLIC_CLAIM_NAME || "Authorization";
|
|
export const AUTH_USER_ID = process.env.NEXT_PUBLIC_AUTH_USER_ID || "token-id";
|
|
export const ACCESS_TOKEN =
|
|
process.env.NEXT_PUBLIC_ACCESS_TOKEN || "access-token";
|
|
export const REFRESH_TOKEN =
|
|
process.env.NEXT_PUBLIC_REFRESH_TOKEN || "refresh-token";
|
|
export const COOKIE_DOMAIN = process.env.NEXT_PUBLIC_COOKIE_DOMAIN;
|
|
```
|
|
|
|
### 3-2. 서비스별 인증 설정 분리 (`shared/config/auth.ts`)
|
|
|
|
하드코딩된 키명을 제거하고 `AuthConfig`로 매핑한다.
|
|
|
|
예시 코드:
|
|
|
|
```ts
|
|
import type { AuthConfig } from "@common/auth";
|
|
import {
|
|
ACCESS_TOKEN,
|
|
AUTH_USER_ID,
|
|
CLAIM_NAME,
|
|
COOKIE_DOMAIN,
|
|
REFRESH_TOKEN,
|
|
} from "./env";
|
|
|
|
export const authConfig: AuthConfig = {
|
|
authUserId: AUTH_USER_ID,
|
|
claimName: CLAIM_NAME,
|
|
cookieDomain: COOKIE_DOMAIN,
|
|
accessToken: ACCESS_TOKEN,
|
|
refreshToken: REFRESH_TOKEN,
|
|
};
|
|
```
|
|
|
|
### 3-3. 토큰 매니저 공통화 (`entities/auth/lib/token-manager.ts`)
|
|
|
|
서비스 고유 `TokenManager` 구현을 제거하고 `createTokenManager`를 사용한다.
|
|
|
|
예시 코드:
|
|
|
|
```ts
|
|
import { authConfig } from "@/shared/config/auth";
|
|
import { createTokenManager } from "@common/auth";
|
|
|
|
export const tokenManager = createTokenManager(authConfig);
|
|
|
|
export const {
|
|
getToken,
|
|
setToken,
|
|
removeToken,
|
|
setupRefreshTimer,
|
|
clearRefreshTimer,
|
|
clearAuthToken,
|
|
getAccessToken,
|
|
getRefreshToken,
|
|
getUserId,
|
|
} = tokenManager;
|
|
```
|
|
|
|
### 3-4. 로그인 성공 처리 표준화 (`entities/auth/api/login-service.ts`)
|
|
|
|
로그인 성공 시 토큰 저장과 자동 갱신 타이머 설정을 한 흐름으로 고정한다.
|
|
|
|
예시 코드:
|
|
|
|
```ts
|
|
import { getRemainingMs } from "@common/auth";
|
|
import { setToken, setupRefreshTimer } from "../lib/token-manager";
|
|
|
|
async function onLoginSuccess(params: {
|
|
accessToken: string;
|
|
tokenId: string;
|
|
refresh: () => Promise<void>;
|
|
}) {
|
|
setToken(authConfig.claimName, params.accessToken);
|
|
setToken(authConfig.authUserId, params.tokenId);
|
|
|
|
const remainingMs = getRemainingMs(params.accessToken);
|
|
if (remainingMs <= 0) return;
|
|
|
|
setupRefreshTimer(params.refresh, remainingMs);
|
|
}
|
|
```
|
|
|
|
### 3-5. 토큰 갱신 실패 처리 표준화
|
|
|
|
토큰 갱신 실패 시 인증 토큰을 일괄 정리한다.
|
|
|
|
예시 코드:
|
|
|
|
```ts
|
|
import { clearAuthToken } from "../lib/token-manager";
|
|
|
|
async function silentRefresh() {
|
|
try {
|
|
...
|
|
} catch (error) {
|
|
clearAuthToken();
|
|
throw error;
|
|
}
|
|
}
|
|
```
|
|
|
|
### 3-6. 공통 레이아웃/쉘에 토큰 매니저 주입
|
|
|
|
공통 컴포넌트가 인증 기능을 직접 사용할 수 있도록 주입한다.
|
|
|
|
예시 코드:
|
|
|
|
```tsx
|
|
import { tokenManager } from "@/entities/auth";
|
|
|
|
export function AppLayout({ children }: { children: React.ReactNode }) {
|
|
return (
|
|
<BaseLayout
|
|
tokenManager={tokenManager}
|
|
...
|
|
>
|
|
{children}
|
|
</BaseLayout>
|
|
);
|
|
}
|
|
```
|
|
|
|
### 3-7. 배럴 파일 re-export 정리 (`entities/auth/index.ts`)
|
|
|
|
외부에서 인증 엔티티를 일관된 경로로 사용하도록 re-export한다.
|
|
|
|
예시 코드:
|
|
|
|
```ts
|
|
export { LoginApi } from "./api/login-service";
|
|
export { AuthWrapper } from "./lib/auth-wrapper";
|
|
export { tokenManager } from "./lib/token-manager";
|
|
```
|
|
|
|
## 4) 서비스별 적용 체크리스트
|
|
|
|
각 서비스 담당자는 아래 항목을 모두 충족해야 한다.
|
|
|
|
- `authConfig`에 서비스별 키명이 정확히 매핑되어 있다.
|
|
- 서비스 내부 커스텀 TokenManager 클래스가 제거되었다.
|
|
- 로그인/갱신 로직이 `getRemainingMs` 기반 타이머를 사용한다.
|
|
- BaseLayout 컴포넌트에 `tokenManager`가 주입되어 있다.
|
|
- 외부 참조 경로가 `entities/auth` 배럴 경로로 통일되어 있다.
|
|
|
|
## 5) 세션 연장 Confirm 적용 (후속 단계)
|
|
|
|
리팩토링이 완료된 서비스(`authService` + `AuthWrapper` + `ConfirmProvider`)는 [세션 연장 Confirm 적용 가이드](./session-extend-confirm-guide.md)를 따라 JWT 만료 5분 전 confirm UX를 추가한다.
|
|
|
|
추가 체크 항목:
|
|
|
|
- `token-manager`에서 `clearRefreshTimer`를 re-export한다.
|
|
- `AuthWrapper`에 `useSessionExtendConfirm`을 연결한다.
|
|
- `clearRefreshTimer`는 앱 `token-manager` 싱글톤을 통해 전달한다 (`authService`의 `setupRefreshTimer`와 동일 인스턴스).
|
|
- `ConfirmProvider` 하위에 `AuthWrapper`와 `ConfirmDialog`가 마운트되어 있다.
|