docs: [종합정비] 구조 재편 — Phase 0+2+4 통합

- Phase 0: INDEX.md 전면 재작성, CLAUDE.md→INDEX.md 통합 삭제
- Phase 0: front/→guides/ 이관(5개 파일), changes/ D7 포맷 통일(3개)
- Phase 0: guides/ai-config-설정.md→ai-config-settings.md D3 통일
- Phase 2: architecture/+specs/→system/ 이관(6개 이동, 4개 폐기)
- Phase 2: 13개 파일 경로 참조 수정 (specs/→system/, architecture/→system/)
- Phase 4: 7개 파일 11개 교차참조 깨진 링크 수정

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-27 18:03:04 +09:00
parent 252cc00cda
commit 0ace50b006
46 changed files with 283 additions and 8035 deletions

View File

@@ -380,7 +380,7 @@ class ExampleModel extends Model
### SAM 전체 문서
```
docs/specs/ # 시스템 스펙
docs/system/ # 시스템 현황
docs/reference/ # 레퍼런스
docs/guides/ # 가이드 (이 문서 포함)
```

353
guides/auto-login-guide.md Normal file
View File

@@ -0,0 +1,353 @@
# MNG → DEV 자동 로그인 기능 (React 구현 가이드)
## 개요
MNG 관리자가 사용자 목록에서 "DEV 접속" 버튼을 클릭하면, 해당 사용자로 자동 로그인되어 DEV 사이트로 이동하는 기능.
## 아키텍처
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ MNG │ │ API │ │ React │
│ (관리자) │ │ (토큰검증) │ │ (프론트) │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
1. DEV접속 클릭 │ │
│ │ │
2. 토큰생성 ─────────────┼─── DB 저장 ───────┤
│ │ (login_tokens) │
3. URL반환 │ │
│ │ │
4. 새창열기 ─────────────┼───────────────────┼─→ /auto-login?token=xxx
│ │ │
│ 5. 토큰검증 ←────────────┤
│ │ │
│ 6. access_token 발급 ────┤
│ │ │
│ 7. 토큰삭제 (1회용) │
│ │ │
│ │ 8. 로그인 완료 → 대시보드
```
## 환경별 URL
| 환경 | MNG | DEV |
|------|-----|-----|
| 로컬 | https://mng.sam.kr | https://dev.sam.kr |
| 개발 | https://admin.codebridge-x.com | https://dev.codebridge-x.com |
---
## React 구현 작업
### 1. 신규 파일: `/auto-login` 페이지
**경로:** `src/app/[locale]/auto-login/page.tsx`
```tsx
"use client";
import { useEffect, useState } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { transformApiMenusToMenuItems } from "@/lib/utils/menuTransform";
export default function AutoLoginPage() {
const router = useRouter();
const searchParams = useSearchParams();
const [status, setStatus] = useState<"loading" | "success" | "error">("loading");
const [message, setMessage] = useState("자동 로그인 처리 중...");
useEffect(() => {
const token = searchParams.get("token");
if (!token) {
setStatus("error");
setMessage("유효하지 않은 접근입니다.");
return;
}
const performAutoLogin = async () => {
try {
// 1. 기존 세션 클리어 (기존 로그인 사용자 로그아웃)
localStorage.removeItem("user");
// 기존 쿠키도 클리어하기 위해 logout API 호출 (선택사항)
try {
await fetch("/api/auth/logout", { method: "POST" });
} catch {
// 로그아웃 실패해도 계속 진행
}
// 2. 토큰 로그인 API 호출
const response = await fetch("/api/auth/token-login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ token }),
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || "토큰이 만료되었거나 유효하지 않습니다.");
}
const data = await response.json();
// 3. 사용자 정보 저장 (기존 로그인과 동일한 형식)
const transformedMenus = transformApiMenusToMenuItems(data.menus || []);
const userData = {
name: data.user?.name,
position: data.roles?.[0]?.description || "사용자",
userId: data.user?.user_id,
menu: transformedMenus,
roles: data.roles || [],
tenant: data.tenant || {},
};
localStorage.setItem("user", JSON.stringify(userData));
setStatus("success");
setMessage("로그인 성공! 대시보드로 이동합니다...");
// 4. 대시보드로 리다이렉트
setTimeout(() => router.push("/dashboard"), 500);
} catch (error) {
console.error("Auto login failed:", error);
setStatus("error");
setMessage(error instanceof Error ? error.message : "로그인에 실패했습니다.");
}
};
performAutoLogin();
}, [searchParams, router]);
return (
<div className="min-h-screen bg-background flex items-center justify-center">
<div className="text-center p-8">
{status === "loading" && (
<div className="animate-spin h-12 w-12 border-4 border-primary border-t-transparent rounded-full mx-auto mb-4" />
)}
{status === "success" && (
<div className="h-12 w-12 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-4">
<svg className="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
</div>
)}
{status === "error" && (
<div className="h-12 w-12 bg-red-100 rounded-full flex items-center justify-center mx-auto mb-4">
<svg className="w-6 h-6 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</div>
)}
<p className={`text-lg ${status === "error" ? "text-destructive" : "text-muted-foreground"}`}>
{message}
</p>
{status === "error" && (
<button
onClick={() => router.push("/login")}
className="mt-4 px-4 py-2 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90"
>
로그인 페이지로 이동
</button>
)}
</div>
</div>
);
}
```
---
### 2. 신규 파일: 토큰 로그인 API Route
**경로:** `src/app/api/auth/token-login/route.ts`
```ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
/**
* 토큰 자동 로그인 API
*
* MNG 관리자가 생성한 One-Time Token으로 자동 로그인 처리
*
* 흐름:
* 1. token 파라미터 검증
* 2. PHP API /v1/auth/token-login 호출
* 3. access_token을 HttpOnly 쿠키로 저장
* 4. 사용자 정보 반환
*/
export async function POST(request: NextRequest) {
try {
const { token } = await request.json();
if (!token) {
return NextResponse.json(
{ error: "Token is required" },
{ status: 400 }
);
}
// PHP 백엔드 호출
const backendResponse = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/v1/auth/token-login`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
"Accept": "application/json",
"X-API-KEY": process.env.NEXT_PUBLIC_API_KEY || "",
},
body: JSON.stringify({ token }),
}
);
if (!backendResponse.ok) {
const errorText = await backendResponse.text();
console.error("Token login backend error:", errorText);
return NextResponse.json(
{ error: "Invalid or expired token" },
{ status: 401 }
);
}
const data = await backendResponse.json();
// HttpOnly 쿠키 설정 (기존 login/route.ts와 동일)
const isProduction = process.env.NODE_ENV === "production";
const accessTokenCookie = [
`access_token=${data.access_token}`,
"HttpOnly",
...(isProduction ? ["Secure"] : []),
"SameSite=Lax",
"Path=/",
`Max-Age=${data.expires_in || 7200}`,
].join("; ");
const refreshTokenCookie = [
`refresh_token=${data.refresh_token}`,
"HttpOnly",
...(isProduction ? ["Secure"] : []),
"SameSite=Lax",
"Path=/",
"Max-Age=604800",
].join("; ");
console.log("✅ Token login successful - tokens stored in HttpOnly cookies");
const response = NextResponse.json({
message: data.message,
user: data.user,
tenant: data.tenant,
menus: data.menus,
roles: data.roles,
expires_in: data.expires_in,
expires_at: data.expires_at,
});
response.headers.append("Set-Cookie", accessTokenCookie);
response.headers.append("Set-Cookie", refreshTokenCookie);
return response;
} catch (error) {
console.error("Token login error:", error);
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 }
);
}
}
```
---
### 3. 수정 파일: auth-config.ts
**경로:** `src/lib/api/auth/auth-config.ts`
```ts
// guestOnlyRoutes 배열에 /auto-login 추가
guestOnlyRoutes: [
'/login',
'/forgot-password',
'/auto-login', // 추가
],
```
---
### 4. 수정 파일: middleware.ts (확인 필요)
`/auto-login` 경로가 인증 없이 접근 가능한지 확인 필요.
`guestOnlyRoutes`에 추가되면 자동으로 처리될 수 있음.
---
## API 엔드포인트 (백엔드)
### POST /api/v1/auth/token-login
**Request:**
```json
{
"token": "abc123..." // 64자 랜덤 문자열
}
```
**Response (성공):**
```json
{
"message": "로그인 성공",
"access_token": "1|xxx...",
"refresh_token": "2|yyy...",
"token_type": "Bearer",
"expires_in": 7200,
"expires_at": "2025-12-20 15:00:00",
"user": {
"id": 1,
"user_id": "testuser",
"name": "테스트 사용자",
"email": "test@example.com"
},
"tenant": { ... },
"menus": [ ... ],
"roles": [ ... ]
}
```
**Response (실패):**
```json
{
"error": "Invalid or expired token"
}
```
---
## 보안 고려사항
1. **토큰 1회 사용**: 사용 후 즉시 삭제
2. **토큰 만료**: 5분 (생성 후)
3. **HTTPS 필수**: 토큰이 URL에 노출되므로
4. **관리자 권한**: MNG 로그인한 관리자만 토큰 생성 가능
---
## 테스트 시나리오
1. MNG에서 사용자 목록 → "DEV 접속" 버튼 클릭
2. 새 창에서 DEV 사이트 열림
3. 자동 로그인 처리 (로딩 표시)
4. 성공 시 대시보드로 이동
5. 실패 시 에러 메시지 + 로그인 페이지 이동 버튼
---
## 작성일
2025-12-20

1442
guides/erp-api-detail.md Normal file

File diff suppressed because it is too large Load Diff

396
guides/erp-api-list.md Normal file
View File

@@ -0,0 +1,396 @@
# ERP API 목록 (List vs Detail 구분)
> **작성일**: 2025-12-19
> **기준 문서**: [erp-api-development-plan.md](../plans/erp-api-development-plan.md)
> **Swagger UI**: http://sam.kr/api-docs/index.html
---
## 📊 API 유형 분류
| 유형 | 설명 | HTTP Method |
|------|------|-------------|
| **LIST** | 목록 조회 (페이지네이션) | GET |
| **DETAIL** | 단건 조회 | GET |
| **CREATE** | 생성 | POST |
| **UPDATE** | 수정 | PUT/PATCH |
| **DELETE** | 삭제 | DELETE |
| **ACTION** | 상태 변경/특수 액션 | POST/PATCH |
---
## 🗂️ Phase 1: 확장 개발 API
### 1. 휴가 관리 (Leaves) - 11개 API
| 유형 | Method | Endpoint | 설명 |
|------|--------|----------|------|
| LIST | GET | `/v1/leaves` | 휴가 목록 조회 |
| DETAIL | GET | `/v1/leaves/{id}` | 휴가 상세 조회 |
| CREATE | POST | `/v1/leaves` | 휴가 신청 |
| UPDATE | PATCH | `/v1/leaves/{id}` | 휴가 수정 |
| DELETE | DELETE | `/v1/leaves/{id}` | 휴가 삭제 |
| ACTION | POST | `/v1/leaves/{id}/approve` | 휴가 승인 |
| ACTION | POST | `/v1/leaves/{id}/reject` | 휴가 반려 |
| ACTION | POST | `/v1/leaves/{id}/cancel` | 휴가 취소 |
| DETAIL | GET | `/v1/leaves/balance` | 내 잔여휴가 조회 |
| DETAIL | GET | `/v1/leaves/balance/{userId}` | 특정 사용자 잔여휴가 |
| UPDATE | PUT | `/v1/leaves/balance` | 잔여휴가 설정 |
---
### 2. 근무/출퇴근 설정 (Work Settings) - 10개 API
#### 2.1 근무 설정 (2개)
| 유형 | Method | Endpoint | 설명 |
|------|--------|----------|------|
| DETAIL | GET | `/v1/settings/work` | 근무 설정 조회 |
| UPDATE | PUT | `/v1/settings/work` | 근무 설정 수정 |
#### 2.2 출퇴근 설정 (2개)
| 유형 | Method | Endpoint | 설명 |
|------|--------|----------|------|
| DETAIL | GET | `/v1/settings/attendance` | 출퇴근 설정 조회 |
| UPDATE | PUT | `/v1/settings/attendance` | 출퇴근 설정 수정 |
#### 2.3 현장 관리 (Sites) - 6개
| 유형 | Method | Endpoint | 설명 |
|------|--------|----------|------|
| LIST | GET | `/v1/sites` | 현장 목록 조회 |
| LIST | GET | `/v1/sites/active` | 활성 현장 목록 (셀렉트박스용) |
| DETAIL | GET | `/v1/sites/{id}` | 현장 상세 조회 |
| CREATE | POST | `/v1/sites` | 현장 등록 |
| UPDATE | PUT | `/v1/sites/{id}` | 현장 수정 |
| DELETE | DELETE | `/v1/sites/{id}` | 현장 삭제 |
---
### 3. 카드/계좌 관리 - 15개 API
#### 3.1 카드 관리 (Cards) - 7개
| 유형 | Method | Endpoint | 설명 |
|------|--------|----------|------|
| LIST | GET | `/v1/cards` | 카드 목록 조회 |
| LIST | GET | `/v1/cards/active` | 활성 카드 목록 (셀렉트박스용) |
| DETAIL | GET | `/v1/cards/{id}` | 카드 상세 조회 |
| CREATE | POST | `/v1/cards` | 카드 등록 |
| UPDATE | PUT | `/v1/cards/{id}` | 카드 수정 |
| DELETE | DELETE | `/v1/cards/{id}` | 카드 삭제 |
| ACTION | PATCH | `/v1/cards/{id}/toggle` | 카드 사용/정지 토글 |
#### 3.2 계좌 관리 (Bank Accounts) - 8개
| 유형 | Method | Endpoint | 설명 |
|------|--------|----------|------|
| LIST | GET | `/v1/bank-accounts` | 계좌 목록 조회 |
| LIST | GET | `/v1/bank-accounts/active` | 활성 계좌 목록 (셀렉트박스용) |
| DETAIL | GET | `/v1/bank-accounts/{id}` | 계좌 상세 조회 |
| CREATE | POST | `/v1/bank-accounts` | 계좌 등록 |
| UPDATE | PUT | `/v1/bank-accounts/{id}` | 계좌 수정 |
| DELETE | DELETE | `/v1/bank-accounts/{id}` | 계좌 삭제 |
| ACTION | PATCH | `/v1/bank-accounts/{id}/toggle` | 계좌 사용/정지 토글 |
| ACTION | PATCH | `/v1/bank-accounts/{id}/set-primary` | 대표계좌 설정 |
---
### 4. 입금/출금 관리 - 12개 API
#### 4.1 입금 관리 (Deposits) - 6개
| 유형 | Method | Endpoint | 설명 |
|------|--------|----------|------|
| LIST | GET | `/v1/deposits` | 입금 목록 조회 |
| LIST | GET | `/v1/deposits/summary` | 입금 요약 (집계) |
| DETAIL | GET | `/v1/deposits/{id}` | 입금 상세 조회 |
| CREATE | POST | `/v1/deposits` | 입금 등록 |
| UPDATE | PUT | `/v1/deposits/{id}` | 입금 수정 |
| DELETE | DELETE | `/v1/deposits/{id}` | 입금 삭제 |
#### 4.2 출금 관리 (Withdrawals) - 6개
| 유형 | Method | Endpoint | 설명 |
|------|--------|----------|------|
| LIST | GET | `/v1/withdrawals` | 출금 목록 조회 |
| LIST | GET | `/v1/withdrawals/summary` | 출금 요약 (집계) |
| DETAIL | GET | `/v1/withdrawals/{id}` | 출금 상세 조회 |
| CREATE | POST | `/v1/withdrawals` | 출금 등록 |
| UPDATE | PUT | `/v1/withdrawals/{id}` | 출금 수정 |
| DELETE | DELETE | `/v1/withdrawals/{id}` | 출금 삭제 |
---
### 5. 매출/매입 관리 - 17개 API
#### 5.1 매출 관리 (Sales) - 10개
| 유형 | Method | Endpoint | 설명 |
|------|--------|----------|------|
| LIST | GET | `/v1/sales` | 매출 목록 조회 |
| LIST | GET | `/v1/sales/summary` | 매출 요약 (집계) |
| DETAIL | GET | `/v1/sales/{id}` | 매출 상세 조회 |
| CREATE | POST | `/v1/sales` | 매출 등록 |
| UPDATE | PUT | `/v1/sales/{id}` | 매출 수정 |
| DELETE | DELETE | `/v1/sales/{id}` | 매출 삭제 |
| ACTION | POST | `/v1/sales/{id}/confirm` | 매출 확정 |
| DETAIL | GET | `/v1/sales/{id}/statement` | 거래명세서 조회 |
| ACTION | POST | `/v1/sales/{id}/statement/issue` | 거래명세서 발행 |
| ACTION | POST | `/v1/sales/{id}/statement/send` | 거래명세서 이메일 발송 |
#### 5.2 매입 관리 (Purchases) - 7개
| 유형 | Method | Endpoint | 설명 |
|------|--------|----------|------|
| LIST | GET | `/v1/purchases` | 매입 목록 조회 |
| LIST | GET | `/v1/purchases/summary` | 매입 요약 (집계) |
| DETAIL | GET | `/v1/purchases/{id}` | 매입 상세 조회 |
| CREATE | POST | `/v1/purchases` | 매입 등록 |
| UPDATE | PUT | `/v1/purchases/{id}` | 매입 수정 |
| DELETE | DELETE | `/v1/purchases/{id}` | 매입 삭제 |
| ACTION | POST | `/v1/purchases/{id}/confirm` | 매입 확정 |
---
### 6. 보고서 (Reports) - 4개 API
| 유형 | Method | Endpoint | 설명 |
|------|--------|----------|------|
| DETAIL | GET | `/v1/reports/daily` | 일일 일보 조회 |
| ACTION | GET | `/v1/reports/daily/export` | 일일 일보 엑셀 다운로드 |
| DETAIL | GET | `/v1/reports/expense-estimate` | 지출 예상 내역서 조회 |
| ACTION | GET | `/v1/reports/expense-estimate/export` | 지출 예상 내역서 엑셀 다운로드 |
---
## 👤 Phase 5: 사용자/계정 관리 API
### 16. 사용자 초대 (User Invitations) - 5개 API
| 유형 | Method | Endpoint | 설명 |
|------|--------|----------|------|
| LIST | GET | `/v1/users/invitations` | 초대 목록 조회 |
| CREATE | POST | `/v1/users/invite` | 사용자 초대 발송 |
| ACTION | POST | `/v1/users/invitations/{token}/accept` | 초대 수락 |
| DELETE | DELETE | `/v1/users/invitations/{id}` | 초대 취소 |
| ACTION | POST | `/v1/users/invitations/{id}/resend` | 초대 재발송 |
---
### 17. 알림 설정 (Notification Settings) - 3개 API
| 유형 | Method | Endpoint | 설명 |
|------|--------|----------|------|
| LIST | GET | `/v1/users/me/notification-settings` | 알림 설정 조회 |
| UPDATE | PUT | `/v1/users/me/notification-settings` | 알림 설정 수정 (단일) |
| ACTION | PUT | `/v1/users/me/notification-settings/bulk` | 알림 설정 일괄 저장 |
---
### 18. 계정 관리 (Account) - 4개 API
| 유형 | Method | Endpoint | 설명 |
|------|--------|----------|------|
| ACTION | POST | `/v1/account/withdraw` | 회원 탈퇴 (SAM 완전 탈퇴) |
| ACTION | POST | `/v1/account/suspend` | 사용 중지 (현재 테넌트에서만 탈퇴) |
| DETAIL | GET | `/v1/account/agreements` | 약관 동의 정보 조회 |
| UPDATE | PUT | `/v1/account/agreements` | 약관 동의 정보 수정 |
---
## 🔨 Phase 2: 핵심 신규 개발 API
### 7. 전자결재 모듈 - 26개 API
#### 7.1 결재 양식 (Approval Forms) - 6개
| 유형 | Method | Endpoint | 설명 |
|------|--------|----------|------|
| LIST | GET | `/v1/approval-forms` | 결재 양식 목록 |
| LIST | GET | `/v1/approval-forms/active` | 활성 결재 양식 (셀렉트박스용) |
| DETAIL | GET | `/v1/approval-forms/{id}` | 결재 양식 상세 |
| CREATE | POST | `/v1/approval-forms` | 결재 양식 등록 |
| UPDATE | PATCH | `/v1/approval-forms/{id}` | 결재 양식 수정 |
| DELETE | DELETE | `/v1/approval-forms/{id}` | 결재 양식 삭제 |
#### 7.2 결재선 템플릿 (Approval Lines) - 5개
| 유형 | Method | Endpoint | 설명 |
|------|--------|----------|------|
| LIST | GET | `/v1/approval-lines` | 결재선 목록 |
| DETAIL | GET | `/v1/approval-lines/{id}` | 결재선 상세 |
| CREATE | POST | `/v1/approval-lines` | 결재선 등록 |
| UPDATE | PATCH | `/v1/approval-lines/{id}` | 결재선 수정 |
| DELETE | DELETE | `/v1/approval-lines/{id}` | 결재선 삭제 |
#### 7.3 결재 문서 (Approvals) - 15개
| 유형 | Method | Endpoint | 설명 |
|------|--------|----------|------|
| LIST | GET | `/v1/approvals/drafts` | 기안함 (내가 작성한 문서) |
| LIST | GET | `/v1/approvals/drafts/summary` | 기안함 요약 |
| LIST | GET | `/v1/approvals/inbox` | 결재함 (결재 대기 문서) |
| LIST | GET | `/v1/approvals/inbox/summary` | 결재함 요약 |
| LIST | GET | `/v1/approvals/reference` | 참조함 (참조 문서) |
| DETAIL | GET | `/v1/approvals/{id}` | 결재 문서 상세 |
| CREATE | POST | `/v1/approvals` | 결재 문서 작성 (임시저장) |
| UPDATE | PATCH | `/v1/approvals/{id}` | 결재 문서 수정 |
| DELETE | DELETE | `/v1/approvals/{id}` | 결재 문서 삭제 |
| ACTION | POST | `/v1/approvals/{id}/submit` | 결재 상신 |
| ACTION | POST | `/v1/approvals/{id}/approve` | 결재 승인 |
| ACTION | POST | `/v1/approvals/{id}/reject` | 결재 반려 |
| ACTION | POST | `/v1/approvals/{id}/cancel` | 결재 취소/회수 |
| ACTION | POST | `/v1/approvals/{id}/read` | 참조 열람 처리 |
| ACTION | POST | `/v1/approvals/{id}/unread` | 참조 미열람 처리 |
---
### 8. 급여 관리 (Payrolls) - 13개 API
| 유형 | Method | Endpoint | 설명 |
|------|--------|----------|------|
| LIST | GET | `/v1/payrolls` | 급여 목록 조회 |
| LIST | GET | `/v1/payrolls/summary` | 급여 현황 요약 |
| DETAIL | GET | `/v1/payrolls/{id}` | 급여 상세 조회 |
| DETAIL | GET | `/v1/payrolls/{id}/payslip` | 급여명세서 조회 |
| CREATE | POST | `/v1/payrolls` | 급여 등록 |
| UPDATE | PUT | `/v1/payrolls/{id}` | 급여 수정 |
| DELETE | DELETE | `/v1/payrolls/{id}` | 급여 삭제 |
| ACTION | POST | `/v1/payrolls/calculate` | 급여 일괄 계산 |
| ACTION | POST | `/v1/payrolls/bulk-confirm` | 급여 일괄 확정 |
| ACTION | POST | `/v1/payrolls/{id}/confirm` | 급여 확정 |
| ACTION | POST | `/v1/payrolls/{id}/pay` | 급여 지급 처리 |
| DETAIL | GET | `/v1/settings/payroll` | 급여 설정 조회 |
| UPDATE | PUT | `/v1/settings/payroll` | 급여 설정 수정 |
---
### 9. 대시보드 (Dashboard) - 3개 API
| 유형 | Method | Endpoint | 설명 |
|------|--------|----------|------|
| DETAIL | GET | `/v1/dashboard/summary` | 대시보드 요약 데이터 |
| DETAIL | GET | `/v1/dashboard/charts` | 차트 데이터 |
| DETAIL | GET | `/v1/dashboard/approvals` | 결재 현황 |
---
## 🔧 Phase 3: 추가 기능 API
### 10. AI 리포트 (AI Reports) - 4개 API
| 유형 | Method | Endpoint | 설명 |
|------|--------|----------|------|
| LIST | GET | `/v1/reports/ai` | AI 리포트 목록 |
| DETAIL | GET | `/v1/reports/ai/{id}` | AI 리포트 상세 |
| ACTION | POST | `/v1/reports/ai/generate` | AI 리포트 생성 |
| DELETE | DELETE | `/v1/reports/ai/{id}` | AI 리포트 삭제 |
---
### 11. 가지급금 관리 (Loans) - 9개 API
| 유형 | Method | Endpoint | 설명 |
|------|--------|----------|------|
| LIST | GET | `/v1/loans` | 가지급금 목록 조회 |
| LIST | GET | `/v1/loans/summary` | 가지급금 요약 |
| DETAIL | GET | `/v1/loans/{id}` | 가지급금 상세 조회 |
| DETAIL | GET | `/v1/loans/interest-report/{year}` | 연도별 인정이자 리포트 |
| CREATE | POST | `/v1/loans` | 가지급금 등록 |
| UPDATE | PUT | `/v1/loans/{id}` | 가지급금 수정 |
| DELETE | DELETE | `/v1/loans/{id}` | 가지급금 삭제 |
| ACTION | POST | `/v1/loans/calculate-interest` | 인정이자 계산 |
| ACTION | POST | `/v1/loans/{id}/settle` | 가지급금 정산 |
---
### 12. 바로빌 연동 (세금계산서) - 12개 API
#### 12.1 바로빌 설정 (3개)
| 유형 | Method | Endpoint | 설명 |
|------|--------|----------|------|
| DETAIL | GET | `/v1/barobill-settings` | 바로빌 설정 조회 |
| UPDATE | PUT | `/v1/barobill-settings` | 바로빌 설정 저장 |
| ACTION | POST | `/v1/barobill-settings/test-connection` | 연동 테스트 |
#### 12.2 세금계산서 (9개)
| 유형 | Method | Endpoint | 설명 |
|------|--------|----------|------|
| LIST | GET | `/v1/tax-invoices` | 세금계산서 목록 |
| LIST | GET | `/v1/tax-invoices/summary` | 세금계산서 요약 통계 |
| DETAIL | GET | `/v1/tax-invoices/{id}` | 세금계산서 상세 |
| DETAIL | GET | `/v1/tax-invoices/{id}/check-status` | 국세청 전송 상태 조회 |
| CREATE | POST | `/v1/tax-invoices` | 세금계산서 생성 |
| UPDATE | PUT | `/v1/tax-invoices/{id}` | 세금계산서 수정 |
| DELETE | DELETE | `/v1/tax-invoices/{id}` | 세금계산서 삭제 |
| ACTION | POST | `/v1/tax-invoices/{id}/issue` | 세금계산서 발행 |
| ACTION | POST | `/v1/tax-invoices/{id}/cancel` | 세금계산서 취소 |
---
## 💼 Phase 4: SaaS 기능 API
### 13. 요금제 관리 (Plans) - 7개 API
| 유형 | Method | Endpoint | 설명 |
|------|--------|----------|------|
| LIST | GET | `/v1/plans` | 요금제 목록 |
| LIST | GET | `/v1/plans/active` | 활성 요금제 목록 |
| DETAIL | GET | `/v1/plans/{id}` | 요금제 상세 |
| CREATE | POST | `/v1/plans` | 요금제 등록 |
| UPDATE | PUT | `/v1/plans/{id}` | 요금제 수정 |
| DELETE | DELETE | `/v1/plans/{id}` | 요금제 삭제 |
| ACTION | PATCH | `/v1/plans/{id}/toggle` | 요금제 활성/비활성 |
---
### 14. 구독 관리 (Subscriptions) - 8개 API
| 유형 | Method | Endpoint | 설명 |
|------|--------|----------|------|
| LIST | GET | `/v1/subscriptions` | 구독 목록 |
| DETAIL | GET | `/v1/subscriptions/current` | 현재 구독 조회 |
| DETAIL | GET | `/v1/subscriptions/{id}` | 구독 상세 |
| CREATE | POST | `/v1/subscriptions` | 구독 신청 |
| ACTION | POST | `/v1/subscriptions/{id}/cancel` | 구독 해지 |
| ACTION | POST | `/v1/subscriptions/{id}/renew` | 구독 갱신 |
| ACTION | POST | `/v1/subscriptions/{id}/suspend` | 구독 일시정지 |
| ACTION | POST | `/v1/subscriptions/{id}/resume` | 구독 재개 |
---
### 15. 결제 관리 (Payments) - 7개 API
| 유형 | Method | Endpoint | 설명 |
|------|--------|----------|------|
| LIST | GET | `/v1/payments` | 결제 내역 목록 |
| LIST | GET | `/v1/payments/summary` | 결제 요약 |
| DETAIL | GET | `/v1/payments/{id}` | 결제 상세 |
| CREATE | POST | `/v1/payments` | 결제 처리 |
| ACTION | POST | `/v1/payments/{id}/complete` | 결제 완료 처리 |
| ACTION | POST | `/v1/payments/{id}/cancel` | 결제 취소 |
| ACTION | POST | `/v1/payments/{id}/refund` | 결제 환불 |
---
## 📊 API 통계 요약
| Phase | 카테고리 | API 개수 |
|-------|----------|----------|
| Phase 1 | 휴가 관리 | 11 |
| Phase 1 | 근무/출퇴근 설정 | 10 |
| Phase 1 | 카드/계좌 관리 | 15 |
| Phase 1 | 입금/출금 관리 | 12 |
| Phase 1 | 매출/매입 관리 | 17 |
| Phase 1 | 보고서 | 4 |
| Phase 2 | 전자결재 | 26 |
| Phase 2 | 급여 관리 | 13 |
| Phase 2 | 대시보드 | 3 |
| Phase 3 | AI 리포트 | 4 |
| Phase 3 | 가지급금 | 9 |
| Phase 3 | 바로빌 연동 | 12 |
| Phase 4 | 요금제 관리 | 7 |
| Phase 4 | 구독 관리 | 8 |
| Phase 4 | 결제 관리 | 7 |
| Phase 5 | 사용자 초대 | 5 |
| Phase 5 | 알림 설정 | 3 |
| Phase 5 | 계정 관리 | 4 |
| **Total** | - | **170개** |
---
## 🔗 관련 문서
- [ERP API 개발 계획](../plans/erp-api-development-plan.md)
- [ERP API 상세 스펙](./erp-api-detail.md)
- Swagger UI: http://sam.kr/api-docs/index.html

339
guides/item-master-guide.md Normal file
View File

@@ -0,0 +1,339 @@
# 품목기준관리(ItemMaster) API 가이드
> 품목 입력 화면을 구성하는 **페이지-섹션-필드** 구조 관리 시스템
---
## 1. 개요
### 1.1 핵심 개념
| 엔티티 | 테이블 | 설명 |
|--------|--------|------|
| **Page** | `item_pages` | 품목 유형별 화면 (FG, PT, SM, RM, CS) |
| **Section** | `item_sections` | 페이지 내 논리적 영역 |
| **Field** | `item_fields` | 섹션 내 입력 항목 |
| **BomItem** | `item_bom_items` | BOM 섹션 내 부품 항목 |
| **CustomTab** | `custom_tabs` | 커스텀 탭 설정 |
| **UnitOption** | `unit_options` | 단위 옵션 |
### 1.2 아키텍처
- **독립 엔티티 구조**: 섹션, 필드, BOM은 독립적으로 존재하며 재사용 가능
- **링크 테이블**: `entity_relationships`로 관계 관리
- **연결 잠금**: 중요한 구조는 잠금으로 보호 가능
```
ItemPage (item_type: FG, PT, SM, RM, CS)
│ entity_relationships (is_locked)
ItemSection (type: default, bom, custom)
├─ entity_relationships → ItemField
└─ entity_relationships → ItemBomItem
```
---
## 2. API 엔드포인트
### 2.1 초기화
| Method | Endpoint | 설명 |
|--------|----------|------|
| GET | `/api/v1/item-master/init` | 전체 데이터 로드 |
**응답 구조:**
```json
{
"pages": [], // 페이지 + 연결된 섹션/필드
"sections": [], // 모든 독립 섹션
"fields": [], // 모든 독립 필드
"customTabs": [], // 커스텀 탭
"unitOptions": [] // 단위 옵션
}
```
### 2.2 페이지
| Method | Endpoint | 설명 |
|--------|----------|------|
| GET | `/api/v1/item-master/pages` | 페이지 목록 |
| POST | `/api/v1/item-master/pages` | 페이지 생성 |
| PUT | `/api/v1/item-master/pages/{id}` | 페이지 수정 |
| DELETE | `/api/v1/item-master/pages/{id}` | 페이지 삭제 |
### 2.3 섹션
| Method | Endpoint | 설명 |
|--------|----------|------|
| GET | `/api/v1/item-master/sections` | 독립 섹션 목록 |
| POST | `/api/v1/item-master/sections` | 독립 섹션 생성 |
| POST | `/api/v1/item-master/pages/{pageId}/sections` | 섹션 생성 + 페이지 연결 |
| PUT | `/api/v1/item-master/sections/{id}` | 섹션 수정 |
| DELETE | `/api/v1/item-master/sections/{id}` | 섹션 삭제 |
| POST | `/api/v1/item-master/sections/{id}/clone` | 섹션 복제 |
| GET | `/api/v1/item-master/sections/{id}/usage` | 사용처 조회 |
| PUT | `/api/v1/item-master/pages/{pageId}/sections/reorder` | 섹션 순서 변경 |
### 2.4 필드
| Method | Endpoint | 설명 |
|--------|----------|------|
| GET | `/api/v1/item-master/fields` | 독립 필드 목록 |
| POST | `/api/v1/item-master/fields` | 독립 필드 생성 |
| POST | `/api/v1/item-master/sections/{sectionId}/fields` | 필드 생성 + 섹션 연결 |
| PUT | `/api/v1/item-master/fields/{id}` | 필드 수정 |
| DELETE | `/api/v1/item-master/fields/{id}` | 필드 삭제 |
| POST | `/api/v1/item-master/fields/{id}/clone` | 필드 복제 |
| GET | `/api/v1/item-master/fields/{id}/usage` | 사용처 조회 |
| PUT | `/api/v1/item-master/sections/{sectionId}/fields/reorder` | 필드 순서 변경 |
### 2.5 BOM
| Method | Endpoint | 설명 |
|--------|----------|------|
| GET | `/api/v1/item-master/bom-items` | 독립 BOM 목록 |
| POST | `/api/v1/item-master/bom-items` | 독립 BOM 생성 |
| POST | `/api/v1/item-master/sections/{sectionId}/bom-items` | BOM 생성 + 섹션 연결 |
| PUT | `/api/v1/item-master/bom-items/{id}` | BOM 수정 |
| DELETE | `/api/v1/item-master/bom-items/{id}` | BOM 삭제 |
### 2.6 섹션 템플릿
| Method | Endpoint | 설명 |
|--------|----------|------|
| GET | `/api/v1/item-master/section-templates` | 템플릿 목록 |
| POST | `/api/v1/item-master/section-templates` | 템플릿 생성 |
| PUT | `/api/v1/item-master/section-templates/{id}` | 템플릿 수정 |
| DELETE | `/api/v1/item-master/section-templates/{id}` | 템플릿 삭제 |
### 2.7 커스텀 탭
| Method | Endpoint | 설명 |
|--------|----------|------|
| GET | `/api/v1/item-master/custom-tabs` | 탭 목록 |
| POST | `/api/v1/item-master/custom-tabs` | 탭 생성 |
| PUT | `/api/v1/item-master/custom-tabs/{id}` | 탭 수정 |
| DELETE | `/api/v1/item-master/custom-tabs/{id}` | 탭 삭제 |
| PUT | `/api/v1/item-master/custom-tabs/reorder` | 탭 순서 변경 |
### 2.8 단위 옵션
| Method | Endpoint | 설명 |
|--------|----------|------|
| GET | `/api/v1/item-master/unit-options` | 단위 목록 |
| POST | `/api/v1/item-master/unit-options` | 단위 생성 |
| DELETE | `/api/v1/item-master/unit-options/{id}` | 단위 삭제 |
### 2.9 엔티티 관계 (Link/Unlink)
**페이지-섹션 연결:**
| Method | Endpoint | 설명 |
|--------|----------|------|
| POST | `/api/v1/item-master/pages/{pageId}/link-section` | 섹션 연결 |
| DELETE | `/api/v1/item-master/pages/{pageId}/unlink-section/{sectionId}` | 섹션 연결 해제 |
**페이지-필드 연결:**
| Method | Endpoint | 설명 |
|--------|----------|------|
| POST | `/api/v1/item-master/pages/{pageId}/link-field` | 필드 연결 |
| DELETE | `/api/v1/item-master/pages/{pageId}/unlink-field/{fieldId}` | 필드 연결 해제 |
**섹션-필드 연결:**
| Method | Endpoint | 설명 |
|--------|----------|------|
| POST | `/api/v1/item-master/sections/{sectionId}/link-field` | 필드 연결 |
| DELETE | `/api/v1/item-master/sections/{sectionId}/unlink-field/{fieldId}` | 필드 연결 해제 |
**섹션-BOM 연결:**
| Method | Endpoint | 설명 |
|--------|----------|------|
| POST | `/api/v1/item-master/sections/{sectionId}/link-bom` | BOM 연결 |
| DELETE | `/api/v1/item-master/sections/{sectionId}/unlink-bom/{bomId}` | BOM 연결 해제 |
**관계 조회/정렬:**
| Method | Endpoint | 설명 |
|--------|----------|------|
| GET | `/api/v1/item-master/pages/{pageId}/relationships` | 페이지 관계 조회 |
| GET | `/api/v1/item-master/pages/{pageId}/structure` | 페이지 구조 조회 |
| GET | `/api/v1/item-master/sections/{sectionId}/relationships` | 섹션 관계 조회 |
| POST | `/api/v1/item-master/relationships/reorder` | 관계 순서 변경 |
---
## 3. 데이터 구조
### 3.1 ItemPage
```typescript
interface ItemPage {
id: number;
tenant_id: number;
group_id: number;
page_name: string;
item_type: 'FG' | 'PT' | 'SM' | 'RM' | 'CS';
source_table: 'products' | 'materials';
absolute_path?: string;
is_active: boolean;
sections: ItemSection[]; // init 응답에 포함
}
```
### 3.2 ItemSection
```typescript
interface ItemSection {
id: number;
tenant_id: number;
group_id: number;
title: string;
type: string;
order_no: number;
is_template: boolean;
is_default: boolean;
is_locked?: boolean; // 연결 잠금 상태
description?: string;
fields?: ItemField[];
bom_items?: ItemBomItem[];
}
```
### 3.3 ItemField
```typescript
interface ItemField {
id: number;
tenant_id: number;
group_id: number;
field_name: string;
field_key: string; // 저장 시 사용할 키
field_type: FieldType;
order_no: number;
is_required: boolean;
is_common: boolean;
is_active: boolean;
is_locked: boolean;
default_value?: string;
placeholder?: string;
display_condition?: object; // 조건부 표시
validation_rules?: object; // 유효성 검사 규칙
options?: object; // dropdown 옵션 등
properties?: object; // 추가 설정
category?: string;
description?: string;
}
type FieldType = 'textbox' | 'number' | 'dropdown' | 'checkbox' | 'date' | 'textarea';
```
### 3.4 EntityRelationship
```typescript
interface EntityRelationship {
id: number;
tenant_id: number;
group_id: number;
parent_type: 'page' | 'section';
parent_id: number;
child_type: 'section' | 'field' | 'bom';
child_id: number;
order_no: number;
is_locked: boolean;
locked_by?: number;
locked_at?: string;
metadata?: object;
}
```
---
## 4. 잠금(Lock) 기능
### 4.1 잠금의 의미
**연결이 잠기면:**
- 해당 연결(관계)를 해제할 수 없음
- 연결된 자식 엔티티를 삭제할 수 없음
- 이름 변경, 속성 추가 등 **비구조적 수정은 허용**
```
예시: page→section 연결이 잠김
├─ ❌ 섹션을 페이지에서 분리할 수 없음
├─ ❌ 해당 섹션을 삭제할 수 없음
├─ ✅ 섹션 제목 변경 가능
└─ ✅ 섹션에 새 필드 추가 가능
```
### 4.2 잠금 상태 확인
init API 응답에 `is_locked` 필드가 포함됩니다.
```json
{
"pages": [{
"id": 1,
"sections": [{
"id": 10,
"is_locked": true,
"fields": [{
"id": 100,
"is_locked": false
}]
}]
}]
}
```
### 4.3 잠금 관련 에러
```json
{
"success": false,
"message": "잠금된 연결은 해제할 수 없습니다.",
"error": "entity_protected_by_locked_relationship"
}
```
---
## 5. 필드 타입
| field_type | 설명 | 렌더링 컴포넌트 |
|------------|------|----------------|
| `textbox` | 텍스트 입력 | `<Input type="text" />` |
| `number` | 숫자 입력 | `<Input type="number" />` |
| `dropdown` | 드롭다운 선택 | `<Select />` |
| `checkbox` | 체크박스 | `<Checkbox />` |
| `date` | 날짜 선택 | `<DatePicker />` |
| `textarea` | 장문 텍스트 | `<Textarea />` |
---
## 6. 주의사항
### 6.1 삭제 시 동작
- **페이지 삭제**: 연결된 섹션/필드는 삭제되지 않고 관계만 해제
- **섹션 삭제**: 연결된 필드/BOM은 삭제되지 않고 관계만 해제
- **잠금된 연결이 있으면**: 삭제/해제 불가
### 6.2 복제(Clone) 시 동작
- 섹션 복제: 섹션 + 필드 + BOM 모두 복제
- 필드 복제: 필드만 복제
- 복제된 항목은 독립 엔티티로 생성됨
### 6.3 순서 변경
- Drag & Drop 후 reorder API 호출 필요
- `items` 배열에 `{id, order_no}` 형태로 전달
---
## 7. 변경 이력
| 날짜 | 변경 내용 |
|------|----------|
| 2025-12-09 | 시스템 기반 문서 전면 재작성 |
| 2025-11-27 | 잠금(Lock) 기능 추가 |
| 2025-11-26 | 독립 엔티티 아키텍처 적용 |
| 2025-11-20 | entity_relationships 링크 테이블 도입 |

View File

@@ -0,0 +1,763 @@
# 품목기준관리(ItemMaster) & 품목관리(Items) API 문서
> 프론트엔드 개발자를 위한 API 스펙 문서
> 작성일: 2025-12-10
## 목차
1. [개요](#개요)
2. [품목관리 (Items) API](#품목관리-items-api)
3. [품목기준관리 (ItemMaster) API](#품목기준관리-itemmaster-api)
4. [공통 응답 형식](#공통-응답-형식)
5. [에러 처리](#에러-처리)
---
## 개요
### 품목관리 (Items) vs 품목기준관리 (ItemMaster)
| 구분 | 품목관리 (Items) | 품목기준관리 (ItemMaster) |
|------|------------------|---------------------------|
| **역할** | 실제 품목 데이터 CRUD | 품목 입력 화면/폼 구성 관리 |
| **대상 데이터** | products, materials 테이블 | item_pages, item_sections, item_fields 테이블 |
| **사용자** | 품목 등록/수정하는 일반 사용자 | 화면 구성을 설정하는 관리자 |
| **비유** | 엑셀 데이터 | 엑셀 양식(템플릿) |
### 품목 유형 코드 (item_type / product_type)
| 코드 | 설명 | 저장 테이블 |
|------|------|-------------|
| `FG` | 완제품 (Finished Goods) | products |
| `PT` | 반제품/부품 (Part) | products |
| `SM` | 반자재 (Semi-Material) | materials |
| `RM` | 원자재 (Raw Material) | materials |
| `CS` | 소모품 (Consumables) | materials |
---
## 품목관리 (Items) API
실제 품목 데이터를 조회/생성/수정/삭제하는 API입니다.
### 1. 통합 품목 목록 조회
**역할**: products와 materials를 통합하여 조회 (UNION 방식)
```
GET /api/v1/items
```
#### Request Parameters (Query)
| 파라미터 | 타입 | 필수 | 설명 |
|----------|------|------|------|
| `type` | string | N | 품목 유형 필터 (쉼표 구분). 기본값: `FG,PT,SM,RM,CS` |
| `search` 또는 `q` | string | N | 검색어 (코드, 이름, 태그) |
| `category_id` | integer | N | 카테고리 ID 필터 |
| `size` | integer | N | 페이지당 항목 수. 기본값: 20 |
| `page` | integer | N | 페이지 번호. 기본값: 1 |
| `include_deleted` | boolean | N | 삭제된 항목 포함 여부. 기본값: false |
#### Request 예시
```http
GET /api/v1/items?type=FG,PT&search=모터&size=10&page=1
```
#### Response
```json
{
"success": true,
"message": "조회되었습니다.",
"data": {
"data": [
{
"id": 1,
"item_type": "FG",
"code": "P-001",
"name": "완제품A",
"specification": null,
"unit": "EA",
"category_id": 5,
"type_code": "FG",
"created_at": "2025-01-01T00:00:00.000000Z",
"deleted_at": null,
"safety_stock": 100,
"lead_time": 7
}
],
"current_page": 1,
"per_page": 20,
"total": 150,
"last_page": 8
}
}
```
#### Response 필드 설명
| 필드 | 설명 |
|------|------|
| `id` | 품목 고유 ID (테이블별 독립) |
| `item_type` | 품목 유형 코드 |
| `code` | 품목 코드 |
| `name` | 품목명 |
| `specification` | 규격 (materials만 해당) |
| `unit` | 단위 (EA, KG, M 등) |
| `category_id` | 카테고리 ID |
| `type_code` | 품목 유형 코드 (item_type과 동일) |
| `created_at` | 생성일시 |
| `deleted_at` | 삭제일시 (Soft Delete) |
| `safety_stock` | 안전재고 (attributes에서 플랫 전개) |
| `lead_time` | 리드타임 (attributes에서 플랫 전개) |
> **Note**: `attributes` JSON 필드는 자동으로 최상위로 플랫 전개되어 반환됩니다.
---
### 2. 단일 품목 조회 (ID 기반)
**역할**: 특정 ID의 품목 상세 정보 조회 (가격 정보 옵션)
```
GET /api/v1/items/{id}
```
#### Path Parameters
| 파라미터 | 타입 | 필수 | 설명 |
|----------|------|------|------|
| `id` | integer | Y | 품목 ID |
#### Query Parameters
| 파라미터 | 타입 | 필수 | 설명 |
|----------|------|------|------|
| `item_type` | string | N | 품목 유형 코드. 기본값: `FG` |
| `include_price` | boolean | N | 가격 정보 포함 여부. 기본값: false |
| `client_id` | integer | N | 고객 ID (가격 조회 시) |
| `price_date` | string | N | 가격 기준일 (YYYY-MM-DD) |
#### Request 예시
```http
GET /api/v1/items/123?item_type=SM&include_price=true&client_id=5
```
#### Response
```json
{
"success": true,
"message": "조회되었습니다.",
"data": {
"id": 123,
"item_type": "SM",
"code": "M-001",
"name": "반자재A",
"specification": "10mm x 20mm",
"unit": "EA",
"category_id": 10,
"category": {
"id": 10,
"name": "철강류"
},
"type_code": "SM",
"prices": {
"sale": {
"unit_price": 15000,
"currency": "KRW",
"effective_from": "2025-01-01"
},
"purchase": {
"unit_price": 10000,
"currency": "KRW",
"effective_from": "2025-01-01"
}
}
}
}
```
---
### 3. 단일 품목 조회 (코드 기반)
**역할**: 품목 코드로 상세 정보 조회 (Product → Material 순서로 검색)
```
GET /api/v1/items/code/{code}
```
#### Path Parameters
| 파라미터 | 타입 | 필수 | 설명 |
|----------|------|------|------|
| `code` | string | Y | 품목 코드 |
#### Query Parameters
| 파라미터 | 타입 | 필수 | 설명 |
|----------|------|------|------|
| `include_bom` | boolean | N | BOM 정보 포함 여부. 기본값: false |
#### Request 예시
```http
GET /api/v1/items/code/P-001?include_bom=true
```
---
### 4. 품목 생성
**역할**: 새 품목 등록 (product_type에 따라 products 또는 materials에 저장)
```
POST /api/v1/items
```
#### Request Body
| 필드 | 타입 | 필수 | 설명 |
|------|------|------|------|
| `code` | string | Y | 품목 코드 (최대 50자, 중복 시 자동 증가) |
| `name` | string | Y | 품목명 (최대 255자) |
| `product_type` | string | Y | 품목 유형: `FG`, `PT`, `SM`, `RM`, `CS` |
| `unit` | string | Y | 단위 (최대 20자) |
| `category_id` | integer | N | 카테고리 ID |
| `description` | string | N | 설명 |
| `is_sellable` | boolean | N | 판매 가능 여부. 기본값: true |
| `is_purchasable` | boolean | N | 구매 가능 여부. 기본값: false |
| `is_producible` | boolean | N | 생산 가능 여부. 기본값: false |
| `safety_stock` | integer | N | 안전재고 (0 이상) |
| `lead_time` | integer | N | 리드타임 (0 이상) |
| `is_variable_size` | boolean | N | 가변 사이즈 여부 |
| `product_category` | string | N | 제품 분류 (최대 20자) |
| `part_type` | string | N | 부품 유형 (최대 20자) |
| `attributes` | object | N | 동적 필드 (JSON) |
| `material_code` | string | N | 자재 코드 (Material 전용) |
| `item_name` | string | N | 품명 (Material 전용) |
| `specification` | string | N | 규격 (Material 전용) |
| `is_inspection` | string | N | 검수 여부: `Y`, `N` |
| `search_tag` | string | N | 검색 태그 |
| `remarks` | string | N | 비고 |
| `options` | object | N | 옵션 (JSON) |
#### Request 예시
```json
{
"code": "P-NEW-001",
"name": "신규 완제품",
"product_type": "FG",
"unit": "EA",
"category_id": 5,
"is_sellable": true,
"safety_stock": 50,
"attributes": {
"color": "red",
"size": "L"
}
}
```
#### Response
```json
{
"success": true,
"message": "품목이 생성되었습니다.",
"data": {
"id": 999,
"code": "P-NEW-001",
"name": "신규 완제품",
"product_type": "FG",
"unit": "EA",
"category_id": 5,
"is_active": true,
"is_sellable": true,
"is_purchasable": false,
"is_producible": false,
"created_by": 1,
"created_at": "2025-12-10T10:00:00.000000Z"
}
}
```
> **중복 코드 처리**: 코드가 이미 존재하면 자동으로 증가합니다.
> - `P-001` 중복 → `P-002`
> - `ABC` 중복 → `ABC-001`
---
### 5. 품목 수정
**역할**: 기존 품목 정보 수정
```
PUT /api/v1/items/{id}
```
#### Path Parameters
| 파라미터 | 타입 | 필수 | 설명 |
|----------|------|------|------|
| `id` | integer | Y | 품목 ID |
#### Request Body
| 필드 | 타입 | 필수 | 설명 |
|------|------|------|------|
| `item_type` | string | Y | 품목 유형 (테이블 분기용) |
| `code` | string | N | 품목 코드 |
| `name` | string | N | 품목명 |
| ... | | | (생성과 동일한 필드, 모두 선택) |
#### Request 예시
```json
{
"item_type": "FG",
"name": "수정된 완제품명",
"safety_stock": 100
}
```
#### Response
```json
{
"success": true,
"message": "품목이 수정되었습니다.",
"data": {
"id": 999,
"code": "P-NEW-001",
"name": "수정된 완제품명",
"safety_stock": 100,
"updated_by": 1,
"updated_at": "2025-12-10T11:00:00.000000Z"
}
}
```
---
### 6. 품목 삭제 (Soft Delete)
**역할**: 품목 삭제 (BOM 구성품으로 사용 중이면 삭제 불가)
```
DELETE /api/v1/items/{id}
```
#### Path Parameters
| 파라미터 | 타입 | 필수 | 설명 |
|----------|------|------|------|
| `id` | integer | Y | 품목 ID |
#### Query Parameters
| 파라미터 | 타입 | 필수 | 설명 |
|----------|------|------|------|
| `item_type` | string | N | 품목 유형. 기본값: `FG` |
#### Response
```json
{
"success": true,
"message": "품목이 삭제되었습니다.",
"data": "success"
}
```
#### 에러 Response (BOM 사용 중)
```json
{
"success": false,
"message": "해당 품목은 3건의 BOM에서 구성품으로 사용 중입니다."
}
```
---
### 7. 품목 일괄 삭제
**역할**: 여러 품목 일괄 삭제
```
DELETE /api/v1/items/batch
```
#### Request Body
| 필드 | 타입 | 필수 | 설명 |
|------|------|------|------|
| `item_type` | string | Y | 품목 유형 |
| `ids` | array | Y | 삭제할 품목 ID 배열 |
#### Request 예시
```json
{
"item_type": "FG",
"ids": [1, 2, 3, 4, 5]
}
```
---
## 품목기준관리 (ItemMaster) API
품목 입력 화면의 구성(페이지, 섹션, 필드)을 관리하는 API입니다.
### 구조 개요
```
ItemMaster 계층 구조:
Page (페이지)
├── Section (섹션) - fields 타입
│ ├── Field (필드)
│ ├── Field (필드)
│ └── Field (필드)
└── Section (섹션) - bom 타입
├── BomItem (BOM 항목)
└── BomItem (BOM 항목)
```
- **Page**: 품목 유형별 입력 화면 (예: 완제품 등록 페이지)
- **Section**: 페이지 내 영역 구분 (예: 기본정보, BOM 구성)
- **Field**: 입력 필드 정의 (예: 품목코드, 품목명)
- **BomItem**: BOM 섹션의 구성품 정의
### 1. 초기화 데이터 로드
**역할**: 프론트엔드 앱 초기화 시 필요한 전체 ItemMaster 데이터 로드
```
GET /api/v1/item-master/init
```
#### Response
```json
{
"success": true,
"message": "조회되었습니다.",
"data": {
"pages": [
{
"id": 1,
"tenant_id": 1,
"group_id": null,
"page_name": "완제품 등록",
"item_type": "FG",
"absolute_path": "/items/fg/create",
"is_active": true,
"sections": [
{
"id": 10,
"title": "기본정보",
"type": "fields",
"order_no": 1,
"is_locked": false,
"fields": [
{
"id": 100,
"field_name": "품목코드",
"field_key": "code",
"field_type": "textbox",
"is_required": true,
"order_no": 1,
"is_locked": true
}
]
}
]
}
],
"sections": [...],
"fields": [...],
"customTabs": [...],
"unitOptions": [...]
}
}
```
#### Response 필드 설명
| 필드 | 설명 |
|------|------|
| `pages` | 페이지 목록 (섹션, 필드 중첩 포함) |
| `sections` | 모든 독립 섹션 목록 (재사용 가능) |
| `fields` | 모든 독립 필드 목록 (재사용 가능) |
| `customTabs` | 커스텀 탭 목록 (컬럼 설정 포함) |
| `unitOptions` | 단위 옵션 목록 |
---
### 2. 페이지 API
#### 페이지 목록 조회
```
GET /api/v1/item-master/pages
```
| Query 파라미터 | 타입 | 필수 | 설명 |
|----------------|------|------|------|
| `item_type` | string | N | 품목 유형 필터 |
#### 페이지 생성
```
POST /api/v1/item-master/pages
```
| Request Body | 타입 | 필수 | 설명 |
|--------------|------|------|------|
| `page_name` | string | Y | 페이지명 (최대 255자) |
| `item_type` | string | Y | 품목 유형: `FG`, `PT`, `SM`, `RM`, `CS` |
| `absolute_path` | string | N | 절대 경로 (최대 500자) |
#### 페이지 수정
```
PUT /api/v1/item-master/pages/{id}
```
#### 페이지 삭제
```
DELETE /api/v1/item-master/pages/{id}
```
---
### 3. 섹션 API
#### 독립 섹션 목록 조회
```
GET /api/v1/item-master/sections
```
| Query 파라미터 | 타입 | 필수 | 설명 |
|----------------|------|------|------|
| `is_template` | boolean | N | 템플릿 섹션 필터 |
#### 독립 섹션 생성 (페이지 연결 없음)
```
POST /api/v1/item-master/sections
```
| Request Body | 타입 | 필수 | 설명 |
|--------------|------|------|------|
| `group_id` | integer | N | 계층 번호 |
| `title` | string | Y | 섹션 제목 (최대 255자) |
| `type` | string | Y | 섹션 타입: `fields`, `bom` |
#### 페이지에 섹션 생성 (연결)
```
POST /api/v1/item-master/pages/{pageId}/sections
```
#### 섹션 복제
```
POST /api/v1/item-master/sections/{id}/clone
```
#### 섹션 사용처 조회
```
GET /api/v1/item-master/sections/{id}/usage
```
**역할**: 해당 섹션이 어떤 페이지에서 사용되고 있는지 조회
#### 섹션 수정
```
PUT /api/v1/item-master/sections/{id}
```
#### 섹션 삭제
```
DELETE /api/v1/item-master/sections/{id}
```
#### 섹션 순서 변경
```
PUT /api/v1/item-master/pages/{pageId}/sections/reorder
```
| Request Body | 타입 | 필수 | 설명 |
|--------------|------|------|------|
| `items` | array | Y | `[{id: 1, order_no: 1}, {id: 2, order_no: 2}]` |
---
### 4. 필드 API
#### 독립 필드 목록 조회
```
GET /api/v1/item-master/fields
```
#### 독립 필드 생성 (섹션 연결 없음)
```
POST /api/v1/item-master/fields
```
| Request Body | 타입 | 필수 | 설명 |
|--------------|------|------|------|
| `group_id` | integer | N | 계층 번호 |
| `field_name` | string | Y | 필드 표시명 (최대 255자) |
| `field_key` | string | N | 필드 키 (최대 80자, 영문 시작) |
| `field_type` | string | Y | 필드 타입 (아래 참조) |
| `is_required` | boolean | N | 필수 입력 여부 |
| `default_value` | string | N | 기본값 |
| `placeholder` | string | N | 플레이스홀더 (최대 255자) |
| `display_condition` | object | N | 표시 조건 (JSON) |
| `validation_rules` | object | N | 검증 규칙 (JSON) |
| `options` | array | N | 드롭다운 옵션 등 (JSON) |
| `properties` | object | N | 추가 속성 (JSON) |
| `is_locked` | boolean | N | 잠금 여부 |
#### 필드 타입 (field_type)
| 타입 | 설명 |
|------|------|
| `textbox` | 한 줄 텍스트 입력 |
| `number` | 숫자 입력 |
| `dropdown` | 드롭다운 선택 |
| `checkbox` | 체크박스 |
| `date` | 날짜 선택 |
| `textarea` | 여러 줄 텍스트 입력 |
#### 섹션에 필드 생성 (연결)
```
POST /api/v1/item-master/sections/{sectionId}/fields
```
#### 필드 복제
```
POST /api/v1/item-master/fields/{id}/clone
```
#### 필드 사용처 조회
```
GET /api/v1/item-master/fields/{id}/usage
```
**역할**: 해당 필드가 어떤 섹션에서 사용되고 있는지 조회
#### 필드 수정
```
PUT /api/v1/item-master/fields/{id}
```
#### 필드 삭제
```
DELETE /api/v1/item-master/fields/{id}
```
---
## 공통 응답 형식
모든 API는 동일한 응답 구조를 따릅니다.
### 성공 응답
```json
{
"success": true,
"message": "처리되었습니다.",
"data": { ... }
}
```
### 페이지네이션 응답
```json
{
"success": true,
"message": "조회되었습니다.",
"data": {
"data": [...],
"current_page": 1,
"per_page": 20,
"total": 100,
"last_page": 5,
"from": 1,
"to": 20
}
}
```
---
## 에러 처리
### 에러 응답 형식
```json
{
"success": false,
"message": "에러 메시지",
"errors": {
"field_name": ["검증 오류 메시지"]
}
}
```
### 주요 HTTP 상태 코드
| 코드 | 설명 |
|------|------|
| `200` | 성공 |
| `201` | 생성 성공 |
| `400` | 잘못된 요청 (검증 실패, 중복 코드 등) |
| `401` | 인증 필요 |
| `403` | 권한 없음 |
| `404` | 리소스 없음 |
| `422` | 검증 실패 (Validation Error) |
| `500` | 서버 오류 |
### 주요 에러 케이스
| 상황 | 메시지 예시 |
|------|-------------|
| 품목 없음 | "해당 품목을 찾을 수 없습니다." |
| 코드 중복 | "이미 사용 중인 품목코드입니다." |
| BOM 사용 중 | "해당 품목은 N건의 BOM에서 구성품으로 사용 중입니다." |
| 필수 필드 누락 | "품목코드는 필수입니다." |
| 잘못된 품목 유형 | "품목 유형은 FG, PT, SM, RM, CS 중 하나여야 합니다." |
---
## 인증
모든 API는 다음 인증이 필요합니다:
1. **API Key**: `X-API-KEY` 헤더
2. **Bearer Token**: `Authorization: Bearer {token}` 헤더
```http
GET /api/v1/items
X-API-KEY: your-api-key
Authorization: Bearer your-access-token
```
---
## 변경 이력
| 날짜 | 버전 | 변경 내용 |
|------|------|----------|
| 2025-12-10 | 1.0 | 최초 작성 |

View File

@@ -267,7 +267,7 @@ sh 'export NODE_OPTIONS="--max-old-space-size=2048" && npm run build'
- [운영 환경 배포 계획서](../plans/production-deployment-plan.md) - Jenkinsfile 상세, 브랜치 전략
- [.env 동기화 절차](production-env-sync.md) - 환경 변수 분리
- [Docker 환경 스펙](../specs/docker-setup.md) - 현재 개발 환경
- [Docker 환경 스펙](../system/docker-setup.md) - 현재 개발 환경
---

View File

@@ -263,7 +263,7 @@ fastcgi_param SCRIPT_FILENAME /var/www/mng/public$fastcgi_script_name;
| 문서 | 설명 |
|------|------|
| [docker-setup.md](../specs/docker-setup.md) | Docker 환경 설정값 상세 |
| [docker-setup.md](../system/docker-setup.md) | Docker 환경 설정값 상세 |
---

View File

@@ -265,7 +265,7 @@ docker exec sam-mng-1 supervisorctl status
| 문서 | 설명 |
|------|------|
| [docker-setup.md](../specs/docker-setup.md) | Docker 환경 설정값 상세 |
| [docker-setup.md](../system/docker-setup.md) | Docker 환경 설정값 상세 |
---

View File

@@ -306,8 +306,8 @@ docker exec sam-mng-1 php artisan config:clear
## 관련 문서
- [Docker 환경 구성](../specs/docker-setup.md)
- [시스템 아키텍처](../architecture/system-overview.md)
- [Docker 환경 구성](../system/docker-setup.md)
- [시스템 아키텍처](../system/overview.md)
- [바로빌 카카오톡 연동](../features/barobill-kakaotalk/README.md)
---

View File

@@ -249,8 +249,8 @@ API 호출 시 → Next.js API Route 프록시 → api.sam.kr
| 문서 | 설명 |
|------|------|
| [docker-setup.md](../specs/docker-setup.md) | Docker 환경 설정값 상세 |
| [system-overview.md](../architecture/system-overview.md) | 시스템 아키텍처 레퍼런스 |
| [docker-setup.md](../system/docker-setup.md) | Docker 환경 설정값 상세 |
| [overview.md](../system/overview.md) | 시스템 아키텍처 레퍼런스 |
| [production-deployment-plan.md](../plans/production-deployment-plan.md) | 운영 배포 계획 |
| [dev-commands.md](../quickstart/dev-commands.md) | 개발 명령어 모음 |

View File

@@ -237,8 +237,8 @@ php artisan l5-swagger:generate
## 관련 문서
- [API 개발 규칙](./api_rules.md)
- [개발 명령어](./dev_commands.md)
- [API 개발 규칙](../standards/api-rules.md)
- [개발 명령어](../quickstart/dev-commands.md)
---