# 품목기준관리 상태 동기화 문제 및 해결 방안

**작성일**: 2025-12-01
**상태**: 참조 문서
**관련**: 서비스 레이어 리팩토링 후 발생한 동기화 버그 분석

---

## 1. 오늘 발생한 버그 분석

### 1.1 실시간 동기화 문제 (sectionsAsTemplates 순서)

```
문제: 섹션탭에서 항목 추가 → 계층구조만 업데이트, 항목탭/속성탭은 안됨
원인: Map 중복 제거 시 unlinkedSections가 linkedSections를 덮어씀
해결: [...unlinkedSections, ...linkedSections] 순서로 변경
```

**리팩토링으로 해결 가능?** ❌ 아니다. **데이터 소스 우선순위** 문제.

### 1.2 422 Validation Error

```
문제: 섹션탭에서 일반 섹션 항목 추가 시 422 에러
원인: 섹션탭은 POST /fields, 계층구조탭은 POST /sections/{id}/fields 사용
해결: 섹션탭도 addFieldToSection() 사용하도록 통일
```

**리팩토링으로 해결 가능?** ⚠️ 부분적. 서비스 레이어에서 API 선택을 중앙화할 수 있었지만, 두 탭이 **다른 API를 호출하는 것 자체**가 문제.

### 1.3 페이지 삭제 시 필드 소실

```
문제: 페이지 삭제 → 섹션의 필드까지 사라짐
원인: refreshIndependentSections()가 필드 없이 섹션만 반환
해결: 페이지의 섹션들을 직접 independentSections에 추가
```

**리팩토링으로 해결 가능?** ❌ 아니다. **백엔드 API 응답 불완전성** + **상태 동기화 전략** 문제.

---

## 2. 근본 원인: 동기화가 애매한 구조

### 2.1 현재 아키텍처의 문제점

```
┌─────────────────────────────────────────────────┐
│ 같은 "섹션" 데이터가 2곳에서 관리됨              │
├─────────────────────────────────────────────────┤
│ 1. itemPages[].sections[] ← 페이지에 연결된 섹션 │
│ 2. independentSections[]  ← 독립 섹션            │
├─────────────────────────────────────────────────┤
│ 문제: 두 소스가 동기화되지 않으면 불일치 발생!   │
└─────────────────────────────────────────────────┘
```

### 2.2 서비스 레이어 리팩토링의 한계

| 해결 가능 | 해결 불가 |
|-----------|-----------|
| ✅ validation/파싱 로직 중복 제거 | ❌ 상태 동기화 문제 |
| ✅ 코드 유지보수성 향상 | ❌ 데이터 소스 불일치 |
| ✅ 도메인 로직 중앙화 | ❌ API 응답 불완전성 |

---

## 3. 해결 방안

### 방법 1: 정규화된 상태 (Normalized State) ⭐ 권장

```typescript
// 현재 구조 (중첩, 중복 가능)
{
  itemPages: [
    { id: 1, sections: [{ id: 10, fields: [...] }] }
  ],
  independentSections: [
    { id: 10, fields: [...] }  // 같은 섹션이 다른 데이터로 존재!
  ]
}

// 정규화된 구조 (Single Source of Truth)
{
  entities: {
    pages: { 1: { id: 1, sectionIds: [10] } },
    sections: { 10: { id: 10, fieldIds: [100, 101] } },
    fields: { 100: {...}, 101: {...} }
  },
  // 관계는 ID로만 참조
  ui: {
    selectedPageId: 1,
    independentSectionIds: [20, 30]  // ID만 저장
  }
}
```

**장점:**
- 데이터 중복 없음 → 불일치 원천 차단
- 한 곳만 수정하면 모든 곳에 반영
- 메모리 효율적

**구현 도구:** Zustand + immer 또는 Redux Toolkit

---

### 방법 2: Derived State 패턴

```typescript
// Context에서 원본 데이터는 하나만 유지
const [allSections, setAllSections] = useState<Section[]>([]);
const [pageRelations, setPageRelations] = useState<{pageId: number, sectionId: number}[]>([]);

// 필요한 데이터는 계산으로 도출 (useMemo)
const sectionsForPage = useMemo(() =>
  allSections.filter(s =>
    pageRelations.some(r => r.pageId === selectedPageId && r.sectionId === s.id)
  ), [allSections, pageRelations, selectedPageId]
);

const independentSections = useMemo(() =>
  allSections.filter(s =>
    !pageRelations.some(r => r.sectionId === s.id)
  ), [allSections, pageRelations]
);
```

**장점:**
- 기존 Context 구조 유지 가능
- 점진적 마이그레이션 가능

---

### 방법 3: React Query / TanStack Query

```typescript
// 서버 상태를 캐시로 관리
const { data: sections } = useQuery({
  queryKey: ['sections'],
  queryFn: () => api.getSections({ includeFields: true })
});

// 뮤테이션 후 자동 갱신
const deletePage = useMutation({
  mutationFn: api.deletePage,
  onSuccess: () => {
    // 관련 쿼리 자동 갱신
    queryClient.invalidateQueries(['sections']);
    queryClient.invalidateQueries(['pages']);
  }
});
```

**장점:**
- 서버-클라이언트 동기화 자동화
- 캐싱, 재시도, 낙관적 업데이트 내장
- 수동 상태 관리 대폭 감소

---

### 방법 4: 백엔드 API 개선

```
현재: GET /sections → 섹션만 반환 (필드 없음)
개선: GET /sections?include=fields → 섹션 + 필드 반환
```

**또는 GraphQL:**
```graphql
query {
  sections {
    id
    title
    fields {  # 필요한 관계 데이터 명시적 요청
      id
      field_name
    }
  }
}
```

---

## 4. 현실적인 적용 순서

| 단계 | 방법 | 난이도 | 효과 | 설명 |
|------|------|--------|------|------|
| 1단계 | Derived State 패턴 | 🟢 낮음 | 즉시 적용 가능 | 기존 구조 유지하며 파생 데이터 정리 |
| 2단계 | 백엔드 API 개선 요청 | 🟡 중간 | 근본 해결 | `include=fields` 파라미터 추가 |
| 3단계 | React Query 도입 | 🟡 중간 | 동기화 자동화 | 서버 상태 관리 단순화 |
| 4단계 | 정규화된 상태 | 🔴 높음 | 완전한 해결 | 대규모 리팩토링 필요 |

---

## 5. 프로젝트 추천 방향

### 단기 (즉시 적용 가능)
- Derived State 패턴으로 `sectionsAsTemplates` 같은 파생 데이터 정리
- 중복 데이터 소스 최소화

### 중기 (백엔드 협업 필요)
- 백엔드에 `GET /sections?include=fields` API 추가 요청
- 관계 데이터 포함 응답으로 프론트엔드 동기화 부담 감소

### 장기 (대규모 개선)
- React Query 도입으로 서버 상태 관리 단순화
- 또는 정규화된 상태 구조로 전환

---

## 6. 결론

| 구분 | 내용 |
|------|------|
| **서비스 레이어** | 도메인 로직 중복 해결 ✅ |
| **상태 동기화** | 아키텍처 레벨 개선 필요 ⚠️ |
| **근본 원인** | 같은 데이터가 여러 곳에서 관리됨 |
| **해결 방향** | Single Source of Truth 패턴 적용 |

---

## 7. 관련 문서

- `[PLAN-2025-12-01] service-layer-refactoring.md` - 서비스 레이어 리팩토링 계획
- `[REF-2025-11-26] item-master-hooks-refactoring.md` - 훅 분리 작업 기록