Files
sam-react-prod/claudedocs/sales/[API-2025-12-04] client-api-analysis.md
byeongcheolryu ded0bc2439 fix: TypeScript 타입 오류 수정 및 설정 페이지 추가
- 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>
2025-12-09 18:07:47 +09:00

13 KiB

거래처 관리 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)

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 마이그레이션 예시

-- 기본 정보
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 구현 패턴

// /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 훅 구현 패턴

// /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 응답 → 프론트엔드 타입 변환

// 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 요청 변환

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