Files
frontend/docs/auth-service-refactoring-guide.md
T

4.8 KiB

서비스별 인증 처리 리팩토링 가이드

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)

서비스마다 이름이 다른 인증 환경 변수를 한 곳에서 노출한다.

예시 코드:

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,
  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 배럴 경로로 통일되어 있다.