# masterDataStore 캐시 테넌트 격리 수정

**작성일**: 2026-01-29
**타입**: 버그 수정 (캐시 격리 누락)
**관련 문서**: `[REF-2025-11-19] multi-tenancy-implementation.md`

---

## 배경

멀티테넌시 검토 결과, `TenantAwareCache`(`mes-{tenantId}-{key}`)는 테넌트별로 캐시가 격리되어 있지만, `masterDataStore`의 sessionStorage 캐시는 테넌트 구분 없이 `page_config_{pageType}` 키를 사용하고 있었음.

추가로 `setCurrentTenantId` 액션이 인터페이스에만 선언되어 있고 **구현도, 호출도 없는** dead code 상태였음.

---

## 문제

### 1. 캐시 키에 tenantId 미포함

```
TenantAwareCache:  mes-282-itemMasters       ← 테넌트 격리됨
masterDataStore:   page_config_item-master    ← 테넌트 격리 안됨
```

### 2. 발생 가능한 시나리오

```
1. 테넌트 282 사용자가 품목관리 접속
   → sessionStorage: page_config_item-master = {테넌트282 설정}

2. 세션 내 테넌트 500으로 전환 (로그아웃 없이)
   → clearTenantCache()는 mes-282-* 만 삭제
   → page_config_item-master 는 삭제되지 않음

3. 테넌트 500 사용자에게 테넌트 282의 페이지 설정이 노출
```

### 3. setCurrentTenantId 미구현

```typescript
// 인터페이스에 선언만 있고 구현 없음
interface MasterDataStore {
  currentTenantId: number | null;            // ← initialState에도 누락
  setCurrentTenantId: (tenantId: number | null) => void;  // ← 구현 없음
}
```

---

## 수정 내역

### masterDataStore.ts

| 영역 | Before | After |
|------|--------|-------|
| initialState | `currentTenantId` 누락 | `currentTenantId: null` 추가 |
| 캐시 키 포맷 | `page_config_{pageType}` | `page_config_{tenantId}_{pageType}` |
| setCurrentTenantId | 인터페이스만 선언 | 구현 추가 |
| fetchPageConfig | tenantId 미사용 | `currentTenantId`를 캐시 함수에 전달 |
| invalidateConfig | tenantId 미사용 | `currentTenantId` 기반 삭제 |
| invalidateAllConfigs | tenantId 미사용 | `currentTenantId` 기반 삭제 |
| reset() | pageType 목록 순회 삭제 | `page_config_` 프리픽스 기반 전체 삭제 |

#### 핵심 변경: 캐시 키 생성 함수 추가

```typescript
function getStorageKey(tenantId: number | null, pageType: PageType): string {
  return tenantId != null
    ? `${STORAGE_PREFIX}${tenantId}_${pageType}`   // page_config_282_item-master
    : `${STORAGE_PREFIX}${pageType}`;               // page_config_item-master (하위 호환)
}
```

#### 핵심 변경: reset()을 프리픽스 기반으로 변경

```typescript
// Before: 고정된 pageType 목록으로 삭제 (tenantId 포함 키를 찾지 못함)
pageTypes.forEach((pt) => removeConfigFromSessionStorage(pt));

// After: page_config_ 프리픽스로 모든 테넌트 캐시 일괄 삭제
Object.keys(window.sessionStorage).forEach(key => {
  if (key.startsWith(STORAGE_PREFIX)) {
    window.sessionStorage.removeItem(key);
  }
});
```

### AuthContext.tsx

| 영역 | Before | After |
|------|--------|-------|
| import | - | `useMasterDataStore` 추가 |
| tenantId 동기화 | 없음 | `currentUser.tenant.id` 변경 시 `setCurrentTenantId()` 호출 |
| clearTenantCache | `mes-{tenantId}-*` 만 삭제 | `mes-{tenantId}-*` + `page_config_{tenantId}_*` 삭제 |

#### 핵심 변경: tenantId 동기화 useEffect

```typescript
useEffect(() => {
  const tenantId = currentUser?.tenant?.id ?? null;
  useMasterDataStore.getState().setCurrentTenantId(tenantId);
}, [currentUser?.tenant?.id]);
```

#### 핵심 변경: clearTenantCache 범위 확장

```typescript
const tenantAwarePrefix = `mes-${tenantId}-`;
const pageConfigPrefix = `page_config_${tenantId}_`;

Object.keys(sessionStorage).forEach(key => {
  if (key.startsWith(tenantAwarePrefix) || key.startsWith(pageConfigPrefix)) {
    sessionStorage.removeItem(key);
  }
});
```

---

## 하위 호환

| 항목 | 영향 |
|------|------|
| 기존 캐시 키 | `page_config_item-master` → 키 불일치로 miss → API 재요청 → 새 포맷으로 자동 전환 |
| logout.ts | `page_config_` 프리픽스 매칭이 새 키 포맷(`page_config_282_item-master`)도 커버 |
| sessionStorage TTL | 10분 만료이므로 기존 키는 자연 소멸 |
| tenantId가 null인 경우 | 기존 포맷(`page_config_{pageType}`) 유지하여 동작 보장 |

---

## 효과

1. **세션 내 테넌트 전환 시 캐시 누수 차단**: `clearTenantCache`가 `page_config_{tenantId}_*`까지 삭제
2. **캐시 패턴 일관성**: TenantAwareCache(`mes-{tenantId}-`)와 masterDataStore(`page_config_{tenantId}_`) 모두 테넌트 격리
3. **dead code 해소**: `currentTenantId` 필드와 `setCurrentTenantId` 액션이 실제로 동작

---

## 관련 파일

- `src/stores/masterDataStore.ts` - 캐시 키 변경, setCurrentTenantId 구현
- `src/contexts/AuthContext.tsx` - tenantId 동기화, clearTenantCache 범위 확장
- `src/lib/auth/logout.ts` - 기존 `page_config_` 프리픽스 매칭 (변경 없음, 호환 확인)
- `src/lib/cache/TenantAwareCache.ts` - 참고 (기존 정상 동작)
