# 멀티테넌시 공통화 및 최적화 로드맵

**작성일**: 2026-02-06
**목적**: 전체 프로젝트 멀티테넌시 준비 상태 점검 및 공통화/최적화 계획 수립
**이전 문서**: `[REF-2025-11-19] multi-tenancy-implementation.md` (Phase 1-2 완료)

---

## 현재 상태 요약 (2026-02-06 기준)

### 완료된 항목 (이전 로드맵 Phase 1-2)

| 항목 | 상태 | 파일 |
|------|------|------|
| User 타입에 Tenant 객체 포함 | ✅ 완료 | `src/contexts/AuthContext.tsx` |
| Tenant 인터페이스 정의 (id, company_name 등) | ✅ 완료 | `src/contexts/AuthContext.tsx` |
| TenantAwareCache 유틸리티 | ✅ 완료 | `src/lib/cache/TenantAwareCache.ts` |
| 테넌트 전환 감지 + 캐시 클리어 | ✅ 완료 | `src/contexts/AuthContext.tsx` |
| masterDataStore 테넌트 스코프 캐시 키 | ✅ 완료 | `src/stores/masterDataStore.ts` |
| sessionStorage/localStorage 테넌트 격리 | ✅ 완료 | `mes-{tenantId}-{key}` 패턴 |

### 미완료 / 개선 필요 항목

| 영역 | 우선순위 | 현재 상태 |
|------|----------|-----------|
| API 프록시에 테넌트 컨텍스트 전달 | 🔴 | X-Tenant-ID 헤더 없음 |
| Server Actions 테넌트 인식 | 🔴 | 70+ actions.ts에 테넌트 미포함 |
| 포매터/유틸리티 다국어/다통화 | 🔴 | 한국어 하드코딩 |
| 브랜딩 동적화 (로고, 앱이름) | 🟡 | "SAM", sam-logo.png 하드코딩 |
| 상수/공휴일 외부화 | 🟡 | 한국 공휴일 하드코딩 |
| localStorage 직접 사용 잔재 | 🟡 | TenantAwareCache 미사용 곳 존재 |
| tenantId 타입 불일치 | 🟡 | string vs number 혼재 |
| 테넌트 라우팅 | 🟢 | 현재 없음 (필요 시 추가) |
| TenantContext Provider | 🟢 | 테넌트 설정 전용 Context 없음 |

---

## 작업 영역 구분: 프론트 단독 vs 백엔드 협의

### 선행 확인 사항

> **핵심 질문**: "백엔드가 이미 JWT 토큰 안의 tenant_id로 데이터를 필터링하고 있는가?"
>
> - **Yes** → 프론트에서 별도 X-Tenant-ID 안 보내도 됨. Phase 1은 불필요하고 프론트 단독 Phase부터 진행
> - **No** → 백엔드도 같이 수정 필요. Phase 1이 최우선

### 프론트 단독 가능 (백엔드 수정 불필요)

| Phase | 작업 | 이유 |
|-------|------|------|
| **3** | 포매터 다국어/다통화 전환 | `formatAmount()`, `formatDate()` 등 프론트 유틸리티 내부 수정. 기본값을 한국어로 유지하면 하위 호환 |
| **6** | localStorage 잔재 정리 + tenantId 타입 통일 | 프론트 코드 정리. TenantAwareCache 미사용 곳 마이그레이션, `string` → `number` 통일 |
| **8** | 테넌트 라우팅 (필요 시) | Next.js App Router 구조 변경. 순수 프론트 라우팅 |

> **즉시 착수 가능**: 백엔드 협의 결과를 기다리지 않고 바로 시작할 수 있음

### 백엔드 협의 필요 (프론트 + 백엔드 동시 수정)

| Phase | 작업 | 백엔드 필요 이유 |
|-------|------|-----------------|
| **1** | API 테넌트 컨텍스트 주입 | 프론트에서 `X-Tenant-ID` 헤더를 보내도, **백엔드가 읽고 필터링**해줘야 의미 있음. 안 읽으면 보내봤자 무용지물 |
| **2** | Server Actions 마이그레이션 | Phase 1에 종속. 백엔드가 헤더 or URL로 테넌트를 구분 안 하면 프론트만 바꿔도 소용없음 |
| **4** | 브랜딩 동적화 | 테넌트별 로고/앱이름을 **어디서 가져오나?** → 백엔드 API 필요 (`GET /api/v1/tenant/config`) |
| **5** | 상수/공휴일 외부화 | 공휴일 데이터를 **DB에서 서빙**해야 함 → 백엔드 API 필요 (`GET /api/v1/holidays?year=2026`) |
| **7** | TenantConfigService | 테넌트 설정 통합 API 필요 → branding + regional + features를 한 번에 가져오는 엔드포인트 |

### 추천 진행 순서

```
[즉시 시작 - 프론트 단독]
  Phase 3 (포매터) + Phase 6 (localStorage/타입) 병렬 진행

[백엔드 협의 후 시작]
  Phase 1 (API 헤더) → Phase 2 (Actions)

[백엔드 API 준비 후 시작]
  Phase 7 (TenantConfigService) → Phase 4 (브랜딩) + Phase 5 (상수)
```

---

## Phase 1: API 레이어 테넌트 컨텍스트 주입 🔴 `백엔드 협의 필요`

> **목표**: 모든 백엔드 API 호출에 테넌트 식별 정보가 전달되도록 함
> **예상**: 3-5일

### 1-1. 로그인 시 tenant_id 쿠키 추가

**파일**: `src/app/api/auth/login/route.ts`

**현재**: access_token, refresh_token 쿠키만 설정
**변경**: tenant_id 쿠키 추가 (HttpOnly, API 프록시에서 읽기용)

```typescript
// 로그인 성공 후 추가
const tenantCookie = [
  `tenant_id=${data.tenant.id}`,
  'HttpOnly',
  ...(isProduction ? ['Secure'] : []),
  'SameSite=Lax',
  'Path=/',
  `Max-Age=${data.expires_in || 7200}`,
].join('; ');
response.headers.append('Set-Cookie', tenantCookie);
```

### 1-2. API 프록시에 X-Tenant-ID 헤더 추가

**파일**: `src/app/api/proxy/[...path]/route.ts`

**현재**:
```typescript
const headers = {
  'Accept': 'application/json',
  'X-API-KEY': process.env.API_KEY || '',
  'Authorization': `Bearer ${token}`,
};
```

**변경**:
```typescript
const tenantId = request.cookies.get('tenant_id')?.value;
const headers = {
  'Accept': 'application/json',
  'X-API-KEY': process.env.API_KEY || '',
  'Authorization': `Bearer ${token}`,
  ...(tenantId && { 'X-Tenant-ID': tenantId }),
};
```

### 1-3. serverFetch 래퍼에 테넌트 헤더 추가

**파일**: `src/lib/api/fetch-wrapper.ts`

**현재**: Authorization 헤더만 전달
**변경**: tenant_id 쿠키 읽어서 X-Tenant-ID 헤더 자동 추가

```typescript
export async function serverFetch(url: string, options?: RequestInit) {
  const cookieStore = await cookies();
  const token = cookieStore.get('access_token')?.value;
  const tenantId = cookieStore.get('tenant_id')?.value;

  const headers = {
    ...options?.headers,
    'Authorization': `Bearer ${token}`,
    ...(tenantId && { 'X-Tenant-ID': tenantId }),
  };
  // ...
}
```

### 1-4. ApiClient 클래스에 테넌트 지원

**파일**: `src/lib/api/client.ts`

**변경**: `getAuthHeaders()`에 X-Tenant-ID 포함

### 체크리스트

```
- [ ] login/route.ts에 tenant_id 쿠키 Set-Cookie 추가
- [ ] proxy/[...path]/route.ts에서 tenant_id 쿠키 읽기 + X-Tenant-ID 헤더 전달
- [ ] fetch-wrapper.ts serverFetch에 X-Tenant-ID 자동 추가
- [ ] client.ts ApiClient에 tenantId 옵션 추가
- [ ] authenticated-fetch.ts에도 테넌트 헤더 전파 확인
- [ ] 로그아웃 시 tenant_id 쿠키 삭제 확인
- [ ] 토큰 갱신 시 tenant_id 쿠키 유지 확인
- [ ] 백엔드와 X-Tenant-ID 헤더 수신 방식 협의
```

---

## Phase 2: Server Actions 점진적 마이그레이션 🔴 `백엔드 협의 필요`

> **목표**: 70+ actions.ts에서 테넌트 컨텍스트가 자동 전달되도록 함
> **예상**: 1-2주 (Phase 1 완료 후 자동 적용되는 부분 다수)

### 2-1. 현재 패턴 분석

대부분의 actions.ts가 이 패턴을 따름:
```typescript
const url = `${process.env.NEXT_PUBLIC_API_URL}/api/v1/endpoint`;
const { response, error } = await serverFetch(url, { method: 'GET' });
```

### 2-2. 자동 적용 범위 (Phase 1 완료 시)

Phase 1에서 `serverFetch`에 X-Tenant-ID를 자동 추가하면, **기존 actions.ts 대부분은 수정 없이** 테넌트 헤더가 전달됨.

### 2-3. 수동 확인 필요 케이스

`serverFetch`를 사용하지 않고 직접 `fetch()`를 호출하는 곳:
```bash
# 검색 대상
grep -r "fetch(" src/components/*/actions.ts --include="*.ts" | grep -v serverFetch
```

### 2-4. 선택적 URL 테넌트 프리픽스

백엔드가 URL 경로에 테넌트를 요구하는 경우만:
```typescript
// 필요한 경우에만 적용
function buildTenantUrl(endpoint: string, tenantId?: string): string {
  if (endpoint.startsWith('http')) return endpoint; // 레거시 호환
  const base = process.env.NEXT_PUBLIC_API_URL;
  return tenantId
    ? `${base}/api/v1/tenant/${tenantId}/${endpoint}`
    : `${base}/api/v1/${endpoint}`;
}
```

### 체크리스트

```
- [ ] serverFetch 사용하지 않는 actions.ts 목록 확인
- [ ] 직접 fetch() 호출하는 곳 serverFetch로 마이그레이션
- [ ] 백엔드와 URL 패턴 vs 헤더 패턴 최종 협의
- [ ] 고빈도 도메인 우선 검증: clients, items, production, sales
- [ ] 에러 시 테넌트 컨텍스트 누락 로그 추가
```

---

## Phase 3: 포매터 & 유틸리티 테넌트 설정 기반 전환 🔴 `프론트 단독 가능`

> **목표**: 한국어 하드코딩된 포매터를 테넌트 설정 기반으로 변경
> **예상**: 3-5일

### 3-1. 영향받는 파일 목록

| 파일 | 함수 | 하드코딩 내용 |
|------|------|---------------|
| `src/utils/formatAmount.ts` | `formatAmount()` | "원", "만원" |
| `src/utils/formatAmount.ts` | `formatKoreanAmount()` | "억", "만" |
| `src/lib/formatters.ts` | `formatBusinessNumber()` | 한국 사업자번호 (XXX-XX-XXXXX) |
| `src/lib/formatters.ts` | `formatPhoneNumber()` | 한국 전화 (02-, 010-) |
| `src/utils/date.ts` | `formatDate()` | `'ko-KR'` 로케일 |

### 3-2. TenantRegionalConfig 인터페이스

```typescript
// src/types/tenant-config.ts (신규)
export interface TenantRegionalConfig {
  locale: string;           // 'ko-KR' | 'en-US' | 'ja-JP'
  timezone: string;         // 'Asia/Seoul' | 'America/New_York'
  currency: {
    code: string;           // 'KRW' | 'USD' | 'JPY'
    symbol: string;         // '원' | '$' | '¥'
    locale: string;         // Intl.NumberFormat 로케일
    largeUnitName?: string; // '만' (한국 전용)
    largeUnitValue?: number; // 10000
  };
  phone: {
    countryCode: string;    // '+82' | '+1' | '+81'
    format: string;         // 'XXX-XXXX-XXXX'
  };
  businessNumber: {
    format: string;         // 'XXX-XX-XXXXX'
    label: string;          // '사업자번호' | 'Business No.' | '法人番号'
  };
}
```

### 3-3. 마이그레이션 접근 (하위 호환)

기존 함수를 바로 변경하지 않고, 오버로드 + 기본값 패턴 적용:

```typescript
// 기존 호출 코드를 깨지 않는 방식
export function formatAmount(amount: number, config?: TenantCurrencyConfig): string {
  const cfg = config ?? DEFAULT_KR_CURRENCY_CONFIG; // 기본값: 한국
  // ... 테넌트 설정 기반 포매팅
}
```

### 3-4. 기존 공통화 작업 참조

**이미 작성된 관련 문서**:
- `claudedocs/[IMPL-2026-02-05] formatter-commonization-plan.md`
- `claudedocs/[ANALYSIS-2026-01-20] 공통화-현황-분석.md`

이 문서들의 포매터 공통화 계획과 병합하여 진행.

### 체크리스트

```
- [ ] TenantRegionalConfig 인터페이스 정의
- [ ] DEFAULT_KR_CONFIG 기본값 생성 (하위 호환)
- [ ] formatAmount() 테넌트 설정 지원 추가
- [ ] formatDate() 테넌트 로케일 지원 추가
- [ ] formatBusinessNumber() 포맷 설정 지원 추가
- [ ] formatPhoneNumber() 국가 코드 지원 추가
- [ ] 기존 호출 코드 깨지지 않는지 검증
- [ ] formatter-commonization-plan.md와 통합
```

---

## Phase 4: 브랜딩 동적화 🟡 `백엔드 API 필요`

> **목표**: 하드코딩된 회사명/로고를 테넌트 설정 기반으로 변경
> **예상**: 2-3일

### 4-1. 영향받는 파일 목록

| 파일 | 하드코딩 | 변경 방향 |
|------|----------|-----------|
| `src/layouts/AuthenticatedLayout.tsx` | `APP_NAME = 'SAM'` | `tenant.company_name` 또는 테넌트 설정 |
| `src/layouts/AuthenticatedLayout.tsx` | `<Image src="/sam-logo.png">` | 테넌트별 로고 URL |
| `src/layouts/AuthenticatedLayout.tsx` | `MOCK_COMPANIES` 배열 | `user.tenant.other_tenants` 연동 |
| `src/app/[locale]/layout.tsx` | `APP_TITLE = 'SAM - 내 손안의 대시보드'` | 테넌트 설정 기반 |
| `src/app/[locale]/layout.tsx` | SEO 메타데이터 | 테넌트별 (단, 폐쇄형이므로 낮은 우선순위) |

### 4-2. 테넌트 브랜딩 설정 구조

```typescript
// src/types/tenant-config.ts에 추가
export interface TenantBrandingConfig {
  appName: string;            // 'SAM' | '주일 MES' | 커스텀
  appSubtitle?: string;       // 'Smart Automation Management'
  logoUrl: string;            // '/sam-logo.png' | '/tenants/282/logo.png'
  faviconUrl?: string;
  primaryColor?: string;      // 테마 주색상
  loginBackground?: string;   // 로그인 페이지 배경
}
```

### 4-3. 적용 방식

```typescript
// AuthenticatedLayout.tsx 내부
const { currentUser } = useAuth();
const branding = currentUser?.tenant?.branding ?? DEFAULT_BRANDING;

// 로고
<Image src={branding.logoUrl} alt={branding.appName} />

// 앱 이름
<h1>{branding.appName}</h1>
```

### 4-4. MOCK_COMPANIES → other_tenants 연동

**현재**: 하드코딩 목업
```typescript
const MOCK_COMPANIES = [
  { id: 'all', name: '전체' },
  { id: 'company1', name: '(주)삼성건설' },
  ...
];
```

**변경**: 실제 테넌트 데이터 연동
```typescript
const tenantOptions = useMemo(() => {
  const current = currentUser?.tenant;
  const others = current?.other_tenants ?? [];
  return [current, ...others].filter(Boolean);
}, [currentUser]);
```

### 체크리스트

```
- [ ] TenantBrandingConfig 인터페이스 정의
- [ ] DEFAULT_BRANDING 기본값 (현재 SAM 설정)
- [ ] AuthenticatedLayout 로고/앱이름 동적화
- [ ] MOCK_COMPANIES를 other_tenants 기반으로 교체
- [ ] 로그인 페이지 브랜딩 동적화
- [ ] favicon 동적 변경 (선택)
- [ ] 테넌트별 로고 파일 서빙 방식 결정 (public/ vs API)
```

---

## Phase 5: 상수 & 비즈니스 로직 외부화 🟡 `백엔드 API 필요`

> **목표**: 한국 특화 상수를 테넌트/국가별 설정으로 외부화
> **예상**: 3-5일

### 5-1. 영향받는 항목

| 항목 | 파일 | 현재 | 변경 |
|------|------|------|------|
| 공휴일 | `src/constants/calendarEvents.ts` | 한국 공휴일 하드코딩 | DB/API 기반 |
| 프로세스 타입 | `src/types/process.ts` | "생산", "검사" 등 | i18n 라벨 |
| 상태 라벨 | `src/lib/utils/status-config.ts` | "대기", "완료" 등 | i18n 라벨 |
| 품목 타입 | `src/types/item.ts` | "제품", "부품" 등 | i18n 라벨 |
| 근무일 | 관련 컴포넌트 | 월-금 하드코딩 | 테넌트 설정 |

### 5-2. 외부화 전략

**공휴일**: 백엔드 API로 이동 (테넌트별 국가 설정에 따라 반환)
```typescript
// AS-IS: 하드코딩
const HOLIDAYS_2026 = [
  { date: '2026-01-01', name: '신정', type: 'holiday' },
  ...
];

// TO-BE: API 호출
const holidays = await getHolidays(tenantId, year);
```

**라벨/상태**: next-intl 다국어 시스템 활용 (이미 ko/en/ja 구조 있음)
```typescript
// AS-IS
const statusLabels = { pending: '대기', completed: '완료' };

// TO-BE
const t = useTranslations('status');
const label = t('pending'); // 로케일에 따라 자동 변환
```

### 체크리스트

```
- [ ] calendarEvents.ts 공휴일 데이터 → API 엔드포인트로 이동
- [ ] 프로세스 타입 라벨 → messages/ko.json, en.json, ja.json으로 이동
- [ ] 상태 라벨 → i18n 키로 변환
- [ ] 품목 타입 라벨 → i18n 키로 변환
- [ ] 근무일 설정 → 테넌트 config로 이동
- [ ] 백엔드에 공휴일 API 요청
```

---

## Phase 6: localStorage 잔재 정리 & 타입 통일 🟡 `프론트 단독 가능`

> **목표**: TenantAwareCache 미사용 곳 정리 + tenantId 타입 통일
> **예상**: 2-3일

### 6-1. localStorage 직접 사용 감사

```bash
# 검색 대상
grep -r "localStorage\.\(setItem\|getItem\)" src/ --include="*.ts" --include="*.tsx"
```

**알려진 비-테넌트-스코프 키**:
- `mes-users` → 사용자 목록 (테넌트 스코프 필요 여부 검토)
- `mes-currentUser` → 현재 사용자 (로그인 상태이므로 테넌트 무관)
- 기타 직접 사용 곳 → TenantAwareCache 또는 테넌트 프리픽스 적용

### 6-2. tenantId 타입 통일

**현재 상황**:
- `User.tenant.id` → `number` (AuthContext)
- `PageConfig.tenantId` → `string` (masterDataStore)
- TenantAwareCache → `number`

**통일**: `number`로 표준화 (백엔드 응답 기준)

```typescript
// 수정 대상 찾기
grep -r "tenantId.*string" src/ --include="*.ts"
```

### 체크리스트

```
- [ ] localStorage 직접 사용 전수 조사
- [ ] TenantAwareCache로 마이그레이션 가능한 곳 목록화
- [ ] 테넌트 스코프 불필요한 곳 명시 (mes-currentUser 등)
- [ ] tenantId: string → number 통일
- [ ] PageConfig 타입 수정
- [ ] 관련 타입 참조 전부 업데이트
```

---

## Phase 7: TenantConfigService & TenantContext (선택) 🟢 `백엔드 API 필요`

> **목표**: 테넌트 설정을 한곳에서 관리하는 서비스 레이어
> **예상**: 3-5일 (Phase 3-5 진행 중 필요에 따라)

### 7-1. TenantConfigService

```typescript
// src/services/TenantConfigService.ts (신규)
export interface TenantConfiguration {
  tenantId: number;
  branding: TenantBrandingConfig;
  regional: TenantRegionalConfig;
  features: {
    enabledModules: string[];
    customFields?: Record<string, unknown>;
  };
  calendar: {
    workingDays: number[];   // [1,2,3,4,5] = 월-금
    holidays: HolidayEntry[];
  };
}

class TenantConfigService {
  private cache: Map<number, TenantConfiguration> = new Map();

  async getConfig(tenantId: number): Promise<TenantConfiguration> {
    if (this.cache.has(tenantId)) return this.cache.get(tenantId)!;
    const config = await this.fetchFromApi(tenantId);
    this.cache.set(tenantId, config);
    return config;
  }
}
```

### 7-2. TenantContext Provider

```typescript
// src/contexts/TenantContext.tsx (신규)
export function TenantProvider({ children }: { children: ReactNode }) {
  const { currentUser } = useAuth();
  const [config, setConfig] = useState<TenantConfiguration>();

  useEffect(() => {
    if (currentUser?.tenant?.id) {
      tenantConfigService.getConfig(currentUser.tenant.id)
        .then(setConfig);
    }
  }, [currentUser?.tenant?.id]);

  return (
    <TenantContext.Provider value={config}>
      {children}
    </TenantContext.Provider>
  );
}

// 사용
const tenantConfig = useTenantConfig();
const currencySymbol = tenantConfig.regional.currency.symbol;
```

### 체크리스트

```
- [ ] TenantConfiguration 통합 인터페이스 설계
- [ ] TenantConfigService 구현 (캐시 + API 호출)
- [ ] TenantContext Provider 구현
- [ ] useTenantConfig() 훅 구현
- [ ] Protected Layout에 TenantProvider 추가
- [ ] 기존 코드에서 점진적 마이그레이션
```

---

## Phase 8: 테넌트 라우팅 (필요 시) 🟢 `프론트 단독 가능`

> **목표**: URL에 테넌트 식별자 포함 (필요한 경우에만)
> **예상**: 1주+

### 현재 라우팅
```
/[locale]/(protected)/dashboard
```

### 옵션 A: 경로 기반 (권장 - 필요 시)
```
/[tenant]/[locale]/(protected)/dashboard
/acme/ko/dashboard
```

### 옵션 B: 서브도메인 기반
```
acme.sam.com/ko/dashboard
```

### 옵션 C: 현재 유지 (인증 기반만)
```
/[locale]/(protected)/dashboard  ← 테넌트는 JWT/쿠키로만 식별
```

**결정**: 현재는 **옵션 C 유지**. 다중 테넌트 URL 분리가 필요해지면 옵션 A 도입.

---

## 백엔드 협의 사항

### 필수 협의 (Phase 1 시작 전)

| 항목 | 질문 | 결정 사항 |
|------|------|-----------|
| 테넌트 식별 방식 | `X-Tenant-ID` 헤더 vs URL 경로 vs JWT만? | TBD |
| X-Tenant-ID 수신 | 백엔드가 이 헤더를 읽고 필터링하는지? | TBD |
| JWT 내 tenant_id | 토큰에 tenant_id가 포함되어 있는지? | TBD |
| 공휴일 API | `GET /api/v1/holidays?year=2026` 지원? | TBD |
| 테넌트 설정 API | `GET /api/v1/tenant/config` 지원? | TBD |

### 선택 협의 (Phase 4-5 시작 전)

| 항목 | 질문 | 결정 사항 |
|------|------|-----------|
| 테넌트 로고 | 로고 URL을 어디서 제공? (API vs 파일서버) | TBD |
| 브랜딩 설정 | 테넌트별 앱이름/테마 API 제공 가능? | TBD |
| 다국어 라벨 | 백엔드 코드 라벨이 다국어 지원? | TBD |

---

## 실행 우선순위 요약

```
[프론트 단독]  Phase 3: 포매터 테넌트 설정 기반      🔴 3-5일   ← 즉시 시작 가능
[프론트 단독]  Phase 6: localStorage 정리/타입 통일  🟡 2-3일   ← 즉시 시작 가능
[프론트 단독]  Phase 8: 테넌트 라우팅               🟢 필요시   ← 당분간 불필요

[백엔드 협의]  Phase 1: API 테넌트 컨텍스트 주입    🔴 3-5일   ← 백엔드 확인 후
[백엔드 협의]  Phase 2: Server Actions 마이그레이션  🔴 1-2주   ← Phase 1 후 자동 적용 범위 큼

[백엔드 API]   Phase 4: 브랜딩 동적화               🟡 2-3일   ← 테넌트 설정 API 필요
[백엔드 API]   Phase 5: 상수/공휴일 외부화           🟡 3-5일   ← 공휴일 API 필요
[백엔드 API]   Phase 7: TenantConfigService         🟢 3-5일   ← 통합 설정 API 필요
```

### 병렬 진행 가능 조합

```
[즉시 시작 - 프론트 단독]
├─ Phase 3 (포매터) ─────────→ 독립 완료
└─ Phase 6 (localStorage) ──→ 독립 완료

[백엔드 협의 후 - 프론트+백엔드]
└─ Phase 1 (API 헤더) ──────→ Phase 2 (Actions 자동 적용)

[백엔드 API 준비 후 - 프론트+백엔드]
└─ Phase 7 (TenantConfig) ──→ Phase 4 (브랜딩) + Phase 5 (상수)
```

---

## 위험 요소 & 대응

| 위험 | 확률 | 영향 | 대응 |
|------|------|------|------|
| 70+ actions.ts 수동 마이그레이션 | 높음 | 중간 | serverFetch 자동 주입으로 대부분 해결 |
| 백엔드 X-Tenant-ID 미지원 | 중간 | 높음 | Phase 1 시작 전 백엔드 팀 협의 필수 |
| 포매터 변경 시 기존 UI 깨짐 | 낮음 | 중간 | 기본값 패턴으로 하위 호환 유지 |
| 캐시 무효화 누락 | 낮음 | 높음 | TenantAwareCache 이미 검증됨 |
| 다국어 번역 리소스 부족 | 중간 | 낮음 | 한국어 기본값 유지, 점진 추가 |

---

## 관련 문서

| 문서 | 설명 |
|------|------|
| `architecture/[REF-2025-11-19] multi-tenancy-implementation.md` | 이전 멀티테넌시 구현 (Phase 1-2 → 완료됨) |
| `architecture/[TEST-2025-11-19] multi-tenancy-test-guide.md` | 캐시 격리 테스트 가이드 |
| `architecture/[FIX-2026-01-29] masterdata-cache-tenant-isolation.md` | masterDataStore 캐시 테넌트 격리 수정 |
| `[IMPL-2026-02-05] formatter-commonization-plan.md` | 포매터 공통화 계획 (Phase 3과 병합) |
| `[ANALYSIS-2026-01-20] 공통화-현황-분석.md` | 공통화 현황 분석 |
| `[ANALYSIS-2026-02-05] list-page-commonization-status.md` | 리스트 페이지 공통화 현황 |
| `auth/[IMPL-2025-11-07] jwt-cookie-authentication-final.md` | JWT 쿠키 인증 구현 |
| `api/[REF] api-requirements.md` | API 요구사항 |

---

## 변경 이력

| 날짜 | 변경 내용 |
|------|-----------|
| 2026-02-06 | 초기 작성 - 전체 코드베이스 분석 기반 8 Phase 로드맵 |

---

**다음 액션**: Phase 1 시작 전 백엔드 팀과 `X-Tenant-ID` 헤더 수신 방식 협의
