2025-12-23 17:48:09 +09:00
# React 목업 데이터 → API 연동 마이그레이션 계획
> **작성일**: 2025-12-23
> **목적**: React 프론트엔드의 목업 데이터를 실제 API와 연동
> **참고 문서**: `react-api-integration-plan.md`, `erp-api-development-plan-d1.0-changes.md`
> **참조 구현**: 단가관리 (`/sales/pricing-management`)
---
## 1. 개요
### 1.1 현황 분석
2025-12-24 17:33:51 +09:00
**목업 데이터 사용 페이지**: 80개+ 파일에서 목업 데이터 사용 중
2025-12-23 17:48:09 +09:00
```
┌─────────────────────────────────────────────────────────────────┐
│ 🎯 연동 목표 │
├─────────────────────────────────────────────────────────────────┤
│ - React 목업 데이터 → 실제 API 호출로 전환 │
│ - 단가관리(pricing-management) 패턴을 표준으로 적용 │
2025-12-24 17:33:51 +09:00
│ - Phase A~B (API 완료 기능) 우선 연동 │
2025-12-23 17:48:09 +09:00
└─────────────────────────────────────────────────────────────────┘
```
2025-12-24 17:33:51 +09:00
### 1.4 2025-12-24 전수 조사 결과 요약
> MCP Serena를 활용한 Mock 데이터 사용 현황 전수 조사 결과
**🔍 발견된 주요 패턴:**
1. **리스트 API 연동 완료, 상세/CRUD Mock** : 리스트는 API 호출하지만 상세 조회, 등록, 수정, 삭제는 Mock
2. **actions.ts 존재하지만 미연동** : Server Actions 파일은 있으나 UI에서 호출하지 않음 (console.log 사용)
3. **MOCK_XXX 패턴** : 상수 배열로 Mock 데이터 정의 (예: `MOCK_POPUPS` , `mockEmployees` )
4. **generateMockData 함수** : 동적 Mock 데이터 생성 함수 사용
5. **console.log CRUD** : 저장/삭제 시 console.log만 출력하고 실제 API 호출 없음
**📊 현황 요약:**
| 분류 | 개수 | 상태 |
|------|------|------|
| 리스트 API 완료 | 12개 | Phase A-B |
| 상세 페이지 Mock | 10개+ | 연동 필요 |
| CRUD Mock (console.log) | 25개+ | 연동 필요 |
| 전체 Mock | 30개+ | Phase C-H |
| API 미존재 | 7개 | Phase I |
**⚠️ 우선 연동 대상 (actions.ts 존재):**
- A-1 악성채권: `BadDebtDetail.tsx` → `actions.ts` 연동 *(다른 세션에서 진행)*
- A-2 팝업: `[id]/page.tsx` , `[id]/edit/page.tsx` → `actions.ts` 연동
- B-1~B-6: 상세 페이지 및 CRUD → `actions.ts` 연동
### 1.5 작업 진행 정책
> **단위 작업 → 검수 → 승인 → 문서 업데이트 → 커밋** 순서로 진행
```
┌─────────────────────────────────────────────────────────────────┐
│ 📋 작업 흐름 (페이지 단위) │
├─────────────────────────────────────────────────────────────────┤
│ 1️ ⃣ 작업 시작: 대상 페이지 Mock → API 연동 작업 │
│ 2️ ⃣ 작업 완료: 코드 수정 완료 후 사용자에게 검수 요청 │
│ 3️ ⃣ 검수: 사용자가 기능 확인 (브라우저 테스트) │
│ 4️ ⃣ [승인] 문서 업데이트: 이 문서의 상태 갱신 │
│ 5️ ⃣ [승인] 커밋: Git 커밋 생성 │
│ 6️ ⃣ 다음 페이지로 이동 │
└─────────────────────────────────────────────────────────────────┘
```
**⚠️ 중요 규칙:**
- 각 단계에서 `[승인]` 표시된 작업은 **사용자 승인 후** 진행
- 한 번에 하나의 페이지만 작업 (병렬 작업 금지)
- 검수 실패 시 수정 후 재검수
- 커밋 메시지: `feat: [Phase]-[번호] [페이지명] Mock → API 연동`
**📌 현재 진행 순서:**
1. ~~A-1 악성채권~~ *(완료 - 상세+CRUD+메모 API 연동)*
2. ~~A-2 팝업 관리~~ *(완료 - 상세+CRUD API 연동)*
3. A-3 결제 내역 (조회 전용, 완료)
4. A-4 구독 관리 (조회 전용, 완료)
5. A-5 알림 설정 (완료)
6. B-1 매출 관리
7. B-2 매입 관리
8. B-3 입금 관리
9. B-4 출금 관리
10. B-5 거래처 관리
11. B-6 어음 관리
2025-12-23 17:48:09 +09:00
### 1.2 단가관리 연동 패턴 (표준 참조)
```
┌─────────────────────────────────────────────────────────────────┐
│ 📂 파일 구조 │
├─────────────────────────────────────────────────────────────────┤
│ react/src/ │
│ ├── app/[locale]/(protected)/sales/pricing-management/ │
│ │ └── page.tsx ← 서버 컴포넌트 (API 호출) │
│ │ │
│ └── components/pricing/ │
│ ├── types.ts ← 타입 정의 │
│ ├── actions.ts ← Server Actions (CRUD) │
│ ├── PricingListClient.tsx ← 클라이언트 컴포넌트 │
│ └── index.ts ← Export │
└─────────────────────────────────────────────────────────────────┘
```
**데이터 흐름:**
```
page.tsx (서버)
↓ API 호출 (fetch)
↓ 데이터 변환 (API → Frontend)
↓ initialData prop 전달
ListClient.tsx (클라이언트)
↓ useState로 데이터 관리
↓ UI 렌더링
```
### 1.3 핵심 패턴 요약
| 구분 | 파일 | 역할 |
|------|------|------|
| 타입 | `types.ts` | 프론트엔드 인터페이스 정의 |
| 서버 페이지 | `page.tsx` | API 호출, 데이터 병합, 초기 데이터 전달 |
| 서버 액션 | `actions.ts` | CRUD 작업, 데이터 변환 함수 |
| 클라이언트 | `*Client.tsx` | UI 렌더링, 사용자 상호작용 |
---
## 2. 우선순위별 연동 대상
2025-12-24 17:33:51 +09:00
> **상태 범례**:
> - ✅ 완료: API 연동 완료 (Server Actions 호출)
> - 🔄 Mock: Mock 데이터 또는 console.log 사용
> - ⚠️ API 필요: 백엔드 API 개발 선행 필요
> - ⏭️ 건너뜀: API 미존재 또는 해당 없음
> - 📦 actions.ts: Server Actions 파일 존재 (UI 연동 필요)
2025-12-23 17:48:09 +09:00
### 2.1 Phase A: API 완료 기능 (즉시 연동 가능)
> 이미 API가 구현되어 있어 React 연동만 필요
2025-12-24 17:33:51 +09:00
> **⚠️ 2025-12-24 전수 조사 결과**: 리스트 API 연동 완료했으나 상세/CRUD 작업에서 Mock 사용 확인
2025-12-23 17:48:09 +09:00
2025-12-24 17:33:51 +09:00
| # | 페이지 | React 경로 | 리스트 | 상세 조회 | 등록 | 수정 | 삭제 | 비고 |
|---|--------|-----------|--------|----------|------|------|------|------|
| A-1 | 악성채권 관리 | `/accounting/bad-debt-collection` | ✅ | ✅ | ✅ | ✅ | ✅ | 상세+CRUD+메모 API 연동 완료 (2025-12-24) |
| A-2 | 팝업 관리 | `/settings/popup-management` | ✅ | ✅ | ✅ | ✅ | ✅ | 상세+CRUD API 연동 완료 (2025-12-24) |
| A-3 | 결제 내역 | `/settings/payment-history` | ✅ | ⏭️ | ⏭️ | ⏭️ | ⏭️ | 조회 전용 |
| A-4 | 구독 관리 | `/settings/subscription` | ✅ | ⏭️ | ⏭️ | ⏭️ | ⏭️ | 조회 전용 |
| A-5 | 알림 설정 | `/settings/notifications` | ✅ | ⏭️ | ⏭️ | ✅ | ⏭️ | 저장 기능 완료 |
2025-12-23 17:48:09 +09:00
### 2.2 Phase B: 핵심 업무 기능
2025-12-24 17:33:51 +09:00
> **⚠️ 2025-12-24 전수 조사 결과**: actions.ts 존재하나 UI에서 console.log로 Mock 처리
| # | 페이지 | React 경로 | 리스트 | 상세 조회 | 등록 | 수정 | 삭제 | 비고 |
|---|--------|-----------|--------|----------|------|------|------|------|
2025-12-24 18:58:10 +09:00
| B-1 | 매출 관리 | `/accounting/sales` | ✅ | ✅ | ✅ | ✅ | ✅ | API 연동 완료, alert→toast 변환 |
2025-12-24 19:41:33 +09:00
| B-2 | 매입 관리 | `/accounting/purchase` | ✅ | ✅ | ✅ | ✅ | ✅ | API 연동 완료, alert→toast 변환 |
| B-3 | 입금 관리 | `/accounting/deposit` | ✅ | ✅ | ✅ | ✅ | ✅ | API 연동 완료, alert→toast 변환 |
| B-4 | 출금 관리 | `/accounting/withdrawal` | ✅ | ✅ | ✅ | ✅ | ✅ | API 연동 완료, alert→toast 변환 |
| B-5 | 거래처 관리 | `/accounting/vendor` | ✅ | ✅ | ✅ | ✅ | ✅ | API 연동 완료, console.error 제거 |
| B-6 | 어음 관리 | `/accounting/bills` | ✅ | ⏭️ 모달 | ✅ | ✅ | ✅ | API 연동 완료 |
2025-12-23 17:48:09 +09:00
### 2.3 Phase C: 인사/근태
2025-12-24 17:33:51 +09:00
> **2025-12-24 분석 결과**: API 존재, 전체 Mock 사용 중
| # | 페이지 | React 경로 | 리스트 | 상세 조회 | 등록 | 수정 | 삭제 | 비고 |
|---|--------|-----------|--------|----------|------|------|------|------|
2025-12-24 18:00:36 +09:00
| C-1 | 직원 관리 | `/hr/employee-management` | ✅ | ✅ | ✅ | ✅ | ✅ | **2025-12-24 연동 완료** - actions.ts, utils.ts 생성, 전체 CRUD API 연동 |
2025-12-24 18:58:10 +09:00
| C-2 | 근태 관리 | `/hr/attendance` | ✅ | ⏭️ | ✅ | ⏭️ | ⏭️ | **2025-12-24 연동 완료** - actions.ts 생성, check-in/check-out/today API 연동 |
2025-12-24 19:41:33 +09:00
| C-3 | 휴가 관리 | `/hr/vacation-management` | ✅ | ⏭️ | ✅ | ⏭️ | ⏭️ | **2025-12-24 연동 완료** - actions.ts 생성, 신청현황 탭 API 연동 (목록/승인/반려/신청), 사용현황/부여현황 탭 API 연동 |
2025-12-24 18:58:10 +09:00
| C-4 | 부서 관리 | `/hr/departments` | ✅ | ⏭️ | ✅ | ✅ | ✅ | **2025-12-24 연동 완료** - actions.ts 생성, 트리 조회/생성/수정/삭제 API 연동 |
2025-12-24 17:33:51 +09:00
### 2.4 Phase D: 설정/시스템 관리
> **2025-12-24 분석 결과**: 대부분 Mock, console.log로 저장 처리
| # | 페이지 | React 경로 | 리스트 | 상세 조회 | 등록 | 수정 | 삭제 | 비고 |
|---|--------|-----------|--------|----------|------|------|------|------|
2025-12-24 18:58:10 +09:00
| D-1 | 회사 정보 관리 | `/settings/company-info` | ✅ | ⏭️ | ⏭️ | ✅ | ⏭️ | **2025-12-24 연동 완료** - actions.ts 생성, API 조회/수정 연동 |
| D-2 | 계정 관리 | `/settings/accounts` | ✅ | ✅ | ✅ | ✅ | ✅ | **2025-12-24 연동 완료** - actions.ts 생성, CRUD API 연동 |
2025-12-24 19:41:33 +09:00
| D-3 | 근무 일정 관리 | `/settings/work-schedule` | ✅ | ⏭️ | ⏭️ | ✅ | ⏭️ | **2025-12-24 연동 완료** - actions.ts 생성, 조회/수정 API 연동 |
| D-4 | 근태 설정 관리 | `/settings/attendance-settings` | ✅ | ⏭️ | ⏭️ | ✅ | ⏭️ | **2025-12-24 연동 완료** - actions.ts 생성, GPS/허용반경 API 연동 |
2025-12-24 17:33:51 +09:00
### 2.5 Phase E: 인사/급여
> **2025-12-24 분석 결과**: 전체 Mock, CRUD 모두 console.log
| # | 페이지 | React 경로 | 리스트 | 상세 조회 | 등록 | 수정 | 삭제 | 비고 |
|---|--------|-----------|--------|----------|------|------|------|------|
| E-1 | 법인카드 관리 | `/hr/card-management` | 🔄 Mock | 🔄 Mock | 🔄 | 🔄 | 🔄 | `new/[id]/page.tsx` - console.log |
| E-2 | 급여 관리 | `/hr/salary` | 🔄 Mock | ⏭️ | ⏭️ | ⏭️ | ⏭️ | `index.tsx:89,158` mockData |
### 2.6 Phase F: 결재 시스템
2025-12-23 17:48:09 +09:00
2025-12-24 17:33:51 +09:00
> **2025-12-24 분석 결과**: 전체 Mock, 삭제 console.log
2025-12-23 17:48:09 +09:00
2025-12-24 17:33:51 +09:00
| # | 페이지 | React 경로 | 리스트 | 상세 조회 | 등록 | 수정 | 삭제 | 비고 |
|---|--------|-----------|--------|----------|------|------|------|------|
| F-1 | 기안함 | `/approval/draft-box` | 🔄 Mock | ⏭️ | ⏭️ | ⏭️ | 🔄 | `index.tsx:183,383,433` console.log('삭제') |
| F-2 | 참조함 | `/approval/reference-box` | 🔄 Mock | ⏭️ | ⏭️ | ⏭️ | ⏭️ | `index.tsx:115` mockData |
| F-3 | 결재함 | `/approval/approval-box` | 🔄 Mock | ⏭️ | ⏭️ | ⏭️ | ⏭️ | `index.tsx:123` mockData |
| F-4 | 비용견적서 양식 | `/approval/create/expense-estimate` | ⏭️ | ⏭️ | 🔄 | ⏭️ | ⏭️ | `ExpenseEstimateForm.tsx:20` mockData |
2025-12-24 14:19:45 +09:00
2025-12-24 17:33:51 +09:00
### 2.7 Phase G: 생산 관리
2025-12-24 14:19:45 +09:00
2025-12-24 17:33:51 +09:00
> **2025-12-24 분석 결과**: 전체 Mock (mockData.ts 파일 사용)
2025-12-24 14:19:45 +09:00
2025-12-24 17:33:51 +09:00
| # | 페이지 | React 경로 | 리스트 | 상세 조회 | 등록 | 수정 | 삭제 | 비고 |
|---|--------|-----------|--------|----------|------|------|------|------|
| G-1 | 작업지시 관리 | `/production/work-orders` | 🔄 Mock | 🔄 Mock | 🔄 | 🔄 | 🔄 | `mockData.ts` 전체 사용 |
| G-2 | 작업실적 관리 | `/production/work-results` | 🔄 Mock | ⏭️ | 🔄 | 🔄 | ⏭️ | `mockData.ts` 전체 사용 |
| G-3 | 생산 대시보드 | `/production/dashboard` | 🔄 Mock | ⏭️ | ⏭️ | ⏭️ | ⏭️ | `mockData.ts` 전체 사용 |
| G-4 | 작업자 화면 | `/production/worker-screen` | 🔄 Mock | ⏭️ | ⏭️ | ⏭️ | ⏭️ | 전체 Mock |
| G-5 | 품질 검사 | `/quality/inspection` | 🔄 Mock | 🔄 Mock | 🔄 | 🔄 | 🔄 | `mockData.ts` 전체 사용 |
2025-12-24 14:19:45 +09:00
2025-12-24 17:33:51 +09:00
### 2.8 Phase H: 자재/출하 관리
2025-12-24 14:19:45 +09:00
2025-12-24 17:33:51 +09:00
> **2025-12-24 분석 결과**: 전체 Mock (mockData.ts 파일 사용)
2025-12-24 14:19:45 +09:00
2025-12-24 17:33:51 +09:00
| # | 페이지 | React 경로 | 리스트 | 상세 조회 | 등록 | 수정 | 삭제 | 비고 |
|---|--------|-----------|--------|----------|------|------|------|------|
| H-1 | 입고 관리 | `/material/receiving` | 🔄 Mock | 🔄 Mock | 🔄 | 🔄 | 🔄 | `mockData.ts` 전체 사용 |
| H-2 | 재고 현황 | `/material/stock-status` | 🔄 Mock | 🔄 Mock | ⏭️ | ⏭️ | ⏭️ | `mockData.ts` , `StockStatusDetail.tsx:38` |
| H-3 | 출하 관리 | `/outbound/shipment` | 🔄 Mock | 🔄 Mock | 🔄 | 🔄 | 🔄 | `mockData.ts` , `ShipmentDetail.tsx:48` |
2025-12-24 14:19:45 +09:00
2025-12-24 17:33:51 +09:00
### 2.9 Phase I: API 신규 개발 필요 (후순위)
2025-12-24 14:19:45 +09:00
> 현재 API가 존재하지 않아 백엔드 개발 선행 필요
| # | 페이지 | React 경로 | 필요 API | 상태 |
|---|--------|-----------|---------|------|
2025-12-24 17:33:51 +09:00
| I-1 | 미지급비용 관리 | `/accounting/expected-expense` | `GET/POST /v1/expected-expenses` | ⚠️ API 개발 필요 |
| I-2 | 거래처 원장 | `/accounting/vendor-ledger` | `GET /v1/vendor-ledger` | ⚠️ API 개발 필요 |
| I-3 | 카드 거래 조회 | `/accounting/card-transaction` | `GET /v1/card-transactions` | ⚠️ API 개발 필요 |
| I-4 | 은행 거래 조회 | `/accounting/bank-transaction` | `GET /v1/bank-transactions` | ⚠️ API 개발 필요 |
| I-5 | 채권 현황 | `/accounting/receivables-status` | `GET /v1/receivables` | ⚠️ API 개발 필요 |
| I-6 | 일일 보고서 | `/accounting/daily-report` | `GET /v1/daily-reports` | ⚠️ API 개발 필요 |
| I-7 | 종합 분석 보고서 | `/reports/comprehensive-analysis` | `GET /v1/reports/comprehensive` | ⚠️ API 개발 필요 |
2025-12-24 18:58:10 +09:00
| I-8 | 휴가 정책 관리 | `/settings/leave-policy` | `GET/PUT /v1/settings/leave-policy` | ⚠️ API 개발 필요 |
2025-12-24 17:33:51 +09:00
### 2.10 상세 페이지 Mock 현황
> 리스트 페이지 API 연동 완료 후 상세 페이지 연동 필요
| # | 컴포넌트 | 파일 위치 | Mock 사용 라인 | 부모 기능 |
|---|----------|----------|---------------|----------|
| S-1 | SalesDetail | `SalesManagement/SalesDetail.tsx` | L56-63 (MOCK_VENDORS), L77-110 (fetchSalesDetail) | B-1 매출 관리 |
| S-2 | PurchaseDetail | `PurchaseManagement/PurchaseDetail.tsx` | L49-56 (Mock 계좌/거래처), L65-126 (Mock 데이터) | B-2 매입 관리 |
| S-3 | DepositDetail | `DepositManagement/DepositDetail.tsx` | L52 (Mock 데이터 로드) | B-3 입금 관리 |
| S-4 | WithdrawalDetail | `WithdrawalManagement/WithdrawalDetail.tsx` | L59 (Mock 데이터 로드) | B-4 출금 관리 |
| S-5 | VendorDetail | `VendorManagement/VendorDetail.tsx` | L61 (Mock 데이터 가져오기) | B-5 거래처 관리 |
| S-6 | BadDebtDetail | `BadDebtCollection/BadDebtDetail.tsx` | L49-57 (Mock 담당자/데이터) | A-1 악성채권 관리 |
| S-7 | StockStatusDetail | `StockStatus/StockStatusDetail.tsx` | L38 (Mock 상세 정보) | H-2 재고 현황 |
| S-8 | ShipmentDetail | `ShipmentManagement/ShipmentDetail.tsx` | L48 (Mock 데이터) | H-3 출하 관리 |
| S-9 | ReceivingDetail | `ReceivingManagement/ReceivingDetail.tsx` | Mock 전체 | H-1 입고 관리 |
| S-10 | WorkOrderDetail | `WorkOrders/WorkOrderDetail.tsx` | Mock 전체 | G-1 작업지시 관리 |
2025-12-23 17:48:09 +09:00
---
## 3. 연동 작업 가이드
### 3.1 표준 연동 절차
```
Step 1: 현재 상태 분석
├── 목업 데이터 구조 확인 (types.ts)
├── 클라이언트 컴포넌트 props 확인 (*Client.tsx)
└── API 스펙 확인 (Swagger: sam.kr/api-docs)
Step 2: 타입 정의 정비
├── API 응답 타입 추가 (xxxApiData)
├── 변환 함수 타입 정의
└── 기존 프론트엔드 타입 유지
Step 3: 서버 컴포넌트 수정 (page.tsx)
├── API 호출 함수 구현
├── 데이터 변환 함수 구현
└── initialData prop 전달
Step 4: 서버 액션 구현 (actions.ts)
├── CRUD 함수 구현 (create, update, delete)
├── transformApiToFrontend() 함수
└── transformFrontendToApi() 함수
Step 5: 클라이언트 컴포넌트 연동
├── Server Actions import
├── 핸들러 함수에서 Server Actions 호출
└── 로딩/에러 상태 처리
```
### 3.2 체크리스트
```
□ API 엔드포인트 확인 (Swagger)
□ 응답 데이터 구조 확인
□ 타입 정의 (API 응답 타입 + 프론트엔드 타입)
□ 변환 함수 구현 (API ↔ Frontend)
□ 서버 컴포넌트에서 API 호출
□ 클라이언트 컴포넌트에 initialData 전달
□ Server Actions 구현 (CRUD)
□ 에러 처리 (try-catch, 사용자 알림)
□ 로딩 상태 처리
□ 브라우저 테스트
```
---
## 4. 상세 구현 가이드
### 4.1 page.tsx 패턴
```typescript
// app/[locale]/(protected)/[feature]/page.tsx
import { cookies } from 'next/headers';
import { FeatureListClient } from '@/components/feature ';
// API 응답 타입
interface ApiData {
id: number;
// ... API 필드
}
// API 헤더 생성
async function getApiHeaders(): Promise< HeadersInit > {
const cookieStore = await cookies();
const token = cookieStore.get('access_token')?.value;
return {
'Accept': 'application/json',
'Authorization': token ? `Bearer ${token}` : '',
'X-API-KEY': process.env.NEXT_PUBLIC_API_KEY || '',
};
}
// API 호출
async function getList(): Promise< ApiData [ ] > {
try {
const headers = await getApiHeaders();
const response = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/v1/feature` ,
{ method: 'GET', headers, cache: 'no-store' }
);
if (!response.ok) return [];
const result = await response.json();
return result.success ? result.data.data : [];
} catch (error) {
console.error('[FeaturePage] Fetch error:', error);
return [];
}
}
// 데이터 변환
function transformToFrontend(apiData: ApiData[]): FeatureListItem[] {
return apiData.map(item => ({
id: String(item.id),
// ... 필드 매핑
}));
}
// 페이지 컴포넌트
export default async function FeaturePage() {
const apiData = await getList();
const data = transformToFrontend(apiData);
return < FeatureListClient initialData = {data} / > ;
}
```
### 4.2 actions.ts 패턴
```typescript
// components/feature/actions.ts
'use server';
import { cookies } from 'next/headers';
import type { FeatureData } from './types';
async function getApiHeaders(): Promise< HeadersInit > {
const cookieStore = await cookies();
const token = cookieStore.get('access_token')?.value;
return {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': token ? `Bearer ${token}` : '',
'X-API-KEY': process.env.API_KEY || '',
};
}
// API → Frontend 변환
function transformApiToFrontend(apiData: ApiData): FeatureData {
return {
id: String(apiData.id),
// ... 필드 매핑
};
}
// Frontend → API 변환
function transformFrontendToApi(data: FeatureData): Record< string , unknown > {
return {
// ... 필드 매핑 (snake_case)
};
}
// 등록
export async function createFeature(
data: FeatureData
): Promise< { success: boolean; data?: FeatureData; error?: string }> {
try {
const headers = await getApiHeaders();
const apiData = transformFrontendToApi(data);
const response = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/v1/feature` ,
{
method: 'POST',
headers,
body: JSON.stringify(apiData),
}
);
const result = await response.json();
if (!response.ok || !result.success) {
return { success: false, error: result.message };
}
return { success: true, data: transformApiToFrontend(result.data) };
} catch (error) {
return { success: false, error: '서버 오류가 발생했습니다.' };
}
}
// 수정
export async function updateFeature(
id: string,
data: FeatureData
): Promise< { success: boolean; data?: FeatureData; error?: string }> {
// ... 유사 패턴
}
// 삭제
export async function deleteFeature(
id: string
): Promise< { success: boolean; error?: string }> {
// ... 유사 패턴
}
```
### 4.3 ListClient.tsx 연동 패턴
```typescript
// components/feature/FeatureListClient.tsx
'use client';
import { useState } from 'react';
import { createFeature, updateFeature, deleteFeature } from './actions';
import { toast } from 'sonner';
interface FeatureListClientProps {
initialData: FeatureListItem[];
}
export function FeatureListClient({ initialData }: FeatureListClientProps) {
const [data, setData] = useState< FeatureListItem [ ] > (initialData);
const [isLoading, setIsLoading] = useState(false);
const handleCreate = async (formData: FeatureData) => {
setIsLoading(true);
const result = await createFeature(formData);
if (result.success & & result.data) {
setData(prev => [...prev, result.data!]);
toast.success('등록되었습니다.');
} else {
toast.error(result.error || '등록에 실패했습니다.');
}
setIsLoading(false);
};
const handleUpdate = async (id: string, formData: FeatureData) => {
setIsLoading(true);
const result = await updateFeature(id, formData);
if (result.success & & result.data) {
setData(prev => prev.map(item =>
item.id === id ? result.data! : item
));
toast.success('수정되었습니다.');
} else {
toast.error(result.error || '수정에 실패했습니다.');
}
setIsLoading(false);
};
const handleDelete = async (id: string) => {
setIsLoading(true);
const result = await deleteFeature(id);
if (result.success) {
setData(prev => prev.filter(item => item.id !== id));
toast.success('삭제되었습니다.');
} else {
toast.error(result.error || '삭제에 실패했습니다.');
}
setIsLoading(false);
};
// ... 나머지 UI 렌더링
}
```
---
## 5. 필드 매핑 규칙
### 5.1 네이밍 컨벤션
| API (snake_case) | Frontend (camelCase) |
|------------------|---------------------|
| `created_at` | `createdAt` |
| `updated_at` | `updatedAt` |
| `item_type` | `itemType` |
| `purchase_price` | `purchasePrice` |
| `sales_price` | `salesPrice` |
| `is_active` | `isActive` |
### 5.2 타입 변환
| API 타입 | Frontend 타입 | 변환 |
|---------|--------------|------|
| `number` (id) | `string` | `String(id)` |
| `string` (decimal) | `number` | `parseFloat(value)` |
| `string` (date) | `string` | 그대로 또는 포맷팅 |
| `null` | `undefined` | `value ?? undefined` |
### 5.3 상태 매핑 예시
```typescript
// API 상태 → Frontend 상태
function mapStatus(apiStatus: string): FrontendStatus {
switch (apiStatus) {
case 'draft': return 'draft';
case 'active': return 'active';
case 'finalized': return 'finalized';
default: return 'draft';
}
}
```
---
## 6. Phase B 상세 필드 매핑
> 이 섹션은 신규 세션에서 바로 개발 가능하도록 상세 스펙을 포함합니다.
### 6.1 매출 관리 (Sales)
**API FormRequest 필드** (`api/app/Http/Requests/V1/Sale/StoreSaleRequest.php` ):
```php
'sale_date' => ['required', 'date'],
'client_id' => ['required', 'integer', 'exists:clients,id'],
'supply_amount' => ['required', 'numeric', 'min:0'],
'tax_amount' => ['required', 'numeric', 'min:0'],
'total_amount' => ['required', 'numeric', 'min:0'],
'description' => ['nullable', 'string', 'max:1000'],
'deposit_id' => ['nullable', 'integer', 'exists:deposits,id'],
```
**React 인터페이스** (`react/src/components/accounting/SalesManagement/types.ts` ):
```typescript
interface SalesRecord {
id: string;
salesNo: string; // 매출번호
salesDate: string; // 매출일
vendorId: string; // 거래처 ID
vendorName: string; // 거래처명
salesType: SalesType; // 매출 유형
accountSubject: string; // 계정과목
items: SalesItem[]; // 품목 목록
totalSupplyAmount: number; // 공급가액 합계
totalVat: number; // 부가세 합계
totalAmount: number; // 총 금액
receivedAmount: number; // 입금액
outstandingAmount: number; // 미수금액
taxInvoiceIssued: boolean; // 세금계산서 발행 여부
transactionStatementIssued: boolean; // 거래명세서 발행 여부
note: string; // 비고
status: SalesStatus; // 상태
createdAt: string;
updatedAt: string;
}
```
**필드 매핑 테이블**:
| API 필드 (snake_case) | React 필드 (camelCase) | 타입 변환 | 비고 |
|----------------------|----------------------|----------|------|
| `id` | `id` | `String(id)` | - |
| `sale_number` | `salesNo` | 그대로 | 시스템 자동 생성 |
| `sale_date` | `salesDate` | 그대로 | YYYY-MM-DD |
| `client_id` | `vendorId` | `String(client_id)` | FK |
| `client.name` | `vendorName` | 그대로 | 관계 조회 |
| `supply_amount` | `totalSupplyAmount` | `parseFloat()` | - |
| `tax_amount` | `totalVat` | `parseFloat()` | - |
| `total_amount` | `totalAmount` | `parseFloat()` | - |
| `description` | `note` | `?? ''` | - |
| `status` | `status` | 매핑 필요 | API: draft/confirmed/invoiced |
| `deposit_id` | `receivedAmount` | 관계 조회 | deposit.amount |
| `created_at` | `createdAt` | 그대로 | - |
| `updated_at` | `updatedAt` | 그대로 | - |
**상태 매핑**:
```typescript
// API → React
const SALES_STATUS_MAP = {
'draft': 'outstanding', // 미수
'confirmed': 'monthlyClose', // 당월마감
'invoiced': 'agreed', // 합의
};
```
---
### 6.2 매입 관리 (Purchases)
**API FormRequest 필드** (`api/app/Http/Requests/V1/Purchase/StorePurchaseRequest.php` ):
```php
'purchase_date' => ['required', 'date'],
'client_id' => ['required', 'integer', 'exists:clients,id'],
'supply_amount' => ['required', 'numeric', 'min:0'],
'tax_amount' => ['required', 'numeric', 'min:0'],
'total_amount' => ['required', 'numeric', 'min:0'],
'description' => ['nullable', 'string', 'max:1000'],
'withdrawal_id' => ['nullable', 'integer', 'exists:withdrawals,id'],
```
**React 인터페이스** (`react/src/components/accounting/PurchaseManagement/types.ts` ):
```typescript
interface PurchaseRecord {
id: string;
purchaseNo: string; // 매입번호
purchaseDate: string; // 매입일자
vendorId: string; // 거래처 ID
vendorName: string; // 거래처명
supplyAmount: number; // 공급가액
vat: number; // 부가세
totalAmount: number; // 합계금액
purchaseType: PurchaseType; // 매입유형
evidenceType: EvidenceType; // 증빙유형
status: PurchaseStatus; // 상태
items: PurchaseItem[]; // 품목 정보
taxInvoiceReceived: boolean; // 세금계산서 수취 여부
createdAt: string;
updatedAt: string;
}
```
**필드 매핑 테이블**:
| API 필드 (snake_case) | React 필드 (camelCase) | 타입 변환 | 비고 |
|----------------------|----------------------|----------|------|
| `id` | `id` | `String(id)` | - |
| `purchase_number` | `purchaseNo` | 그대로 | 시스템 자동 생성 |
| `purchase_date` | `purchaseDate` | 그대로 | YYYY-MM-DD |
| `client_id` | `vendorId` | `String(client_id)` | FK |
| `client.name` | `vendorName` | 그대로 | 관계 조회 |
| `supply_amount` | `supplyAmount` | `parseFloat()` | - |
| `tax_amount` | `vat` | `parseFloat()` | - |
| `total_amount` | `totalAmount` | `parseFloat()` | - |
| `description` | `note` | `?? ''` | 품목 적요용 |
| `status` | `status` | 그대로 | pending/completed/cancelled |
| `withdrawal_id` | - | 관계 조회 | 출금 연결 |
| `created_at` | `createdAt` | 그대로 | - |
| `updated_at` | `updatedAt` | 그대로 | - |
**매입유형 (purchaseType) 옵션**:
- `raw_material` : 원재료매입
- `subsidiary_material` : 부재료매입
- `product` : 상품매입
- `outsourcing` : 외주가공비
- `consumables` : 소모품비
- 기타 15종 (types.ts 참조)
---
### 6.3 입금 관리 (Deposits)
**API FormRequest 필드** (`api/app/Http/Requests/V1/Deposit/StoreDepositRequest.php` ):
```php
'deposit_date' => ['required', 'date'],
'client_id' => ['nullable', 'integer', 'exists:clients,id'],
'client_name' => ['nullable', 'string', 'max:100'],
'bank_account_id' => ['nullable', 'integer', 'exists:bank_accounts,id'],
'amount' => ['required', 'numeric', 'min:0'],
'payment_method' => ['required', 'string', 'in:cash,transfer,card,check'],
'account_code' => ['nullable', 'string', 'max:20'],
'description' => ['nullable', 'string', 'max:1000'],
'reference_type' => ['nullable', 'string', 'max:50'],
'reference_id' => ['nullable', 'integer'],
```
**React 인터페이스** (`react/src/components/accounting/DepositManagement/types.ts` ):
```typescript
interface DepositRecord {
id: string;
depositDate: string; // 입금일
depositAmount: number; // 입금액
accountName: string; // 입금계좌명
depositorName: string; // 입금자명
note: string; // 적요
depositType: DepositType; // 입금유형
vendorId: string; // 거래처 ID
vendorName: string; // 거래처명
status: DepositStatus; // 상태
createdAt: string;
updatedAt: string;
}
```
**필드 매핑 테이블**:
| API 필드 (snake_case) | React 필드 (camelCase) | 타입 변환 | 비고 |
|----------------------|----------------------|----------|------|
| `id` | `id` | `String(id)` | - |
| `deposit_date` | `depositDate` | 그대로 | YYYY-MM-DD |
| `amount` | `depositAmount` | `parseFloat()` | - |
| `bank_account.name` | `accountName` | 그대로 | 관계 조회 |
| `client_name` | `depositorName` | 그대로 | 입금자명 |
| `description` | `note` | `?? ''` | 적요 |
| `account_code` | `depositType` | 매핑 필요 | 유형 코드 |
| `client_id` | `vendorId` | `String(client_id)` | FK |
| `client.name` | `vendorName` | 그대로 | 관계 조회 |
| `payment_method` | - | 참조 | cash/transfer/card/check |
| `created_at` | `createdAt` | 그대로 | - |
| `updated_at` | `updatedAt` | 그대로 | - |
**입금유형 (depositType) 옵션**:
- `salesRevenue` : 매출대금
- `advance` : 선수금
- `suspense` : 가수금
- `rentalIncome` : 임대수익
- `interestIncome` : 이자수익
- 기타 6종 (types.ts 참조)
**입금상태 (status) 옵션**:
- `inputWaiting` : 입력대기
- `requesting` : 신청중
- `rejected` : 반려
- `pending` : 보류
- `incomplete` : 미완
- `error` : 오류
- `confirmed` : 확정완료
---
### 6.4 출금 관리 (Withdrawals)
**API FormRequest 필드** (`api/app/Http/Requests/V1/Withdrawal/StoreWithdrawalRequest.php` ):
```php
'withdrawal_date' => ['required', 'date'],
'client_id' => ['nullable', 'integer', 'exists:clients,id'],
'client_name' => ['nullable', 'string', 'max:100'],
'bank_account_id' => ['nullable', 'integer', 'exists:bank_accounts,id'],
'amount' => ['required', 'numeric', 'min:0'],
'payment_method' => ['required', 'string', 'in:cash,transfer,card,check'],
'account_code' => ['nullable', 'string', 'max:20'],
'description' => ['nullable', 'string', 'max:1000'],
'reference_type' => ['nullable', 'string', 'max:50'],
'reference_id' => ['nullable', 'integer'],
```
**React 인터페이스** (`react/src/components/accounting/WithdrawalManagement/types.ts` ):
```typescript
interface WithdrawalRecord {
id: string;
withdrawalDate: string; // 출금일
withdrawalAmount: number; // 출금액
accountName: string; // 출금계좌명
recipientName: string; // 수취인명
note: string; // 적요
withdrawalType: WithdrawalType; // 출금유형
vendorId: string; // 거래처 ID
vendorName: string; // 거래처명
createdAt: string;
updatedAt: string;
}
```
**필드 매핑 테이블**:
| API 필드 (snake_case) | React 필드 (camelCase) | 타입 변환 | 비고 |
|----------------------|----------------------|----------|------|
| `id` | `id` | `String(id)` | - |
| `withdrawal_date` | `withdrawalDate` | 그대로 | YYYY-MM-DD |
| `amount` | `withdrawalAmount` | `parseFloat()` | - |
| `bank_account.name` | `accountName` | 그대로 | 관계 조회 |
| `client_name` | `recipientName` | 그대로 | 수취인명 |
| `description` | `note` | `?? ''` | 적요 |
| `account_code` | `withdrawalType` | 매핑 필요 | 유형 코드 |
| `client_id` | `vendorId` | `String(client_id)` | FK |
| `client.name` | `vendorName` | 그대로 | 관계 조회 |
| `payment_method` | - | 참조 | cash/transfer/card/check |
| `created_at` | `createdAt` | 그대로 | - |
| `updated_at` | `updatedAt` | 그대로 | - |
**출금유형 (withdrawalType) 옵션**:
- `purchasePayment` : 매입대금
- `advance` : 선급금
- `suspense` : 가지급금
- `rent` : 임대료
- `salary` : 급여
- 기타 11종 (types.ts 참조)
---
### 6.5 거래처 관리 (Clients)
**API FormRequest 필드** (`api/app/Http/Requests/Client/ClientStoreRequest.php` ):
```php
'client_group_id' => 'nullable|integer',
'client_code' => 'nullable|string|max:50',
'name' => 'required|string|max:100',
'client_type' => ['nullable', Rule::exists('common_codes', 'code')...],
'contact_person' => 'nullable|string|max:100',
'phone' => 'nullable|string|max:20',
'mobile' => 'nullable|string|max:20',
'fax' => 'nullable|string|max:20',
'email' => 'nullable|email|max:100',
'address' => 'nullable|string|max:255',
'manager_name' => 'nullable|string|max:50',
'manager_tel' => 'nullable|string|max:20',
'system_manager' => 'nullable|string|max:50',
'account_id' => 'nullable|string|max:50',
'account_password' => 'nullable|string|max:255',
'purchase_payment_day' => 'nullable|string|max:20',
'sales_payment_day' => 'nullable|string|max:20',
'business_no' => 'nullable|string|max:20',
'business_type' => 'nullable|string|max:50',
'business_item' => 'nullable|string|max:100',
'tax_agreement' => 'nullable|boolean',
'tax_amount' => 'nullable|numeric|min:0',
'tax_start_date' => 'nullable|date',
'tax_end_date' => 'nullable|date',
'bad_debt' => 'nullable|boolean',
'bad_debt_amount' => 'nullable|numeric|min:0',
'bad_debt_receive_date' => 'nullable|date',
'bad_debt_end_date' => 'nullable|date',
'bad_debt_progress' => ['nullable', Rule::exists('common_codes', 'code')...],
'memo' => 'nullable|string',
'is_active' => 'nullable|boolean',
```
**필드 매핑 테이블 (주요 필드)**:
| API 필드 (snake_case) | React 필드 (camelCase) | 타입 변환 | 비고 |
|----------------------|----------------------|----------|------|
| `id` | `id` | `String(id)` | - |
| `client_code` | `clientCode` | 그대로 | 자동 생성 |
| `name` | `name` | 그대로 | 거래처명 |
| `client_type` | `clientType` | common_codes 참조 | - |
| `contact_person` | `contactPerson` | 그대로 | 담당자 |
| `phone` | `phone` | 그대로 | 전화번호 |
| `mobile` | `mobile` | 그대로 | 휴대폰 |
| `email` | `email` | 그대로 | 이메일 |
| `address` | `address` | 그대로 | 주소 |
| `business_no` | `businessNo` | 그대로 | 사업자번호 |
| `business_type` | `businessType` | 그대로 | 업종 |
| `business_item` | `businessItem` | 그대로 | 업태 |
| `bad_debt` | `badDebt` | `Boolean` | 악성채권 여부 |
| `bad_debt_amount` | `badDebtAmount` | `parseFloat()` | 악성채권 금액 |
| `is_active` | `isActive` | `Boolean` | 활성 상태 |
| `created_at` | `createdAt` | 그대로 | - |
| `updated_at` | `updatedAt` | 그대로 | - |
---
### 6.6 공통 변환 함수 템플릿
```typescript
// utils/apiTransform.ts
/** snake_case를 camelCase로 변환 */
export function snakeToCamel(str: string): string {
return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
}
/** camelCase를 snake_case로 변환 */
export function camelToSnake(str: string): string {
return str.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}` );
}
/** 객체 전체 키 변환 */
export function transformKeys< T > (obj: Record< string , any > , transformer: (key: string) => string): T {
const result: Record< string , any > = {};
for (const [key, value] of Object.entries(obj)) {
const newKey = transformer(key);
result[newKey] = value !== null ? value : undefined;
}
return result as T;
}
/** API → Frontend 공통 변환 */
export function transformApiToFrontend< T > (apiData: Record< string , any > ): T {
return transformKeys< T > (apiData, snakeToCamel);
}
/** Frontend → API 공통 변환 */
export function transformFrontendToApi(data: Record< string , any > ): Record< string , any > {
return transformKeys(data, camelToSnake);
}
```
---
## 7. 작업 일정
### Phase A: API 완료 기능 (1주)
| 일차 | 작업 내용 | 상태 |
|------|----------|------|
| Day 1 | A-1 악성채권 관리 연동 | ✅ 완료 (2025-12-23) |
2025-12-24 08:54:52 +09:00
| Day 2 | A-2 팝업 관리 연동 | ✅ 완료 (2025-12-23) |
| Day 3 | A-3 결제 내역 연동 | ✅ 완료 (2025-12-23) |
| Day 4 | A-4 구독 관리 연동 | ✅ 완료 (2025-12-23) |
| Day 5 | A-5 알림 설정 연동 | ✅ 완료 (2025-12-23) |
2025-12-23 17:48:09 +09:00
### Phase B: 핵심 업무 기능 (2주)
| 일차 | 작업 내용 |
|------|----------|
| Day 1-2 | B-1 매출 관리 연동 |
| Day 3-4 | B-2 매입 관리 연동 |
| Day 5-6 | B-3 입금 관리, B-4 출금 관리 연동 |
| Day 7-8 | B-5 거래처 관리 연동 |
| Day 9-10 | B-6 어음 관리 연동 + 통합 테스트 |
---
## 8. 변경 이력
| 날짜 | 내용 | 작성자 |
|------|------|--------|
| 2025-12-23 | 문서 초안 작성 | Claude |
| 2025-12-23 | Phase B 상세 필드 매핑 추가 (6.1~6.6) | Claude |
| 2025-12-23 | A-1 악성채권 관리 API 연동 완료 (`actions.ts` , `page.tsx` , `index.tsx` ) | Claude |
2025-12-24 08:54:52 +09:00
| 2025-12-23 | A-2 팝업 관리 API 연동 완료 (`actions.ts` , `page.tsx` , `PopupList.tsx` , `[id]/page.tsx` , `[id]/edit/page.tsx` ) | Claude |
| 2025-12-23 | A-3 결제 내역 API 연동 완료 (`types.ts` , `actions.ts` , `page.tsx` , `PaymentHistoryClient.tsx` , `index.ts` ) | Claude |
| 2025-12-23 | A-4 구독 관리 API 연동 완료 (`types.ts` , `utils.ts` , `actions.ts` , `page.tsx` , `SubscriptionClient.tsx` , `index.ts` ) | Claude |
| 2025-12-23 | A-5 알림 설정 API 연동 완료 (`types.ts` , `actions.ts` , `page.tsx` , `NotificationSettingsClient.tsx` , `index.ts` ) | Claude |
| 2025-12-23 | A-6 거래처 원장 - API 미존재 확인, Phase A 완료 | Claude |
| 2025-12-23 | B-1 매출 관리 - 기존 API 연동 확인 (`/api/proxy/sales` 사용) | Claude |
| 2025-12-23 | B-2 매입 관리 - 기존 API 연동 확인 (`/api/proxy/purchases` 사용) | Claude |
| 2025-12-23 | B-3 입금 관리 API 연동 완료 (`types.ts` : API 타입 추가, `index.tsx` : Mock → API 호출 전환) | Claude |
| 2025-12-23 | B-4 출금 관리 API 연동 완료 (`types.ts` : API 타입 추가, `index.tsx` : Mock → API 호출 전환) | Claude |
| 2025-12-23 | B-5 거래처 관리 API 연동 완료 (`types.ts` : API 타입 추가, `actions.ts` : Server Actions, `page.tsx` : 서버 컴포넌트, `VendorManagementClient.tsx` : 클라이언트 컴포넌트) | Claude |
2025-12-24 14:19:45 +09:00
| 2025-12-24 | 커밋 유실로 인한 API 연동 복원 (A-2~A-5, B-3~B-5: `actions.ts` 재생성, `page.tsx` 서버 컴포넌트 전환, `index.tsx` mock 제거) | Claude |
| 2025-12-24 | Mock 데이터 사용 파일 전수 조사 (MCP Serena 활용), 신규 Phase 추가 (D~H, S) | Claude |
| 2025-12-24 | B-1 매출관리, B-2 매입관리 상태 수정 (Mock 사용중으로 재분류) | Claude |
| 2025-12-24 | Phase E (설정/시스템), F (인사/급여), G (결재), H (API 개발 필요), S (상세 페이지) 신규 추가 | Claude |
2025-12-24 17:33:51 +09:00
| 2025-12-24 | **문서 전면 수정** : 리스트/상세 분리 상태 표기, 상세 페이지 Mock 현황 섹션 추가, Phase 재구성 (A~I), 게시판 제외 | Claude |
| 2025-12-24 | **전수 조사 및 문서 업데이트** : MCP Serena로 80개+ 파일 Mock 패턴 분석, Phase A~H CRUD별 상태 테이블 추가, 전수 조사 결과 요약(1.4) 추가, console.log 패턴 및 actions.ts 미연동 현황 문서화 | Claude |
| 2025-12-24 | **작업 진행 정책 추가 (1.5)** : 페이지 단위 작업 → 검수 → [승인] 문서 업데이트 → [승인] 커밋 순서 정의, A-1 다른 세션 처리, A-2부터 순차 진행 | Claude |
| 2025-12-24 | **A-1 악성채권 관리 완전 연동** : 상세 페이지(`[id]/page.tsx` , `[id]/edit/page.tsx` ) 서버 컴포넌트 전환, `BadDebtDetail.tsx` CRUD API 연동, 메모 API 연동(`addBadDebtMemo` , `deleteBadDebtMemo` ) | Claude |
| 2025-12-24 | **A-2 팝업 관리 API 연동** : `[id]/page.tsx` getPopupById+deletePopup 연동, `[id]/edit/page.tsx` getPopupById 연동, `PopupForm.tsx` createPopup+updatePopup 연동, 로딩/에러 상태 처리 | Claude |
2025-12-24 18:00:36 +09:00
| 2025-12-24 | **C-1 직원 관리 API 연동** : `actions.ts` 생성 (CRUD+통계+일괄삭제), `utils.ts` 생성 (API↔Frontend 변환), `index.tsx` Mock 제거+API 연동, `[id]/page.tsx` 상세 API 연동, `[id]/edit/page.tsx` 수정 API 연동, `new/page.tsx` 등록 API 연동 | Claude |
2025-12-24 18:58:10 +09:00
| 2025-12-24 | **C-2 근태 관리 API 연동** : `actions.ts` 생성 (checkIn/checkOut/getTodayAttendance), `GoogleMap.tsx` userLocation 콜백 추가, `page.tsx` Mock console.log 제거+API 연동, 처리중 상태 및 버튼 텍스트 추가 | Claude |
| 2025-12-24 | **C-3 휴가 관리 API 연동** : `actions.ts` 생성 (getLeaves/createLeave/approveLeave/rejectLeave 등), `index.tsx` 신청현황 탭 Mock 제거+API 연동, 일괄승인/반려 API 연동 | Claude |
| 2025-12-24 | **C-4 부서 관리 API 연동** : `actions.ts` 생성 (getDepartmentTree/createDepartment/updateDepartment/deleteDepartment/deleteDepartmentsMany), `index.tsx` Mock 제거+API 연동 | Claude |
| 2025-12-24 | ** `.env.local` 환경변수 수정**: `API_URL=https://api.sam.kr/api` 추가 - Server Actions 서버사이드 API URL | Claude |
| 2025-12-24 | **D-2 계정 관리(계좌) API 연동** : `actions.ts` 생성 (CRUD+toggle+setPrimary+일괄삭제), `types.ts` API 필드 추가 (id:number, bankName, isPrimary, assignedUserId), `index.tsx` generateMockData 제거+API 연동, `AccountDetail.tsx` console.log 제거+API 연동 | Claude |
2025-12-24 19:41:33 +09:00
| 2025-12-24 | **D-3 근무 일정 관리 API 연동** : `actions.ts` 생성 (getWorkSetting/updateWorkSetting), `index.tsx` console.log 제거+API 연동, 로딩/저장 상태 추가, HH:mm↔HH:mm:ss 시간 변환 | Claude |
| 2025-12-24 | **D-4 근태 설정 관리 API 연동** : `actions.ts` 생성 (getAttendanceSetting/updateAttendanceSetting), `index.tsx` console.log 제거+API 연동, 로딩/저장 상태 추가 (API 지원 필드: useGps, allowedRadius만 연동) | Claude |
| 2025-12-24 | **B-6 어음 관리 중복번호 검증 추가** : `StoreBillRequest.php` tenant scope unique 검증 규칙 추가 (`Rule::unique` ), `lang/ko/error.php` 에러 메시지 추가 (`bill.duplicate_number` ), SAM 표준 패턴 적용 (`app('tenant_id')` ) | Claude |
2025-12-23 17:48:09 +09:00
---
## 9. 참고 문서
- **API 스펙**: http://sam.kr/api-docs
- **기존 연동 계획**: `react-api-integration-plan.md`
- **API 개발 계획**: `erp-api-development-plan-d1.0-changes.md`
- **표준 구현 참조**: `/sales/pricing-management`