Files
sam-react-prod/claudedocs/item-master/[REF-2025-12-01] state-sync-solutions.md
byeongcheolryu 3be5714805 refactor: 품목관리 시스템 리팩토링 및 Sales 페이지 추가
DynamicItemForm 개선:
- 품목코드 자동생성 기능 추가
- 조건부 표시 로직 개선
- 불필요한 컴포넌트 정리 (DynamicField, DynamicSection 등)
- 타입 시스템 단순화

새로운 기능:
- Sales 페이지 마이그레이션 (견적관리, 거래처관리)
- 공통 컴포넌트 추가 (atoms, molecules, organisms, templates)

문서화:
- 구현 문서 및 참조 문서 추가

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 12:48:41 +09:00

6.9 KiB

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

작성일: 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) 권장

// 현재 구조 (중첩, 중복 가능)
{
  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 패턴

// 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

// 서버 상태를 캐시로 관리
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:

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 - 훅 분리 작업 기록