- BOMItem Omit 타입 시그니처 통일 (useTemplateManagement, SectionsTab, ItemMasterContext) - HeadersInit → Record<string, string> 타입 변경 - Zustand useShallow 마이그레이션 (zustand/react/shallow) - DataTable, ListPageTemplate 제네릭 타입 제약 추가 - 설정 관리 페이지 추가 (직급, 직책, 휴가정책, 근무일정, 권한) - HR 관리 페이지 추가 (급여, 휴가) - 단가관리 페이지 리팩토링 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
390 lines
13 KiB
Markdown
390 lines
13 KiB
Markdown
# 거래처 관리 API 분석
|
|
|
|
> **작성일**: 2025-12-04
|
|
> **최종 수정**: 2025-12-09
|
|
> **목적**: sam-api 백엔드 Client API와 프론트엔드 거래처 관리 페이지 간 연동 분석
|
|
|
|
---
|
|
|
|
## 1. 현재 상태 요약
|
|
|
|
### 프론트엔드 (sam-react-prod)
|
|
- **파일**: `src/app/[locale]/(protected)/sales/client-management-sales-admin/page.tsx`
|
|
- **상태**: ✅ **API 연동 완료** (2025-12-09)
|
|
- **Hook**: `src/hooks/useClientList.ts` - 2차 필드 모두 지원
|
|
|
|
### 백엔드 (sam-api)
|
|
- **컨트롤러**: `app/Http/Controllers/Api/V1/ClientController.php`
|
|
- **서비스**: `app/Services/ClientService.php`
|
|
- **모델**: `app/Models/Orders/Client.php`
|
|
- **상태**: ✅ **API 구현 완료** - 2차 필드 포함, is_active Boolean 변경 완료
|
|
|
|
---
|
|
|
|
## 2. 백엔드 API 명세
|
|
|
|
### 2.1 Client (거래처) API
|
|
|
|
| Method | Endpoint | 설명 | 인증 |
|
|
|--------|----------|------|------|
|
|
| `GET` | `/api/v1/clients` | 목록 조회 (페이지네이션, 검색) | ✅ Required |
|
|
| `GET` | `/api/v1/clients/{id}` | 단건 조회 | ✅ Required |
|
|
| `POST` | `/api/v1/clients` | 생성 | ✅ Required |
|
|
| `PUT` | `/api/v1/clients/{id}` | 수정 | ✅ Required |
|
|
| `DELETE` | `/api/v1/clients/{id}` | 삭제 | ✅ Required |
|
|
| `PATCH` | `/api/v1/clients/{id}/toggle` | 활성/비활성 토글 | ✅ Required |
|
|
|
|
### 2.2 Client Group (거래처 그룹) API
|
|
|
|
| Method | Endpoint | 설명 | 인증 |
|
|
|--------|----------|------|------|
|
|
| `GET` | `/api/v1/client-groups` | 그룹 목록 | ✅ Required |
|
|
| `GET` | `/api/v1/client-groups/{id}` | 그룹 단건 | ✅ Required |
|
|
| `POST` | `/api/v1/client-groups` | 그룹 생성 | ✅ Required |
|
|
| `PUT` | `/api/v1/client-groups/{id}` | 그룹 수정 | ✅ Required |
|
|
| `DELETE` | `/api/v1/client-groups/{id}` | 그룹 삭제 | ✅ Required |
|
|
| `PATCH` | `/api/v1/client-groups/{id}/toggle` | 그룹 활성/비활성 | ✅ Required |
|
|
|
|
### 2.3 목록 조회 파라미터 (`GET /api/v1/clients`)
|
|
|
|
| 파라미터 | 타입 | 설명 | 기본값 |
|
|
|---------|------|------|--------|
|
|
| `page` | integer | 페이지 번호 | 1 |
|
|
| `size` | integer | 페이지당 개수 | 20 |
|
|
| `q` | string | 검색어 (이름, 코드, 담당자) | - |
|
|
| `only_active` | boolean | 활성 거래처만 조회 | - |
|
|
|
|
---
|
|
|
|
## 3. 데이터 모델 비교
|
|
|
|
### 3.1 필드 매핑 분석
|
|
|
|
| 프론트엔드 필드 | 백엔드 필드 | 상태 | 비고 |
|
|
|---------------|------------|------|------|
|
|
| `id` | `id` | ✅ 동일 | |
|
|
| `code` | `client_code` | ✅ 매핑 필요 | 필드명 변경 |
|
|
| `name` | `name` | ✅ 동일 | |
|
|
| `representative` | `contact_person` | ✅ 매핑 필요 | 필드명 변경 |
|
|
| `phone` | `phone` | ✅ 동일 | |
|
|
| `email` | `email` | ✅ 동일 | |
|
|
| `address` | `address` | ✅ 동일 | |
|
|
| `registeredDate` | `created_at` | ✅ 매핑 필요 | 필드명 변경 |
|
|
| `status` | `is_active` | ✅ 매핑 완료 | "활성"/"비활성" ↔ boolean (2025-12-09 변경) |
|
|
| `businessNo` | `business_no` | ✅ 추가됨 | |
|
|
| `businessType` | `business_type` | ✅ 추가됨 | |
|
|
| `businessItem` | `business_item` | ✅ 추가됨 | |
|
|
| - | `tenant_id` | ✅ 백엔드 전용 | 자동 처리 |
|
|
| - | `client_group_id` | ⚠️ 프론트 없음 | 그룹 기능 미구현 |
|
|
|
|
### 3.2 백엔드 모델 필드 (Client.php)
|
|
|
|
```php
|
|
protected $fillable = [
|
|
'tenant_id',
|
|
'client_group_id',
|
|
'client_code', // 거래처 코드
|
|
'name', // 거래처명
|
|
'contact_person', // 담당자
|
|
'phone', // 전화번호
|
|
'email', // 이메일
|
|
'address', // 주소
|
|
'is_active', // 활성 상태 (Y/N)
|
|
];
|
|
```
|
|
|
|
---
|
|
|
|
## 4. 백엔드 수정 요청 사항
|
|
|
|
### 4.1 1차 필드 추가 ✅ 완료 (2025-12-04)
|
|
|
|
| 필드명 | 타입 | 설명 | 상태 |
|
|
|--------|------|------|------|
|
|
| `business_no` | string(20) | 사업자등록번호 | ✅ 추가됨 |
|
|
| `business_type` | string(50) | 업태 | ✅ 추가됨 |
|
|
| `business_item` | string(100) | 업종 | ✅ 추가됨 |
|
|
|
|
---
|
|
|
|
### 4.2 🚨 2차 필드 추가 요청 (sam-design 기준) - 2025-12-04
|
|
|
|
> **참고**: `sam-design/src/components/ClientRegistration.tsx` 기준으로 UI 구현 필요
|
|
> 현재 백엔드 API에 누락된 필드들 추가 요청
|
|
|
|
#### 섹션 1: 기본 정보 추가 필드
|
|
| 필드명 | 타입 | 설명 | nullable | 비고 |
|
|
|--------|------|------|----------|------|
|
|
| `client_type` | enum('매입','매출','매입매출') | 거래처 유형 | NO | 기본값 '매입' |
|
|
|
|
#### 섹션 2: 연락처 정보 추가 필드
|
|
| 필드명 | 타입 | 설명 | nullable |
|
|
|--------|------|------|----------|
|
|
| `mobile` | string(20) | 모바일 번호 | YES |
|
|
| `fax` | string(20) | 팩스 번호 | YES |
|
|
|
|
#### 섹션 3: 담당자 정보 추가 필드
|
|
| 필드명 | 타입 | 설명 | nullable |
|
|
|--------|------|------|----------|
|
|
| `manager_name` | string(50) | 담당자명 | YES |
|
|
| `manager_tel` | string(20) | 담당자 전화 | YES |
|
|
| `system_manager` | string(50) | 시스템 관리자 | YES |
|
|
|
|
#### 섹션 4: 발주처 설정 추가 필드
|
|
| 필드명 | 타입 | 설명 | nullable |
|
|
|--------|------|------|----------|
|
|
| `account_id` | string(50) | 계정 ID | YES |
|
|
| `account_password` | string(255) | 비밀번호 (암호화) | YES |
|
|
| `purchase_payment_day` | string(20) | 매입 결제일 | YES |
|
|
| `sales_payment_day` | string(20) | 매출 결제일 | YES |
|
|
|
|
#### 섹션 5: 약정 세금 추가 필드
|
|
| 필드명 | 타입 | 설명 | nullable |
|
|
|--------|------|------|----------|
|
|
| `tax_agreement` | boolean | 세금 약정 여부 | YES |
|
|
| `tax_amount` | decimal(15,2) | 약정 금액 | YES |
|
|
| `tax_start_date` | date | 약정 시작일 | YES |
|
|
| `tax_end_date` | date | 약정 종료일 | YES |
|
|
|
|
#### 섹션 6: 악성채권 정보 추가 필드
|
|
| 필드명 | 타입 | 설명 | nullable |
|
|
|--------|------|------|----------|
|
|
| `bad_debt` | boolean | 악성채권 여부 | YES |
|
|
| `bad_debt_amount` | decimal(15,2) | 악성채권 금액 | YES |
|
|
| `bad_debt_receive_date` | date | 채권 발생일 | YES |
|
|
| `bad_debt_end_date` | date | 채권 만료일 | YES |
|
|
| `bad_debt_progress` | enum('협의중','소송중','회수완료','대손처리') | 진행 상태 | YES |
|
|
|
|
#### 섹션 7: 기타 정보 추가 필드
|
|
| 필드명 | 타입 | 설명 | nullable |
|
|
|--------|------|------|----------|
|
|
| `memo` | text | 메모 | YES |
|
|
|
|
---
|
|
|
|
### 4.3 마이그레이션 예시
|
|
|
|
```sql
|
|
-- 기본 정보
|
|
ALTER TABLE clients ADD COLUMN client_type ENUM('매입','매출','매입매출') DEFAULT '매입';
|
|
|
|
-- 연락처 정보
|
|
ALTER TABLE clients ADD COLUMN mobile VARCHAR(20) NULL;
|
|
ALTER TABLE clients ADD COLUMN fax VARCHAR(20) NULL;
|
|
|
|
-- 담당자 정보
|
|
ALTER TABLE clients ADD COLUMN manager_name VARCHAR(50) NULL;
|
|
ALTER TABLE clients ADD COLUMN manager_tel VARCHAR(20) NULL;
|
|
ALTER TABLE clients ADD COLUMN system_manager VARCHAR(50) NULL;
|
|
|
|
-- 발주처 설정
|
|
ALTER TABLE clients ADD COLUMN account_id VARCHAR(50) NULL;
|
|
ALTER TABLE clients ADD COLUMN account_password VARCHAR(255) NULL;
|
|
ALTER TABLE clients ADD COLUMN purchase_payment_day VARCHAR(20) NULL;
|
|
ALTER TABLE clients ADD COLUMN sales_payment_day VARCHAR(20) NULL;
|
|
|
|
-- 약정 세금
|
|
ALTER TABLE clients ADD COLUMN tax_agreement TINYINT(1) DEFAULT 0;
|
|
ALTER TABLE clients ADD COLUMN tax_amount DECIMAL(15,2) NULL;
|
|
ALTER TABLE clients ADD COLUMN tax_start_date DATE NULL;
|
|
ALTER TABLE clients ADD COLUMN tax_end_date DATE NULL;
|
|
|
|
-- 악성채권 정보
|
|
ALTER TABLE clients ADD COLUMN bad_debt TINYINT(1) DEFAULT 0;
|
|
ALTER TABLE clients ADD COLUMN bad_debt_amount DECIMAL(15,2) NULL;
|
|
ALTER TABLE clients ADD COLUMN bad_debt_receive_date DATE NULL;
|
|
ALTER TABLE clients ADD COLUMN bad_debt_end_date DATE NULL;
|
|
ALTER TABLE clients ADD COLUMN bad_debt_progress ENUM('협의중','소송중','회수완료','대손처리') NULL;
|
|
|
|
-- 기타 정보
|
|
ALTER TABLE clients ADD COLUMN memo TEXT NULL;
|
|
```
|
|
|
|
---
|
|
|
|
### 4.4 수정 필요 파일 목록
|
|
|
|
| 파일 | 수정 내용 |
|
|
|------|----------|
|
|
| `app/Models/Orders/Client.php` | fillable에 새 필드 추가, casts 설정 |
|
|
| `database/migrations/xxxx_add_client_extended_fields.php` | 마이그레이션 생성 |
|
|
| `app/Services/ClientService.php` | 새 필드 처리 로직 추가 |
|
|
| `app/Http/Requests/Client/ClientStoreRequest.php` | 유효성 검증 규칙 추가 |
|
|
| `app/Http/Requests/Client/ClientUpdateRequest.php` | 유효성 검증 규칙 추가 |
|
|
| `app/Swagger/v1/ClientApi.php` | API 문서 업데이트 |
|
|
|
|
---
|
|
|
|
## 5. 프론트엔드 API 연동 구현 ✅ 완료 (2025-12-09)
|
|
|
|
### 5.1 완료된 작업
|
|
|
|
| # | 작업 | 우선순위 | 상태 |
|
|
|---|------|---------|------|
|
|
| 1 | Next.js API Proxy 생성 (`/api/proxy/[...path]`) | 🔴 HIGH | ✅ 완료 |
|
|
| 2 | 커스텀 훅 생성 (`useClientList`) | 🔴 HIGH | ✅ 완료 |
|
|
| 3 | 타입 정의 업데이트 (2차 필드 모두 포함) | 🟡 MEDIUM | ✅ 완료 |
|
|
| 4 | CRUD 함수를 API 호출로 변경 | 🔴 HIGH | ✅ 완료 |
|
|
| 5 | 거래처 그룹 기능 추가 (선택) | 🟢 LOW | ⬜ 미완료 |
|
|
|
|
### 5.2 숨긴 섹션 (기획 미확정)
|
|
- 발주처 설정: 계정ID, 비밀번호, 매입/매출 결제일
|
|
- 약정 세금: 약정 여부, 금액, 시작/종료일
|
|
- 악성채권 정보: 악성채권 여부, 금액, 발생/만료일, 진행상태
|
|
|
|
> ⚠️ 백엔드 API 지원됨. 기획 확정 시 `ClientRegistration.tsx` TODO 주석 해제
|
|
|
|
### 5.2 API Proxy 구현 패턴
|
|
|
|
```typescript
|
|
// /src/app/api/proxy/clients/route.ts
|
|
import { NextRequest, NextResponse } from 'next/server';
|
|
|
|
const BACKEND_URL = process.env.NEXT_PUBLIC_API_URL;
|
|
|
|
export async function GET(request: NextRequest) {
|
|
const token = request.cookies.get('access_token')?.value;
|
|
const searchParams = request.nextUrl.searchParams;
|
|
|
|
const response = await fetch(
|
|
`${BACKEND_URL}/api/v1/clients?${searchParams.toString()}`,
|
|
{
|
|
headers: {
|
|
'Authorization': `Bearer ${token}`,
|
|
'X-API-KEY': process.env.NEXT_PUBLIC_API_KEY || '',
|
|
},
|
|
}
|
|
);
|
|
|
|
return NextResponse.json(await response.json());
|
|
}
|
|
```
|
|
|
|
### 5.3 useClientList 훅 구현 패턴
|
|
|
|
```typescript
|
|
// /src/hooks/useClientList.ts
|
|
export function useClientList() {
|
|
const [clients, setClients] = useState<Client[]>([]);
|
|
const [pagination, setPagination] = useState<PaginationInfo | null>(null);
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
|
|
const fetchClients = async (params: FetchParams) => {
|
|
setIsLoading(true);
|
|
const searchParams = new URLSearchParams({
|
|
page: String(params.page || 1),
|
|
size: String(params.size || 20),
|
|
...(params.q && { q: params.q }),
|
|
...(params.onlyActive !== undefined && { only_active: String(params.onlyActive) }),
|
|
});
|
|
|
|
const response = await fetch(`/api/proxy/clients?${searchParams}`);
|
|
const data = await response.json();
|
|
|
|
setClients(data.data.data);
|
|
setPagination({
|
|
currentPage: data.data.current_page,
|
|
lastPage: data.data.last_page,
|
|
total: data.data.total,
|
|
});
|
|
setIsLoading(false);
|
|
};
|
|
|
|
return { clients, pagination, isLoading, fetchClients };
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 6. 데이터 변환 유틸리티
|
|
|
|
### 6.1 API 응답 → 프론트엔드 타입 변환
|
|
|
|
```typescript
|
|
// API 응답 타입
|
|
interface ClientApiResponse {
|
|
id: number;
|
|
client_code: string;
|
|
name: string;
|
|
contact_person: string | null;
|
|
phone: string | null;
|
|
email: string | null;
|
|
address: string | null;
|
|
is_active: 'Y' | 'N';
|
|
created_at: string;
|
|
updated_at: string;
|
|
}
|
|
|
|
// 프론트엔드 타입으로 변환
|
|
function transformClient(api: ClientApiResponse): CustomerAccount {
|
|
return {
|
|
id: String(api.id),
|
|
code: api.client_code,
|
|
name: api.name,
|
|
representative: api.contact_person || '',
|
|
phone: api.phone || '',
|
|
email: api.email || '',
|
|
address: api.address || '',
|
|
businessNo: '', // TODO: 백엔드 필드 추가 후 매핑
|
|
businessType: '', // TODO: 백엔드 필드 추가 후 매핑
|
|
businessItem: '', // TODO: 백엔드 필드 추가 후 매핑
|
|
registeredDate: api.created_at.split(' ')[0],
|
|
status: api.is_active === 'Y' ? '활성' : '비활성',
|
|
};
|
|
}
|
|
```
|
|
|
|
### 6.2 프론트엔드 → API 요청 변환
|
|
|
|
```typescript
|
|
function transformToApiRequest(form: FormData): ClientCreateRequest {
|
|
return {
|
|
client_code: form.code,
|
|
name: form.name,
|
|
contact_person: form.representative || null,
|
|
phone: form.phone || null,
|
|
email: form.email || null,
|
|
address: form.address || null,
|
|
is_active: 'Y',
|
|
};
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 7. 결론 및 권장 사항
|
|
|
|
### 7.1 즉시 진행 가능 (백엔드 변경 없이)
|
|
|
|
1. ✅ API Proxy 생성
|
|
2. ✅ useClientList 훅 구현
|
|
3. ✅ 기본 CRUD 연동 (현재 백엔드 필드만 사용)
|
|
|
|
### 7.2 백엔드 변경 필요
|
|
|
|
1. ⚠️ `business_no`, `business_type`, `business_item` 필드 추가
|
|
2. ⚠️ ClientService, ClientStoreRequest, ClientUpdateRequest 업데이트
|
|
3. ⚠️ Swagger 문서 업데이트
|
|
|
|
### 7.3 선택적 개선
|
|
|
|
1. 거래처 그룹 기능 프론트엔드 구현
|
|
2. 거래처 상세 페이지 구현
|
|
3. 엑셀 내보내기/가져오기 기능
|
|
|
|
---
|
|
|
|
## 참고 파일
|
|
|
|
### 백엔드 (sam-api)
|
|
- `app/Http/Controllers/Api/V1/ClientController.php`
|
|
- `app/Http/Controllers/Api/V1/ClientGroupController.php`
|
|
- `app/Services/ClientService.php`
|
|
- `app/Services/ClientGroupService.php`
|
|
- `app/Models/Orders/Client.php`
|
|
- `app/Models/Orders/ClientGroup.php`
|
|
- `app/Swagger/v1/ClientApi.php`
|
|
- `routes/api.php` (Line 316-333)
|
|
|
|
### 프론트엔드 (sam-react-prod)
|
|
- `src/app/[locale]/(protected)/sales/client-management-sales-admin/page.tsx` |