Files
sam-manage/resources/views/dev-tools/flow-tester/partials/example-flows.blade.php
hskwon 5c892c1ed9 브라우저 alert/confirm을 SweetAlert2로 전환
- layouts/app.blade.php에 SweetAlert2 CDN 및 전역 헬퍼 함수 추가
  - showToast(): 토스트 알림 (success, error, warning, info)
  - showConfirm(): 확인 대화상자
  - showDeleteConfirm(): 삭제 확인 (경고 아이콘)
  - showPermanentDeleteConfirm(): 영구 삭제 확인 (빨간색 경고)
  - showSuccess(), showError(): 성공/에러 알림

- 변환된 파일 목록 (48개 Blade 파일):
  - menus/* (6개), boards/* (2개), posts/* (3개)
  - daily-logs/* (3개), project-management/* (6개)
  - dev-tools/flow-tester/* (6개)
  - quote-formulas/* (4개), permission-analyze/* (1개)
  - archived-records/* (1개), profile/* (1개)
  - roles/* (3개), permissions/* (3개)
  - departments/* (3개), tenants/* (3개), users/* (3개)

- 주요 개선사항:
  - Tailwind CSS 테마와 일관된 디자인
  - 비동기 콜백 패턴으로 리팩토링
  - 삭제/복원/영구삭제 각각 다른 스타일 적용
2025-12-05 09:49:56 +09:00

889 lines
36 KiB
PHP

{{-- 예제 플로우 모달 --}}
<div id="examples-modal"
class="hidden fixed inset-0 z-50 overflow-y-auto"
aria-labelledby="examples-modal-title"
role="dialog"
aria-modal="true">
{{-- 배경 오버레이 --}}
<div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
onclick="ExamplesModal.close()"></div>
{{-- 모달 컨테이너 --}}
<div class="flex min-h-full items-start justify-center p-4 pt-8">
<div class="relative bg-white rounded-lg shadow-xl w-full max-w-5xl max-h-[90vh] flex flex-col">
{{-- 모달 헤더 --}}
<div class="flex items-center justify-between px-6 py-4 border-b border-gray-200 flex-shrink-0">
<h2 id="examples-modal-title" class="text-xl font-semibold text-gray-800">
예제 플로우
</h2>
<button type="button"
onclick="ExamplesModal.close()"
class="text-gray-400 hover:text-gray-600 transition-colors">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
{{-- 예제 선택 --}}
<div class="px-6 py-3 border-b border-gray-200 flex-shrink-0">
<div class="flex gap-2 flex-wrap">
<button type="button" class="example-tab active px-4 py-2 text-sm font-medium rounded-lg transition-colors" data-example="item-master-full">
Item Master (전체)
</button>
<button type="button" class="example-tab px-4 py-2 text-sm font-medium rounded-lg transition-colors" data-example="item-master-simple">
Item Master (기본)
</button>
<button type="button" class="example-tab px-4 py-2 text-sm font-medium rounded-lg transition-colors" data-example="auth-flow">
인증 플로우
</button>
<button type="button" class="example-tab px-4 py-2 text-sm font-medium rounded-lg transition-colors bg-green-100 text-green-800" data-example="items-crud">
품목 CRUD (Faker)
</button>
<button type="button" class="example-tab px-4 py-2 text-sm font-medium rounded-lg transition-colors bg-green-100 text-green-800" data-example="items-search">
품목 검색/통계
</button>
<button type="button" class="example-tab px-4 py-2 text-sm font-medium rounded-lg transition-colors bg-green-100 text-green-800" data-example="items-bom">
품목 BOM 관리
</button>
</div>
</div>
{{-- 모달 콘텐츠 --}}
<div class="flex-1 overflow-y-auto p-6">
{{-- Item Master 전체 플로우 --}}
<div id="example-item-master-full" class="example-content">
<div class="mb-4">
<h3 class="text-lg font-semibold text-gray-800">Item Master 전체 테스트 (18 스텝)</h3>
<p class="text-sm text-gray-600 mt-1">페이지, 섹션, 필드의 CRUD + 연결/해제 전체 시나리오</p>
</div>
<div class="relative">
<button onclick="ExamplesModal.copy('item-master-full')"
class="absolute top-2 right-2 px-3 py-1 text-xs bg-gray-700 hover:bg-gray-600 text-white rounded transition-colors z-10">
복사
</button>
<pre id="json-item-master-full" class="bg-gray-800 text-gray-100 p-4 rounded-lg text-xs overflow-x-auto max-h-[60vh]"></pre>
</div>
</div>
{{-- Item Master 기본 플로우 --}}
<div id="example-item-master-simple" class="example-content hidden">
<div class="mb-4">
<h3 class="text-lg font-semibold text-gray-800">Item Master 기본 테스트 (3 스텝)</h3>
<p class="text-sm text-gray-600 mt-1">페이지 생성 조회 삭제 기본 CRUD</p>
</div>
<div class="relative">
<button onclick="ExamplesModal.copy('item-master-simple')"
class="absolute top-2 right-2 px-3 py-1 text-xs bg-gray-700 hover:bg-gray-600 text-white rounded transition-colors z-10">
복사
</button>
<pre id="json-item-master-simple" class="bg-gray-800 text-gray-100 p-4 rounded-lg text-xs overflow-x-auto max-h-[60vh]"></pre>
</div>
</div>
{{-- 인증 플로우 --}}
<div id="example-auth-flow" class="example-content hidden">
<div class="mb-4">
<h3 class="text-lg font-semibold text-gray-800">인증 플로우 테스트 (4 스텝)</h3>
<p class="text-sm text-gray-600 mt-1">로그인 프로필 조회 토큰 갱신 로그아웃</p>
</div>
<div class="relative">
<button onclick="ExamplesModal.copy('auth-flow')"
class="absolute top-2 right-2 px-3 py-1 text-xs bg-gray-700 hover:bg-gray-600 text-white rounded transition-colors z-10">
복사
</button>
<pre id="json-auth-flow" class="bg-gray-800 text-gray-100 p-4 rounded-lg text-xs overflow-x-auto max-h-[60vh]"></pre>
</div>
</div>
{{-- 품목 CRUD (Faker) --}}
<div id="example-items-crud" class="example-content hidden">
<div class="mb-4">
<h3 class="text-lg font-semibold text-gray-800">품목 CRUD 테스트 - Faker 활용 (6 스텝)</h3>
<p class="text-sm text-gray-600 mt-1">Faker 변수로 랜덤 데이터 생성 - 품목 생성 - 조회 - 수정 - 삭제</p>
<div class="mt-2 p-2 bg-green-50 border border-green-200 rounded text-xs text-green-800">
<strong>Faker 활용:</strong> <code>@{{$faker.itemCode:TEST}}</code>, <code>@{{$faker.productName}}</code>, <code>@{{$faker.price:1000:50000}}</code>
</div>
</div>
<div class="relative">
<button onclick="ExamplesModal.copy('items-crud')"
class="absolute top-2 right-2 px-3 py-1 text-xs bg-gray-700 hover:bg-gray-600 text-white rounded transition-colors z-10">
복사
</button>
<pre id="json-items-crud" class="bg-gray-800 text-gray-100 p-4 rounded-lg text-xs overflow-x-auto max-h-[60vh]"></pre>
</div>
</div>
{{-- 품목 검색/통계 --}}
<div id="example-items-search" class="example-content hidden">
<div class="mb-4">
<h3 class="text-lg font-semibold text-gray-800">품목 검색/통계 테스트 (5 스텝)</h3>
<p class="text-sm text-gray-600 mt-1">목록 조회 - 검색 (키워드/필터) - 통계 조회</p>
</div>
<div class="relative">
<button onclick="ExamplesModal.copy('items-search')"
class="absolute top-2 right-2 px-3 py-1 text-xs bg-gray-700 hover:bg-gray-600 text-white rounded transition-colors z-10">
복사
</button>
<pre id="json-items-search" class="bg-gray-800 text-gray-100 p-4 rounded-lg text-xs overflow-x-auto max-h-[60vh]"></pre>
</div>
</div>
{{-- 품목 BOM 관리 --}}
<div id="example-items-bom" class="example-content hidden">
<div class="mb-4">
<h3 class="text-lg font-semibold text-gray-800">품목 BOM 관리 테스트 (8 스텝)</h3>
<p class="text-sm text-gray-600 mt-1">부모 품목 생성 - 자재 생성 - BOM 등록 - BOM 조회 - BOM 수정 - 정리</p>
<div class="mt-2 p-2 bg-blue-50 border border-blue-200 rounded text-xs text-blue-800">
<strong>BOM:</strong> Bill of Materials - 제품 구성 자재 목록 관리
</div>
</div>
<div class="relative">
<button onclick="ExamplesModal.copy('items-bom')"
class="absolute top-2 right-2 px-3 py-1 text-xs bg-gray-700 hover:bg-gray-600 text-white rounded transition-colors z-10">
복사
</button>
<pre id="json-items-bom" class="bg-gray-800 text-gray-100 p-4 rounded-lg text-xs overflow-x-auto max-h-[60vh]"></pre>
</div>
</div>
</div>
{{-- 모달 푸터 --}}
<div class="flex justify-end px-6 py-4 border-t border-gray-200 flex-shrink-0">
<button type="button"
onclick="ExamplesModal.close()"
class="px-4 py-2 bg-gray-600 hover:bg-gray-700 text-white rounded-lg transition-colors">
닫기
</button>
</div>
</div>
</div>
</div>
<style>
.example-tab {
background-color: #f3f4f6;
color: #374151;
}
.example-tab:hover {
background-color: #e5e7eb;
}
.example-tab.active {
background-color: #7c3aed;
color: white;
}
</style>
@verbatim
<script>
const ExampleFlows = {
'item-master-full': {
"version": "1.0",
"meta": {
"author": "SAM Admin",
"description": "품목관리 API 통합 테스트 - CRUD 및 연결/해제 검증",
"tags": ["item-master", "integration", "full-test"]
},
"config": {
"baseUrl": "https://sam.kr/api/v1",
"timeout": 30000,
"stopOnFailure": false,
"headers": {
"Accept": "application/json",
"Content-Type": "application/json"
}
},
"variables": {
"testPrefix": "TEST_",
"timestamp": "{{$timestamp}}"
},
"steps": [
{
"id": "page_create_1",
"name": "[페이지] 1차 생성",
"description": "테스트 페이지 최초 생성",
"method": "POST",
"endpoint": "/item-master/pages",
"body": {
"page_name": "{{variables.testPrefix}}Page_{{variables.timestamp}}",
"item_type": "PRODUCT",
"description": "테스트용 페이지 (삭제 예정)"
},
"expect": {
"status": [200, 201],
"jsonPath": { "$.success": true, "$.data.id": "@isNumber" }
},
"extract": { "tempPageId": "$.data.id" }
},
{
"id": "page_delete_1",
"name": "[페이지] 1차 삭제",
"dependsOn": ["page_create_1"],
"method": "DELETE",
"endpoint": "/item-master/pages/{{page_create_1.tempPageId}}",
"expect": { "status": [200, 204] }
},
{
"id": "page_create_2",
"name": "[페이지] 2차 생성 (유지)",
"dependsOn": ["page_delete_1"],
"method": "POST",
"endpoint": "/item-master/pages",
"body": {
"page_name": "{{variables.testPrefix}}MainPage_{{variables.timestamp}}",
"item_type": "PRODUCT",
"description": "통합 테스트 메인 페이지"
},
"expect": { "status": [200, 201] },
"extract": { "mainPageId": "$.data.id", "mainPageName": "$.data.page_name" }
},
{
"id": "section_create_1",
"name": "[섹션] 독립 1차 생성",
"dependsOn": ["page_create_2"],
"method": "POST",
"endpoint": "/item-master/sections",
"body": { "title": "{{variables.testPrefix}}Section_Temp", "type": "form" },
"expect": { "status": [200, 201] },
"extract": { "tempSectionId": "$.data.id" }
},
{
"id": "section_delete_1",
"name": "[섹션] 1차 삭제",
"dependsOn": ["section_create_1"],
"method": "DELETE",
"endpoint": "/item-master/sections/{{section_create_1.tempSectionId}}",
"expect": { "status": [200, 204] }
},
{
"id": "section_create_2",
"name": "[섹션] 독립 2차 생성 (유지)",
"dependsOn": ["section_delete_1"],
"method": "POST",
"endpoint": "/item-master/sections",
"body": { "title": "{{variables.testPrefix}}MainSection_{{variables.timestamp}}", "type": "form" },
"expect": { "status": [200, 201] },
"extract": { "mainSectionId": "$.data.id" }
},
{
"id": "field_create_1",
"name": "[필드] 독립 1차 생성",
"dependsOn": ["section_create_2"],
"method": "POST",
"endpoint": "/item-master/fields",
"body": { "field_name": "{{variables.testPrefix}}Field_Temp", "field_type": "text", "is_required": false },
"expect": { "status": [200, 201] },
"extract": { "tempFieldId": "$.data.id" }
},
{
"id": "field_delete_1",
"name": "[필드] 1차 삭제",
"dependsOn": ["field_create_1"],
"method": "DELETE",
"endpoint": "/item-master/fields/{{field_create_1.tempFieldId}}",
"expect": { "status": [200, 204] }
},
{
"id": "field_create_2",
"name": "[필드] 독립 2차 생성 (유지)",
"dependsOn": ["field_delete_1"],
"method": "POST",
"endpoint": "/item-master/fields",
"body": { "field_name": "{{variables.testPrefix}}MainField_{{variables.timestamp}}", "field_type": "text", "is_required": true },
"expect": { "status": [200, 201] },
"extract": { "mainFieldId": "$.data.id" }
},
{
"id": "page_link_section_1",
"name": "[연결] 페이지에 섹션 연결",
"dependsOn": ["field_create_2"],
"method": "POST",
"endpoint": "/item-master/pages/{{page_create_2.mainPageId}}/link-section",
"body": { "section_id": "{{section_create_2.mainSectionId}}", "sort_order": 1 },
"expect": { "status": [200, 201] }
},
{
"id": "page_unlink_section",
"name": "[연결] 페이지-섹션 연결 해제",
"dependsOn": ["page_link_section_1"],
"method": "DELETE",
"endpoint": "/item-master/pages/{{page_create_2.mainPageId}}/unlink-section/{{section_create_2.mainSectionId}}",
"expect": { "status": [200, 204] }
},
{
"id": "page_link_section_2",
"name": "[연결] 페이지에 섹션 재연결",
"dependsOn": ["page_unlink_section"],
"method": "POST",
"endpoint": "/item-master/pages/{{page_create_2.mainPageId}}/link-section",
"body": { "section_id": "{{section_create_2.mainSectionId}}", "sort_order": 1 },
"expect": { "status": [200, 201] }
},
{
"id": "section_link_field_1",
"name": "[연결] 섹션에 필드 연결",
"dependsOn": ["page_link_section_2"],
"method": "POST",
"endpoint": "/item-master/sections/{{section_create_2.mainSectionId}}/link-field",
"body": { "field_id": "{{field_create_2.mainFieldId}}", "sort_order": 1 },
"expect": { "status": [200, 201] }
},
{
"id": "section_unlink_field",
"name": "[연결] 섹션-필드 연결 해제",
"dependsOn": ["section_link_field_1"],
"method": "DELETE",
"endpoint": "/item-master/sections/{{section_create_2.mainSectionId}}/unlink-field/{{field_create_2.mainFieldId}}",
"expect": { "status": [200, 204] }
},
{
"id": "cleanup_field",
"name": "[정리] 필드 삭제",
"dependsOn": ["section_unlink_field"],
"method": "DELETE",
"endpoint": "/item-master/fields/{{field_create_2.mainFieldId}}",
"expect": { "status": [200, 204] },
"continueOnFailure": true
},
{
"id": "cleanup_section",
"name": "[정리] 섹션 삭제",
"dependsOn": ["cleanup_field"],
"method": "DELETE",
"endpoint": "/item-master/sections/{{section_create_2.mainSectionId}}",
"expect": { "status": [200, 204] },
"continueOnFailure": true
},
{
"id": "cleanup_page",
"name": "[정리] 페이지 삭제",
"dependsOn": ["cleanup_section"],
"method": "DELETE",
"endpoint": "/item-master/pages/{{page_create_2.mainPageId}}",
"expect": { "status": [200, 204] },
"continueOnFailure": true
}
]
},
'item-master-simple': {
"version": "1.0",
"config": {
"baseUrl": "https://sam.kr/api/v1",
"timeout": 30000,
"stopOnFailure": true
},
"variables": {
"testPrefix": "SIMPLE_"
},
"steps": [
{
"id": "create_page",
"name": "페이지 생성",
"method": "POST",
"endpoint": "/item-master/pages",
"body": { "page_name": "{{variables.testPrefix}}Page_{{$timestamp}}", "item_type": "PRODUCT" },
"expect": { "status": [200, 201] },
"extract": { "pageId": "$.data.id" }
},
{
"id": "get_page",
"name": "페이지 조회",
"dependsOn": ["create_page"],
"method": "GET",
"endpoint": "/item-master/pages",
"expect": { "status": [200], "jsonPath": { "$.success": true } }
},
{
"id": "delete_page",
"name": "페이지 삭제",
"dependsOn": ["get_page"],
"method": "DELETE",
"endpoint": "/item-master/pages/{{create_page.pageId}}",
"expect": { "status": [200, 204] }
}
]
},
'auth-flow': {
"name": "인증 플로우 테스트",
"description": "로그인, 프로필 조회, 토큰 갱신, 로그아웃 플로우를 테스트합니다.",
"version": "1.0",
"config": {
"baseUrl": "https://api.sam.kr/api/v1",
"apiKey": "YOUR_API_KEY_HERE",
"timeout": 30000,
"stopOnFailure": true
},
"variables": {
"user_id": "{{$env.FLOW_TESTER_USER_ID}}",
"user_pwd": "{{$env.FLOW_TESTER_USER_PWD}}"
},
"steps": [
{
"id": "login",
"name": "로그인",
"method": "POST",
"endpoint": "/login",
"body": {
"user_id": "{{variables.user_id}}",
"user_pwd": "{{variables.user_pwd}}"
},
"expect": {
"status": [200],
"jsonPath": {
"$.access_token": "@isString"
}
},
"extract": {
"accessToken": "$.access_token",
"refreshToken": "$.refresh_token"
}
},
{
"id": "get_profile",
"name": "프로필 조회",
"dependsOn": ["login"],
"method": "GET",
"endpoint": "/users/me",
"headers": {
"Authorization": "Bearer {{login.accessToken}}"
},
"expect": {
"status": [200],
"jsonPath": {
"$.success": true
}
}
},
{
"id": "refresh_token",
"name": "토큰 갱신",
"dependsOn": ["get_profile"],
"method": "POST",
"endpoint": "/refresh",
"body": {
"refresh_token": "{{login.refreshToken}}"
},
"expect": {
"status": [200],
"jsonPath": {
"$.access_token": "@isString"
}
},
"extract": {
"newToken": "$.access_token"
}
},
{
"id": "logout",
"name": "로그아웃",
"dependsOn": ["refresh_token"],
"method": "POST",
"endpoint": "/logout",
"headers": {
"Authorization": "Bearer {{refresh_token.newToken}}"
},
"expect": {
"status": [200, 204]
}
}
]
},
'items-crud': {
"version": "1.0",
"meta": {
"name": "품목 CRUD 테스트 (Faker)",
"description": "Faker 변수를 활용한 품목 생성/조회/수정/삭제 테스트",
"tags": ["items", "crud", "faker"]
},
"config": {
"baseUrl": "https://sam.kr/api/v1",
"timeout": 30000,
"stopOnFailure": false,
"headers": {
"Accept": "application/json",
"Content-Type": "application/json"
}
},
"variables": {},
"steps": [
{
"id": "create_item",
"name": "[품목] Faker로 생성",
"description": "Faker 변수를 사용해 랜덤 품목 데이터 생성",
"method": "POST",
"endpoint": "/items",
"body": {
"code": "{{$faker.itemCode:TEST}}",
"name": "{{$faker.productName}}",
"item_type": "PRODUCT",
"unit": "{{$faker.unit}}",
"unit_price": "{{$faker.price:1000:50000}}",
"description": "{{$faker.sentence}}",
"is_active": true
},
"expect": {
"status": [200, 201],
"jsonPath": {
"$.success": true,
"$.data.code": "@isString",
"$.data.name": "@notEmpty"
}
},
"extract": {
"itemCode": "$.data.code",
"itemId": "$.data.id",
"itemName": "$.data.name"
}
},
{
"id": "get_item_by_code",
"name": "[품목] 코드로 조회",
"dependsOn": ["create_item"],
"method": "GET",
"endpoint": "/items/code/{{create_item.itemCode}}",
"expect": {
"status": [200],
"jsonPath": {
"$.success": true,
"$.data.code": "{{create_item.itemCode}}"
}
}
},
{
"id": "update_item",
"name": "[품목] 수정 (코드 기반)",
"dependsOn": ["get_item_by_code"],
"method": "PUT",
"endpoint": "/items/{{create_item.itemCode}}",
"body": {
"name": "{{$faker.productName}} (수정됨)",
"unit_price": "{{$faker.price:50000:100000}}",
"description": "수정된 설명 - {{$faker.sentence}}"
},
"expect": {
"status": [200],
"jsonPath": {
"$.success": true
}
}
},
{
"id": "verify_update",
"name": "[품목] 수정 확인",
"dependsOn": ["update_item"],
"method": "GET",
"endpoint": "/items/code/{{create_item.itemCode}}",
"expect": {
"status": [200],
"jsonPath": {
"$.data.description": "@contains:수정된 설명"
}
}
},
{
"id": "delete_item",
"name": "[품목] 삭제 (코드 기반)",
"dependsOn": ["verify_update"],
"method": "DELETE",
"endpoint": "/items/{{create_item.itemCode}}",
"expect": {
"status": [200, 204]
}
},
{
"id": "verify_delete",
"name": "[품목] 삭제 확인",
"dependsOn": ["delete_item"],
"method": "GET",
"endpoint": "/items/code/{{create_item.itemCode}}",
"expect": {
"status": [404]
},
"continueOnFailure": true
}
]
},
'items-search': {
"version": "1.0",
"meta": {
"name": "품목 검색/통계 테스트",
"description": "품목 목록 조회, 검색, 통계 API 테스트",
"tags": ["items", "search", "stats"]
},
"config": {
"baseUrl": "https://sam.kr/api/v1",
"timeout": 30000,
"stopOnFailure": false
},
"variables": {
"searchKeyword": "부품"
},
"steps": [
{
"id": "list_items",
"name": "[목록] 전체 품목 조회",
"method": "GET",
"endpoint": "/items",
"expect": {
"status": [200],
"jsonPath": {
"$.success": true,
"$.data": "@isArray"
}
},
"extract": {
"totalCount": "$.meta.total"
}
},
{
"id": "list_items_paginated",
"name": "[목록] 페이지네이션",
"dependsOn": ["list_items"],
"method": "GET",
"endpoint": "/items?page=1&per_page=10",
"expect": {
"status": [200],
"jsonPath": {
"$.success": true,
"$.meta.per_page": 10
}
}
},
{
"id": "search_by_keyword",
"name": "[검색] 키워드 검색",
"dependsOn": ["list_items_paginated"],
"method": "GET",
"endpoint": "/items/search?keyword={{variables.searchKeyword}}",
"expect": {
"status": [200],
"jsonPath": {
"$.success": true
}
},
"extract": {
"searchCount": "$.meta.total"
}
},
{
"id": "search_by_type",
"name": "[검색] 품목유형 필터",
"dependsOn": ["search_by_keyword"],
"method": "GET",
"endpoint": "/items/search?item_type=PRODUCT",
"expect": {
"status": [200],
"jsonPath": {
"$.success": true
}
}
},
{
"id": "get_stats",
"name": "[통계] 품목 통계 조회",
"dependsOn": ["search_by_type"],
"method": "GET",
"endpoint": "/items/stats",
"expect": {
"status": [200],
"jsonPath": {
"$.success": true
}
},
"extract": {
"productCount": "$.data.by_type.PRODUCT",
"materialCount": "$.data.by_type.MATERIAL"
}
}
]
},
'items-bom': {
"version": "1.0",
"meta": {
"name": "품목 BOM 관리 테스트",
"description": "BOM(Bill of Materials) 등록/조회/수정/삭제 테스트",
"tags": ["items", "bom", "integration"]
},
"config": {
"baseUrl": "https://sam.kr/api/v1",
"timeout": 30000,
"stopOnFailure": false
},
"variables": {},
"steps": [
{
"id": "create_parent",
"name": "[부모품목] 완제품 생성",
"method": "POST",
"endpoint": "/items",
"body": {
"code": "{{$faker.itemCode:PROD}}",
"name": "{{$faker.productName}}",
"item_type": "PRODUCT",
"unit": "EA",
"unit_price": "{{$faker.price:100000:500000}}"
},
"expect": { "status": [200, 201] },
"extract": {
"parentCode": "$.data.code",
"parentId": "$.data.id"
}
},
{
"id": "create_child1",
"name": "[자재1] 원자재 생성",
"dependsOn": ["create_parent"],
"method": "POST",
"endpoint": "/items",
"body": {
"code": "{{$faker.itemCode:MAT}}",
"name": "원자재 - {{$faker.word}}",
"item_type": "MATERIAL",
"unit": "{{$faker.unit}}",
"unit_price": "{{$faker.price:1000:10000}}"
},
"expect": { "status": [200, 201] },
"extract": {
"child1Code": "$.data.code",
"child1Id": "$.data.id"
}
},
{
"id": "create_child2",
"name": "[자재2] 부품 생성",
"dependsOn": ["create_child1"],
"method": "POST",
"endpoint": "/items",
"body": {
"code": "{{$faker.itemCode:PART}}",
"name": "부품 - {{$faker.word}}",
"item_type": "MATERIAL",
"unit": "EA",
"unit_price": "{{$faker.price:5000:20000}}"
},
"expect": { "status": [200, 201] },
"extract": {
"child2Code": "$.data.code",
"child2Id": "$.data.id"
}
},
{
"id": "add_bom1",
"name": "[BOM] 원자재 등록",
"dependsOn": ["create_child2"],
"method": "POST",
"endpoint": "/items/{{create_parent.parentCode}}/bom",
"body": {
"child_code": "{{create_child1.child1Code}}",
"quantity": "{{$faker.number:1:10}}",
"unit": "{{$faker.unit}}"
},
"expect": { "status": [200, 201] }
},
{
"id": "add_bom2",
"name": "[BOM] 부품 등록",
"dependsOn": ["add_bom1"],
"method": "POST",
"endpoint": "/items/{{create_parent.parentCode}}/bom",
"body": {
"child_code": "{{create_child2.child2Code}}",
"quantity": "{{$faker.number:1:5}}",
"unit": "EA"
},
"expect": { "status": [200, 201] }
},
{
"id": "get_bom",
"name": "[BOM] 조회",
"dependsOn": ["add_bom2"],
"method": "GET",
"endpoint": "/items/{{create_parent.parentCode}}/bom",
"expect": {
"status": [200],
"jsonPath": {
"$.success": true,
"$.data": "@isArray",
"$.data": "@minLength:2"
}
}
},
{
"id": "cleanup_parent",
"name": "[정리] 부모품목 삭제",
"dependsOn": ["get_bom"],
"method": "DELETE",
"endpoint": "/items/{{create_parent.parentCode}}",
"expect": { "status": [200, 204] },
"continueOnFailure": true
},
{
"id": "cleanup_children",
"name": "[정리] 자재 삭제",
"dependsOn": ["cleanup_parent"],
"method": "DELETE",
"endpoint": "/items/batch",
"body": {
"codes": ["{{create_child1.child1Code}}", "{{create_child2.child2Code}}"]
},
"expect": { "status": [200, 204] },
"continueOnFailure": true
}
]
}
};
const ExamplesModal = {
open() {
document.getElementById('examples-modal').classList.remove('hidden');
document.body.style.overflow = 'hidden';
this.renderAllExamples();
},
close() {
document.getElementById('examples-modal').classList.add('hidden');
document.body.style.overflow = '';
},
switchExample(exampleId) {
document.querySelectorAll('.example-tab').forEach(tab => tab.classList.remove('active'));
document.querySelector(`.example-tab[data-example="${exampleId}"]`).classList.add('active');
document.querySelectorAll('.example-content').forEach(content => content.classList.add('hidden'));
document.getElementById(`example-${exampleId}`).classList.remove('hidden');
},
renderAllExamples() {
Object.keys(ExampleFlows).forEach(key => {
const element = document.getElementById(`json-${key}`);
if (element) {
element.textContent = JSON.stringify(ExampleFlows[key], null, 2);
}
});
},
copy(exampleId) {
const json = JSON.stringify(ExampleFlows[exampleId], null, 2);
navigator.clipboard.writeText(json).then(() => {
showToast('JSON이 클립보드에 복사되었습니다.', 'success');
}).catch(err => {
console.error('복사 실패:', err);
});
}
};
document.querySelectorAll('.example-tab').forEach(tab => {
tab.addEventListener('click', function() {
ExamplesModal.switchExample(this.dataset.example);
});
});
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && !document.getElementById('examples-modal').classList.contains('hidden')) {
ExamplesModal.close();
}
});
</script>
@endverbatim