Files
sam-manage/resources/views/dev-tools/flow-tester/partials/guide-modal.blade.php
hskwon 4ede126518 feat: [flow-tester] Faker 변수 바인딩 및 품목관리 API 테스트 예제 추가
- VariableBinder에 Laravel Faker 기반 랜덤 데이터 생성 기능 추가
  - {{$faker.name}}, {{$faker.email}}, {{$faker.phone}} 등 텍스트 생성
  - {{$faker.number:MIN:MAX}}, {{$faker.price:MIN:MAX}} 숫자/가격 생성
  - {{$faker.itemCode:PREFIX}}, {{$faker.code:PREFIX:LENGTH}} 코드 생성
  - {{$faker.productName}}, {{$faker.unit}}, {{$faker.category}} 품목 관련
- 가이드 모달에 Faker 변수 문서 추가
- 품목관리 API 테스트 예제 3개 추가
  - items-crud: Faker 기반 CRUD 테스트 (6단계)
  - items-search: 검색/통계 API 테스트 (5단계)
  - items-bom: BOM 관리 테스트 (8단계)
2025-12-01 14:04:57 +09:00

543 lines
31 KiB
PHP

{{-- JSON 작성 가이드 모달 --}}
<div id="guide-modal"
class="hidden fixed inset-0 z-50 overflow-y-auto"
aria-labelledby="guide-modal-title"
role="dialog"
aria-modal="true">
{{-- 배경 오버레이 --}}
<div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
onclick="GuideModal.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-4xl 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="guide-modal-title" class="text-xl font-semibold text-gray-800">
API Flow JSON 작성 가이드
</h2>
<button type="button"
onclick="GuideModal.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">
<button type="button" class="guide-tab active px-4 py-2 text-sm font-medium rounded-lg transition-colors" data-tab="basic">
기본 구조
</button>
<button type="button" class="guide-tab px-4 py-2 text-sm font-medium rounded-lg transition-colors" data-tab="variables">
변수 바인딩
</button>
<button type="button" class="guide-tab px-4 py-2 text-sm font-medium rounded-lg transition-colors" data-tab="validation">
응답 검증
</button>
<button type="button" class="guide-tab px-4 py-2 text-sm font-medium rounded-lg transition-colors" data-tab="ai-prompt">
AI로 만들기
</button>
</div>
</div>
{{-- 모달 콘텐츠 --}}
<div class="flex-1 overflow-y-auto p-6">
{{-- 기본 구조 --}}
<div id="guide-tab-basic" class="guide-tab-content">
<div class="prose prose-sm max-w-none">
<h4 class="text-lg font-semibold mb-3">1. 전체 구조</h4>
<pre class="bg-gray-100 p-4 rounded-lg text-xs overflow-x-auto"><code>{
"version": "1.0",
"config": {
"baseUrl": "https://sam.kr/api/v1",
"timeout": 30000,
"stopOnFailure": true,
"headers": {
"Accept": "application/json",
"Authorization": "Bearer @{{auth_token}}"
}
},
"variables": {
"testPrefix": "TEST_",
"timestamp": "@{{$timestamp}}"
},
"steps": [...]
}</code></pre>
<h4 class="text-lg font-semibold mt-6 mb-3">2. Step 구조</h4>
<pre class="bg-gray-100 p-4 rounded-lg text-xs overflow-x-auto"><code>{
"id": "step1", // 고유 ID (필수)
"name": "페이지 생성", // 표시 이름 (필수)
"description": "테스트용 페이지 생성", // 설명 (선택)
"dependsOn": [], // 의존 스텝 ID 배열 (선택)
"method": "POST", // HTTP 메서드 (필수)
"endpoint": "/item-master/pages", // 엔드포인트 (필수)
"headers": {}, // 추가 헤더 (선택)
"body": { // 요청 바디 (선택)
"page_name": "@{{variables.testPrefix}}Page"
},
"expect": { // 응답 검증 (선택)
"status": [200, 201],
"jsonPath": {
"$.success": true
}
},
"extract": { // 변수 추출 (선택)
"pageId": "$.data.id"
},
"continueOnFailure": false, // 실패 시 계속 여부 (기본: false)
"delay": 0 // 실행 전 대기 (ms, 기본: 0)
}</code></pre>
<h4 class="text-lg font-semibold mt-6 mb-3">3. HTTP 메서드</h4>
<table class="w-full text-sm border border-gray-200 rounded-lg overflow-hidden">
<thead class="bg-gray-50">
<tr>
<th class="px-4 py-2 text-left">메서드</th>
<th class="px-4 py-2 text-left">용도</th>
<th class="px-4 py-2 text-left">예시</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200">
<tr><td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded">GET</code></td><td class="px-4 py-2">조회</td><td class="px-4 py-2">목록, 상세</td></tr>
<tr><td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded">POST</code></td><td class="px-4 py-2">생성</td><td class="px-4 py-2"> 리소스</td></tr>
<tr><td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded">PUT</code></td><td class="px-4 py-2">수정 (전체)</td><td class="px-4 py-2">리소스 교체</td></tr>
<tr><td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded">PATCH</code></td><td class="px-4 py-2">수정 (일부)</td><td class="px-4 py-2">필드 수정</td></tr>
<tr><td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded">DELETE</code></td><td class="px-4 py-2">삭제</td><td class="px-4 py-2">리소스 삭제</td></tr>
</tbody>
</table>
</div>
</div>
{{-- 변수 바인딩 --}}
<div id="guide-tab-variables" class="guide-tab-content hidden">
<div class="prose prose-sm max-w-none">
<h4 class="text-lg font-semibold mb-3">변수 바인딩 문법</h4>
<table class="w-full text-sm border border-gray-200 rounded-lg overflow-hidden">
<thead class="bg-gray-50">
<tr>
<th class="px-4 py-2 text-left">패턴</th>
<th class="px-4 py-2 text-left">설명</th>
<th class="px-4 py-2 text-left">예시</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200">
<tr>
<td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded text-xs">@{{variables.xxx}}</code></td>
<td class="px-4 py-2">전역 변수</td>
<td class="px-4 py-2"><code class="text-xs">@{{variables.testPrefix}}</code> "TEST_"</td>
</tr>
<tr>
<td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded text-xs">@{{stepN.xxx}}</code></td>
<td class="px-4 py-2">이전 스텝 추출값</td>
<td class="px-4 py-2"><code class="text-xs">@{{step1.pageId}}</code> 42</td>
</tr>
<tr>
<td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded text-xs">@{{stepN.response.xxx}}</code></td>
<td class="px-4 py-2">이전 스텝 응답</td>
<td class="px-4 py-2"><code class="text-xs">@{{step1.response.data.name}}</code></td>
</tr>
<tr>
<td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded text-xs">@{{$timestamp}}</code></td>
<td class="px-4 py-2">현재 타임스탬프</td>
<td class="px-4 py-2">1701100800</td>
</tr>
<tr>
<td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded text-xs">@{{$uuid}}</code></td>
<td class="px-4 py-2">랜덤 UUID</td>
<td class="px-4 py-2">550e8400-e29b-...</td>
</tr>
<tr>
<td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded text-xs">@{{$random:N}}</code></td>
<td class="px-4 py-2">N자리 랜덤 숫자</td>
<td class="px-4 py-2"><code class="text-xs">@{{$random:6}}</code> 482916</td>
</tr>
<tr>
<td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded text-xs">@{{$date}}</code></td>
<td class="px-4 py-2">오늘 날짜</td>
<td class="px-4 py-2">2025-11-27</td>
</tr>
</tbody>
</table>
<h4 class="text-lg font-semibold mt-6 mb-3">Faker 변수 (랜덤 데이터 생성)</h4>
<p class="text-gray-600 mb-3 text-sm">Create API 테스트 임의의 테스트 데이터를 자동 생성합니다. <code class="bg-gray-100 px-1 rounded">@{{$faker.타입}}</code> 형식으로 사용합니다.</p>
<table class="w-full text-sm border border-gray-200 rounded-lg overflow-hidden">
<thead class="bg-gray-50">
<tr>
<th class="px-4 py-2 text-left">패턴</th>
<th class="px-4 py-2 text-left">설명</th>
<th class="px-4 py-2 text-left">예시 결과</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200">
<tr class="bg-blue-50">
<td colspan="3" class="px-4 py-2 font-semibold text-blue-800">텍스트</td>
</tr>
<tr>
<td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded text-xs">@{{$faker.name}}</code></td>
<td class="px-4 py-2">랜덤 이름</td>
<td class="px-4 py-2">김철수</td>
</tr>
<tr>
<td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded text-xs">@{{$faker.company}}</code></td>
<td class="px-4 py-2">랜덤 회사명</td>
<td class="px-4 py-2">삼성전자</td>
</tr>
<tr>
<td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded text-xs">@{{$faker.email}}</code></td>
<td class="px-4 py-2">랜덤 이메일</td>
<td class="px-4 py-2">test@example.com</td>
</tr>
<tr>
<td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded text-xs">@{{$faker.phone}}</code></td>
<td class="px-4 py-2">랜덤 전화번호</td>
<td class="px-4 py-2">010-1234-5678</td>
</tr>
<tr>
<td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded text-xs">@{{$faker.sentence}}</code></td>
<td class="px-4 py-2">랜덤 문장</td>
<td class="px-4 py-2">테스트용 설명 문장입니다.</td>
</tr>
<tr class="bg-green-50">
<td colspan="3" class="px-4 py-2 font-semibold text-green-800">숫자/가격</td>
</tr>
<tr>
<td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded text-xs">@{{$faker.number:MIN:MAX}}</code></td>
<td class="px-4 py-2">범위 정수</td>
<td class="px-4 py-2"><code class="text-xs">@{{$faker.number:1:100}}</code> 42</td>
</tr>
<tr>
<td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded text-xs">@{{$faker.price:MIN:MAX}}</code></td>
<td class="px-4 py-2">범위 가격</td>
<td class="px-4 py-2"><code class="text-xs">@{{$faker.price:1000:50000}}</code> 25430.50</td>
</tr>
<tr>
<td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded text-xs">@{{$faker.quantity}}</code></td>
<td class="px-4 py-2">수량 (1~100)</td>
<td class="px-4 py-2">25</td>
</tr>
<tr class="bg-purple-50">
<td colspan="3" class="px-4 py-2 font-semibold text-purple-800">코드/ID 생성</td>
</tr>
<tr>
<td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded text-xs">@{{$faker.itemCode:PREFIX}}</code></td>
<td class="px-4 py-2">품목코드 생성</td>
<td class="px-4 py-2"><code class="text-xs">@{{$faker.itemCode:TEST}}</code> TEST-482916</td>
</tr>
<tr>
<td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded text-xs">@{{$faker.code:PREFIX:LEN}}</code></td>
<td class="px-4 py-2">커스텀 코드</td>
<td class="px-4 py-2"><code class="text-xs">@{{$faker.code:MAT:4}}</code> MAT0042</td>
</tr>
<tr>
<td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded text-xs">@{{$faker.sku}}</code></td>
<td class="px-4 py-2">SKU 코드</td>
<td class="px-4 py-2">SKU-AB123</td>
</tr>
<tr>
<td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded text-xs">@{{$faker.barcode}}</code></td>
<td class="px-4 py-2">바코드 (EAN13)</td>
<td class="px-4 py-2">4006381333931</td>
</tr>
<tr class="bg-orange-50">
<td colspan="3" class="px-4 py-2 font-semibold text-orange-800">품목 관련</td>
</tr>
<tr>
<td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded text-xs">@{{$faker.productName}}</code></td>
<td class="px-4 py-2">제품명 (한글)</td>
<td class="px-4 py-2">프리미엄 부품 Pro-425</td>
</tr>
<tr>
<td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded text-xs">@{{$faker.unit}}</code></td>
<td class="px-4 py-2">단위</td>
<td class="px-4 py-2">EA, SET, BOX, KG </td>
</tr>
<tr>
<td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded text-xs">@{{$faker.category}}</code></td>
<td class="px-4 py-2">품목 카테고리</td>
<td class="px-4 py-2">원자재, 부품, 반제품 </td>
</tr>
</tbody>
</table>
<h4 class="text-lg font-semibold mt-6 mb-3">사용 위치</h4>
<ul class="list-disc list-inside space-y-2 text-gray-700">
<li><strong>endpoint:</strong> <code class="bg-gray-100 px-1 rounded text-xs">/pages/@{{step1.pageId}}/sections</code></li>
<li><strong>body:</strong> <code class="bg-gray-100 px-1 rounded text-xs">"name": "@{{variables.testPrefix}}Page_@{{$timestamp}}"</code></li>
<li><strong>headers:</strong> <code class="bg-gray-100 px-1 rounded text-xs">"X-Request-ID": "@{{$uuid}}"</code></li>
</ul>
<div class="mt-6 p-4 bg-blue-50 border border-blue-200 rounded-lg">
<div class="flex items-start gap-3">
<svg class="w-5 h-5 text-blue-600 flex-shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="text-sm text-blue-800"><strong>Tip:</strong> extract로 추출한 값은 이후 스텝에서 <code class="bg-blue-100 px-1 rounded">@{{stepId.변수명}}</code>으로 사용</span>
</div>
</div>
</div>
</div>
{{-- 응답 검증 --}}
<div id="guide-tab-validation" class="guide-tab-content hidden">
<div class="prose prose-sm max-w-none">
<h4 class="text-lg font-semibold mb-3">expect.status - 상태 코드 검증</h4>
<pre class="bg-gray-100 p-4 rounded-lg text-xs">"expect": {
"status": [200, 201] // 허용 상태 코드 배열
}</pre>
<h4 class="text-lg font-semibold mt-6 mb-3">expect.jsonPath - 응답 바디 검증</h4>
<table class="w-full text-sm border border-gray-200 rounded-lg overflow-hidden">
<thead class="bg-gray-50">
<tr>
<th class="px-4 py-2 text-left">연산자</th>
<th class="px-4 py-2 text-left">설명</th>
<th class="px-4 py-2 text-left">예시</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200">
<tr><td class="px-4 py-2">(직접 )</td><td class="px-4 py-2">정확한 비교</td><td class="px-4 py-2"><code class="text-xs">"$.success": true</code></td></tr>
<tr><td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded">@exists</code></td><td class="px-4 py-2">존재 여부</td><td class="px-4 py-2"><code class="text-xs">"$.data.id": "@exists"</code></td></tr>
<tr><td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded">@isNumber</code></td><td class="px-4 py-2">숫자 타입</td><td class="px-4 py-2"><code class="text-xs">"$.data.id": "@isNumber"</code></td></tr>
<tr><td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded">@isString</code></td><td class="px-4 py-2">문자열 타입</td><td class="px-4 py-2"><code class="text-xs">"$.data.name": "@isString"</code></td></tr>
<tr><td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded">@isArray</code></td><td class="px-4 py-2">배열 타입</td><td class="px-4 py-2"><code class="text-xs">"$.data.items": "@isArray"</code></td></tr>
<tr><td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded">@notEmpty</code></td><td class="px-4 py-2">비어있지 않음</td><td class="px-4 py-2"><code class="text-xs">"$.data.name": "@notEmpty"</code></td></tr>
<tr><td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded">@minLength:N</code></td><td class="px-4 py-2">최소 길이</td><td class="px-4 py-2"><code class="text-xs">"$.data.items": "@minLength:1"</code></td></tr>
<tr><td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded">@gt:N</code></td><td class="px-4 py-2">N보다 </td><td class="px-4 py-2"><code class="text-xs">"$.data.id": "@gt:0"</code></td></tr>
<tr><td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded">@regex:패턴</code></td><td class="px-4 py-2">정규식 매칭</td><td class="px-4 py-2"><code class="text-xs">"$.data.code": "@regex:^[A-Z]+$"</code></td></tr>
<tr><td class="px-4 py-2"><code class="bg-gray-100 px-1 rounded">@contains:</code></td><td class="px-4 py-2">포함 여부</td><td class="px-4 py-2"><code class="text-xs">"$.message": "@contains:success"</code></td></tr>
</tbody>
</table>
<h4 class="text-lg font-semibold mt-6 mb-3">extract - 추출</h4>
<pre class="bg-gray-100 p-4 rounded-lg text-xs">"extract": {
"pageId": "$.data.id", // 이후 @{{step1.pageId}}로 사용
"pageName": "$.data.page_name", // 이후 @{{step1.pageName}}로 사용
"sections": "$.data.sections" // 배열도 추출 가능
}</pre>
</div>
</div>
{{-- AI로 만들기 --}}
<div id="guide-tab-ai-prompt" class="guide-tab-content hidden">
<div class="prose prose-sm max-w-none">
<h4 class="text-lg font-semibold mb-3">AI에게 플로우 JSON 요청하기</h4>
<p class="text-gray-600 mb-4">Claude, ChatGPT AI에게 아래 템플릿으로 요청하면 바로 붙여넣기 가능한 JSON을 생성합니다.</p>
<div class="p-4 bg-green-50 border border-green-200 rounded-lg mb-6">
<div class="flex items-start gap-3">
<svg class="w-5 h-5 text-green-600 flex-shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="text-sm text-green-800"><strong>자동 추출:</strong> AI가 <code class="bg-green-100 px-1 rounded">meta</code> 필드를 포함하면 이름, 카테고리, 설명이 자동으로 채워집니다!</span>
</div>
</div>
<h4 class="text-lg font-semibold mb-3">프롬프트 템플릿 (복사해서 사용)</h4>
<div class="relative">
<button onclick="copyPromptTemplate()"
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="prompt-template" class="bg-gray-800 text-gray-100 p-4 rounded-lg text-xs overflow-x-auto">API Flow Tester용 JSON을 만들어줘.
## 테스트 목적
[테스트하려는 기능/시나리오 설명]
## API 서버 정보
- Base URL: https://sam.kr/api/v1
- 인증: Bearer Token
## 테스트 플로우
1. [ 번째 단계]
2. [ 번째 단계] - 1번에서 생성된 ID 사용
3. ...
## API 엔드포인트 참고
[Swagger 문서 URL 또는 API 명세]
## JSON 형식 (반드시 이 형식으로!)
{
"version": "1.0",
"meta": {
"name": "플로우 이름 (50자 이내)",
"description": "상세 설명",
"tags": ["category", "integration", "crud"]
},
"config": {
"baseUrl": "https://sam.kr/api/v1",
"timeout": 30000,
"stopOnFailure": true
},
"variables": {
"testPrefix": "TEST_"
},
"steps": [
{
"id": "unique_step_id",
"name": "단계 이름",
"method": "POST",
"endpoint": "/path",
"body": { ... },
"expect": { "status": [200, 201] },
"extract": { "변수명": "$.data.id" }
}
]
}
## 주의사항
- meta.name: 값이 플로우 이름으로 저장됨
- meta.tags[0]: 번째 태그가 카테고리로 사용됨
- meta.description: 값이 설명으로 저장됨
- 스텝 ID는 고유해야
- extract로 추출한 값은 이후 스텝에서 @{{stepId.변수명}}으로 사용</pre>
</div>
<h4 class="text-lg font-semibold mt-6 mb-3">실제 예시: 품목관리 API 테스트</h4>
<div class="relative">
<button onclick="copyExamplePrompt()"
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="example-prompt" class="bg-gray-800 text-gray-100 p-4 rounded-lg text-xs overflow-x-auto">품목관리(Item Master) API 통합 테스트 플로우를 만들어줘.
## 테스트 목적
품목관리의 페이지, 섹션, 필드 CRUD 연결/해제 기능 검증
## API 서버
- Base URL: https://sam.kr/api/v1
- 인증: Bearer Token
## 테스트 시나리오
1. 페이지 생성 삭제 다시 생성
2. 섹션 독립 생성 삭제 다시 생성
3. 필드 독립 생성 삭제 다시 생성
4. 페이지에 섹션 연결 연결 해제 다시 연결
5. 섹션에 필드 연결 연결 해제
6. 정리: 생성된 리소스 삭제
## API 엔드포인트
- POST /item-master/pages - 페이지 생성
- DELETE /item-master/pages/{id} - 페이지 삭제
- POST /item-master/sections - 섹션 독립 생성
- POST /item-master/pages/{id}/link-section - 섹션 연결
- POST /item-master/sections/{id}/link-field - 필드 연결
## JSON 형식 (meta 포함 필수!)
- meta.name: "품목관리 API 통합 테스트"
- meta.tags: ["item-master", "integration", "crud"]
- meta.description: "페이지/섹션/필드 CRUD 및 연결 기능 검증"
API 응답에서 생성된 ID를 추출해서 다음 단계에서 사용해줘.
테스트 데이터는 "TEST_" 접두사를 붙여줘.</pre>
</div>
<div class="mt-6 p-4 bg-blue-50 border border-blue-200 rounded-lg">
<h5 class="font-semibold text-blue-800 mb-2">사용 흐름</h5>
<ol class="list-decimal list-inside text-sm text-blue-700 space-y-1">
<li> 템플릿을 복사해서 AI에게 요청</li>
<li>AI가 생성한 JSON을 복사</li>
<li>"새 플로우" 페이지에 붙여넣기</li>
<li>"검증 및 추출" 클릭 이름/카테고리/설명 자동 채움</li>
<li>"저장" 클릭</li>
</ol>
</div>
</div>
</div>
</div>
{{-- 모달 푸터 --}}
<div class="flex justify-end px-6 py-4 border-t border-gray-200 flex-shrink-0">
<button type="button"
onclick="GuideModal.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>
.guide-tab {
background-color: #f3f4f6;
color: #374151;
}
.guide-tab:hover {
background-color: #e5e7eb;
}
.guide-tab.active {
background-color: #2563eb;
color: white;
}
</style>
<script>
const GuideModal = {
open() {
document.getElementById('guide-modal').classList.remove('hidden');
document.body.style.overflow = 'hidden';
},
close() {
document.getElementById('guide-modal').classList.add('hidden');
document.body.style.overflow = '';
},
switchTab(tabId) {
// 탭 버튼 활성화
document.querySelectorAll('.guide-tab').forEach(tab => {
tab.classList.remove('active');
});
document.querySelector(`.guide-tab[data-tab="${tabId}"]`).classList.add('active');
// 탭 콘텐츠 표시
document.querySelectorAll('.guide-tab-content').forEach(content => {
content.classList.add('hidden');
});
document.getElementById(`guide-tab-${tabId}`).classList.remove('hidden');
}
};
// 탭 클릭 이벤트
document.querySelectorAll('.guide-tab').forEach(tab => {
tab.addEventListener('click', function() {
GuideModal.switchTab(this.dataset.tab);
});
});
// ESC 키로 닫기
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && !document.getElementById('guide-modal').classList.contains('hidden')) {
GuideModal.close();
}
});
// 프롬프트 템플릿 복사
function copyPromptTemplate() {
const text = document.getElementById('prompt-template').textContent;
navigator.clipboard.writeText(text).then(() => {
alert('프롬프트 템플릿이 복사되었습니다.');
}).catch(err => {
console.error('복사 실패:', err);
});
}
// 예시 프롬프트 복사
function copyExamplePrompt() {
const text = document.getElementById('example-prompt').textContent;
navigator.clipboard.writeText(text).then(() => {
alert('예시 프롬프트가 복사되었습니다.');
}).catch(err => {
console.error('복사 실패:', err);
});
}
</script>