Files
sam-manage/resources/views/dev-tools/flow-tester/partials/guide-modal.blade.php

551 lines
31 KiB
PHP
Raw Normal View History

{{-- 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>{
"name": "플로우 이름", // 플로우 제목 (필수)
"description": "플로우 설명", // 상세 설명 (선택)
"version": "1.0",
"config": {
"baseUrl": "https://api.sam.kr/api/v1",
"apiKey": "@{{$env.API_KEY}}", // API 키 (선택)
"timeout": 30000,
"stopOnFailure": true
},
"variables": {
"user_id": "@{{$env.FLOW_TESTER_USER_ID}}", // 환경변수 참조
"user_pwd": "@{{$env.FLOW_TESTER_USER_PWD}}"
},
"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://api.sam.kr/api/v1
- API Key: 별도 제공 (config.apiKey에 설정)
- 인증: user_id, user_pwd로 로그인 Bearer Token 사용
## 테스트 플로우
1. [ 번째 단계]
2. [ 번째 단계] - 1번에서 추출한 사용
3. ...
## JSON 형식 (반드시 이 형식으로!)
{
"name": "플로우 이름 (50자 이내)",
"description": "플로우 상세 설명",
"version": "1.0",
"config": {
"baseUrl": "https://api.sam.kr/api/v1",
"apiKey": "YOUR_API_KEY",
"timeout": 30000,
"stopOnFailure": true
},
"variables": {
"user_id": "@{{$env.FLOW_TESTER_USER_ID}}",
"user_pwd": "@{{$env.FLOW_TESTER_USER_PWD}}"
},
"steps": [
{
"id": "unique_step_id",
"name": "단계 이름",
"method": "POST",
"endpoint": "/path",
"body": { ... },
"expect": {
"status": [200],
"jsonPath": { "$.access_token": "@isString" }
},
"extract": { "token": "$.access_token" }
}
]
}
## 주의사항
- name: 최상위 필드로, 플로우 이름으로 저장됨
- description: 최상위 필드로, 설명으로 저장됨
- config.apiKey: API 인증키 설정
- variables에서 @{{$env.XXX}} 형식으로 환경변수 참조 가능
- extract로 추출한 값은 이후 스텝에서 @{{stepId.변수명}}으로 사용
- headers에 Authorization: Bearer @{{login.token}} 형식으로 토큰 전달</pre>
</div>
<h4 class="text-lg font-semibold mt-6 mb-3">실제 예시: 인증 플로우 테스트</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">인증 API 테스트 플로우를 만들어줘.
## 테스트 목적
로그인, 프로필 조회, 토큰 갱신, 로그아웃 전체 인증 플로우 검증
## API 서버
- Base URL: https://api.sam.kr/api/v1
- 인증: user_id/user_pwd access_token/refresh_token
## 테스트 시나리오
1. 로그인 access_token, refresh_token 추출
2. 프로필 조회 Authorization 헤더에 access_token 사용
3. 토큰 갱신 refresh_token으로 access_token 발급
4. 로그아웃 access_token으로 로그아웃
## API 엔드포인트
- POST /login - { user_id, user_pwd } { access_token, refresh_token }
- GET /users/me - Authorization: Bearer {token} { success: true }
- POST /refresh - { refresh_token } { access_token }
- POST /logout - Authorization: Bearer {token}
## JSON 형식
{
"name": "인증 플로우 테스트",
"description": "로그인, 프로필 조회, 토큰 갱신, 로그아웃 플로우를 테스트합니다.",
"version": "1.0",
"config": {
"baseUrl": "https://api.sam.kr/api/v1",
"apiKey": "YOUR_API_KEY",
"timeout": 30000,
"stopOnFailure": true
},
"variables": {
"user_id": "@{{$env.FLOW_TESTER_USER_ID}}",
"user_pwd": "@{{$env.FLOW_TESTER_USER_PWD}}"
}
}
extract로 token을 추출하고 다음 스텝 headers에서 사용해줘.</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(() => {
showToast('프롬프트 템플릿이 복사되었습니다.', 'success');
}).catch(err => {
console.error('복사 실패:', err);
});
}
// 예시 프롬프트 복사
function copyExamplePrompt() {
const text = document.getElementById('example-prompt').textContent;
navigator.clipboard.writeText(text).then(() => {
showToast('예시 프롬프트가 복사되었습니다.', 'success');
}).catch(err => {
console.error('복사 실패:', err);
});
}
</script>