DynamicItemForm 개선: - 품목코드 자동생성 기능 추가 - 조건부 표시 로직 개선 - 불필요한 컴포넌트 정리 (DynamicField, DynamicSection 등) - 타입 시스템 단순화 새로운 기능: - Sales 페이지 마이그레이션 (견적관리, 거래처관리) - 공통 컴포넌트 추가 (atoms, molecules, organisms, templates) 문서화: - 구현 문서 및 참조 문서 추가 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
6.9 KiB
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=fieldsAPI 추가 요청 - 관계 데이터 포함 응답으로 프론트엔드 동기화 부담 감소
장기 (대규모 개선)
- 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- 훅 분리 작업 기록