5.5 KiB
5.5 KiB
서비스별 인증 처리 리팩토링 가이드
1) 문서 목적
여러 서비스 앱에서 중복 구현된 인증 로직을 공통 모듈(@common/auth) 기반으로 통일하기 위한 표준 가이드
핵심 원칙:
- 인증 정책은 공통 모듈에 모은다.
- 서비스별 차이는 설정(
authConfig)으로만 표현한다. - 토큰 갱신 타이머는 JWT
exp기준으로 계산한다.
2) 공통 아키텍처
인증 관련 책임을 아래처럼 분리한다.
shared/config/env.ts: 환경 변수 노출shared/config/auth.ts: 서비스별 인증 키/도메인 설정entities/auth/lib/token-manager.ts: 공통 토큰 매니저 생성/re-exportentities/auth/api/*: 로그인/토큰 갱신 API 오케스트레이션- 레이아웃: BaseLayout 컴포넌트에
tokenManager주입
3) 단계별 리팩토링 방법
3-1. 환경 변수 정의 통일 (shared/config/env.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로 매핑한다.
예시 코드:
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를 사용한다.
예시 코드:
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)
로그인 성공 시 토큰 저장과 자동 갱신 타이머 설정을 한 흐름으로 고정한다.
예시 코드:
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. 토큰 갱신 실패 처리 표준화
토큰 갱신 실패 시 인증 토큰을 일괄 정리한다.
예시 코드:
import { clearAuthToken } from "../lib/token-manager";
async function silentRefresh() {
try {
...
} catch (error) {
clearAuthToken();
throw error;
}
}
3-6. 공통 레이아웃/쉘에 토큰 매니저 주입
공통 컴포넌트가 인증 기능을 직접 사용할 수 있도록 주입한다.
예시 코드:
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한다.
예시 코드:
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 적용 가이드를 따라 JWT 만료 5분 전 confirm UX를 추가한다.
추가 체크 항목:
token-manager에서clearRefreshTimer를 re-export한다.AuthWrapper에useSessionExtendConfirm을 연결한다.clearRefreshTimer는 앱token-manager싱글톤을 통해 전달한다 (authService의setupRefreshTimer와 동일 인스턴스).ConfirmProvider하위에AuthWrapper와ConfirmDialog가 마운트되어 있다.