2026-01-26 22:12:17 +09:00
|
|
|
# 변경 내용 요약
|
|
|
|
|
|
|
|
|
|
**날짜:** 2026-01-26
|
|
|
|
|
**작업자:** Claude Code
|
refactor: [docs] 팀별 폴더 구조 재편 (공유/개발/프론트/기획)
- 개발팀 전용 폴더 dev/ 생성 (standards, guides, quickstart, changes, deploys, data, history, dev_plans 이동)
- 프론트엔드 전용 폴더 frontend/ 생성 (api/ → frontend/api-specs/)
- 기획팀 폴더 requests/ 생성
- plans/ → dev/dev_plans/ 이름 변경
- README.md 신규 (사람용 안내), INDEX.md 재작성 (Claude Code용)
- resources.md 신규 (노션 링크용, assets/brochure 이관 예정)
- CURRENT_WORKS.md 삭제, TODO.md → dev/ 이동
- 전체 참조 경로 업데이트
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 16:46:03 +09:00
|
|
|
**관련 계획:** docs/dev_plans/quote-management-url-migration-plan.md (Step 1.3, 1.4)
|
2026-01-26 22:12:17 +09:00
|
|
|
|
|
|
|
|
## 📋 변경 개요
|
|
|
|
|
V2 견적 상세/수정 테스트 페이지(test/[id])에서 Mock 데이터를 실제 API 연동으로 변경
|
|
|
|
|
|
|
|
|
|
## 📁 수정된 파일
|
|
|
|
|
- `react/src/app/[locale]/(protected)/sales/quote-management/test/[id]/page.tsx` - API 연동 구현
|
|
|
|
|
|
|
|
|
|
## 🔧 상세 변경 사항
|
|
|
|
|
|
|
|
|
|
### 1. Import 추가
|
|
|
|
|
```typescript
|
|
|
|
|
import { getQuoteById, updateQuote } from "@/components/quotes/actions";
|
|
|
|
|
import { transformApiToV2, transformV2ToApi } from "@/components/quotes/types";
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 2. MOCK_DATA 제거
|
|
|
|
|
- 65줄의 하드코딩된 테스트 데이터 제거
|
|
|
|
|
|
|
|
|
|
### 3. useEffect 수정 (데이터 로드)
|
|
|
|
|
|
|
|
|
|
**변경 전:**
|
|
|
|
|
```typescript
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const loadQuote = async () => {
|
|
|
|
|
setIsLoading(true);
|
|
|
|
|
try {
|
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 500)); // Mock delay
|
|
|
|
|
setQuote({ ...MOCK_DATA, id: quoteId });
|
|
|
|
|
} catch (error) {
|
|
|
|
|
toast.error("견적 정보를 불러오는데 실패했습니다.");
|
|
|
|
|
router.push("/sales/quote-management");
|
|
|
|
|
} finally {
|
|
|
|
|
setIsLoading(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
loadQuote();
|
|
|
|
|
}, [quoteId, router]);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**변경 후:**
|
|
|
|
|
```typescript
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const loadQuote = async () => {
|
|
|
|
|
setIsLoading(true);
|
|
|
|
|
try {
|
|
|
|
|
const result = await getQuoteById(quoteId);
|
|
|
|
|
|
|
|
|
|
if (!result.success || !result.data) {
|
|
|
|
|
toast.error(result.error || "견적 정보를 불러오는데 실패했습니다.");
|
|
|
|
|
router.push("/sales/quote-management");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// API 응답을 V2 폼 데이터로 변환
|
|
|
|
|
const v2Data = transformApiToV2(result.data);
|
|
|
|
|
setQuote(v2Data);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
toast.error("견적 정보를 불러오는데 실패했습니다.");
|
|
|
|
|
router.push("/sales/quote-management");
|
|
|
|
|
} finally {
|
|
|
|
|
setIsLoading(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (quoteId) {
|
|
|
|
|
loadQuote();
|
|
|
|
|
}
|
|
|
|
|
}, [quoteId, router]);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 4. handleSave 수정 (수정 저장)
|
|
|
|
|
|
|
|
|
|
**변경 전:**
|
|
|
|
|
```typescript
|
|
|
|
|
const handleSave = useCallback(async (data: QuoteFormDataV2, saveType: "temporary" | "final") => {
|
|
|
|
|
setIsSaving(true);
|
|
|
|
|
try {
|
|
|
|
|
console.log("[테스트] 수정 데이터:", data);
|
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 1000)); // Mock delay
|
|
|
|
|
toast.success(`[테스트] ${saveType === "temporary" ? "임시" : "최종"} 저장 완료`);
|
|
|
|
|
if (saveType === "final") {
|
|
|
|
|
router.push(`/sales/quote-management/test/${quoteId}`);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
toast.error("저장 중 오류가 발생했습니다.");
|
|
|
|
|
} finally {
|
|
|
|
|
setIsSaving(false);
|
|
|
|
|
}
|
|
|
|
|
}, [router, quoteId]);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**변경 후:**
|
|
|
|
|
```typescript
|
|
|
|
|
const handleSave = useCallback(async (data: QuoteFormDataV2, saveType: "temporary" | "final") => {
|
|
|
|
|
setIsSaving(true);
|
|
|
|
|
try {
|
|
|
|
|
// V2 폼 데이터를 API 형식으로 변환
|
|
|
|
|
const updatedData = { ...data, status: saveType };
|
|
|
|
|
const apiData = transformV2ToApi(updatedData);
|
|
|
|
|
|
|
|
|
|
// API 호출
|
|
|
|
|
const result = await updateQuote(quoteId, apiData);
|
|
|
|
|
|
|
|
|
|
if (!result.success) {
|
|
|
|
|
toast.error(result.error || "저장 중 오류가 발생했습니다.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
toast.success(`${saveType === "temporary" ? "임시" : "최종"} 저장 완료`);
|
|
|
|
|
|
|
|
|
|
// 저장 후 view 모드로 전환
|
|
|
|
|
router.push(`/sales/quote-management/test/${quoteId}`);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
toast.error("저장 중 오류가 발생했습니다.");
|
|
|
|
|
} finally {
|
|
|
|
|
setIsSaving(false);
|
|
|
|
|
}
|
|
|
|
|
}, [router, quoteId]);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## ✅ Phase 1 완료
|
|
|
|
|
- [x] Step 1.1: V2 데이터 변환 함수 구현
|
|
|
|
|
- [x] Step 1.2: test-new 페이지 API 연동 (createQuote)
|
|
|
|
|
- [x] Step 1.3: test/[id] 상세 페이지 API 연동 (getQuoteById)
|
|
|
|
|
- [x] Step 1.4: test/[id] 수정 API 연동 (updateQuote)
|
|
|
|
|
|
|
|
|
|
## 🔜 다음 작업 (Phase 2)
|
|
|
|
|
- [ ] Step 2.1: test-new → new 경로 변경
|
|
|
|
|
- [ ] Step 2.2: test/[id] → [id] 경로 통합
|
|
|
|
|
- [ ] Step 2.3: 기존 V1 페이지 처리 결정
|
|
|
|
|
|
|
|
|
|
## 🔗 관련 문서
|
refactor: [docs] 팀별 폴더 구조 재편 (공유/개발/프론트/기획)
- 개발팀 전용 폴더 dev/ 생성 (standards, guides, quickstart, changes, deploys, data, history, dev_plans 이동)
- 프론트엔드 전용 폴더 frontend/ 생성 (api/ → frontend/api-specs/)
- 기획팀 폴더 requests/ 생성
- plans/ → dev/dev_plans/ 이름 변경
- README.md 신규 (사람용 안내), INDEX.md 재작성 (Claude Code용)
- resources.md 신규 (노션 링크용, assets/brochure 이관 예정)
- CURRENT_WORKS.md 삭제, TODO.md → dev/ 이동
- 전체 참조 경로 업데이트
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 16:46:03 +09:00
|
|
|
- 계획 문서: `docs/dev_plans/quote-management-url-migration-plan.md`
|
2026-01-26 22:12:17 +09:00
|
|
|
- Step 1.1 변경 내역: `docs/changes/20260126_quote_v2_transform_functions.md`
|
|
|
|
|
- Step 1.2 변경 내역: `docs/changes/20260126_quote_v2_test_new_api.md`
|
|
|
|
|
- V2 컴포넌트: `react/src/components/quotes/QuoteRegistrationV2.tsx`
|