- 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 테마와 일관된 디자인 - 비동기 콜백 패턴으로 리팩토링 - 삭제/복원/영구삭제 각각 다른 스타일 적용
889 lines
36 KiB
PHP
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
|