📝 docs: 번역 요청 엑셀 파일의 MD 파일 생성 지침 수정

This commit is contained in:
“hyeonggkim”
2026-04-20 20:22:38 +09:00
parent d6e750e2e9
commit 1ab599f4ff
4 changed files with 651 additions and 10 deletions

View File

@@ -0,0 +1,166 @@
---
name: requirement-analyzer
description: |
PPT 기획서를 분석하여 요구사항 명세서를 자동 생성합니다.
Nuxt pages 라우팅 구조, 컴포넌트 트리, API 엔드포인트 목록,
화면 전환 플로우(Mermaid)를 구조화된 마크다운 문서로 출력합니다.
다음 상황에서 반드시 사용하세요:
- "기획서 분석해줘", "PPT 파싱", "요구사항 정리해줘"
- "라우팅 구조 뽑아줘", "컴포넌트 트리 만들어줘"
- "API 목록 정리", "화면 플로우 그려줘"
- `.pptx` 파일 경로를 제공받고 개발 요구사항 추출을 요청받았을 때
---
# 요구사항 분석기 (Requirement Analyzer)
이 skill 은 PPT 기획서(`.pptx`)를 파싱하여 팀 전체가 동일한 기반으로 개발에 착수할 수 있는
구조화된 요구사항 명세서를 자동으로 생성합니다.
## 언제 사용하는가
- 기획팀에서 PPT 기획서를 전달받았을 때
- 개발 착수 전 화면 목록 / 라우팅 / API 목록을 정리해야 할 때
- 화면 전환 플로우를 다이어그램으로 시각화해야 할 때
---
## 작업 순서
### Phase 1: 파일 확인 및 추출
1. **파일 경로 확인**
- 사용자가 PPTX 파일 경로를 제공했는지 확인한다.
- 미제공 시: 파일 경로를 먼저 요청한다.
2. **의존성 확인**
스킬 디렉토리 기준 스크립트를 실행하기 전, `python-pptx` 설치 여부를 확인한다.
```bash
python3 -c "import pptx" 2>/dev/null && echo "OK" || echo "MISSING"
```
- `MISSING` 출력 시: 아래 메시지를 사용자에게 안내한다.
```
⚠️ python-pptx 패키지가 필요합니다.
설치 명령어: pip3 install python-pptx
또는 자동 설치: python3 scripts/extract_pptx.py --auto-install <파일경로>
```
3. **PPTX 추출 실행**
```bash
python3 <skill-dir>/scripts/extract_pptx.py --extract-images "<pptx경로>"
```
- `--extract-images`: 슬라이드 이미지(PNG)를 임시 디렉토리에 추출 → 시각 분석에 활용
- 출력: JSON 형태의 슬라이드 데이터 (슬라이드별 제목, 텍스트, 노트, 도형, 테이블, 이미지 경로)
4. **결과 확인**
- JSON 파싱 성공 여부 확인
- 추출된 이미지가 있으면 각 슬라이드별로 순서대로 열어 와이어프레임/목업 시각 분석
---
### Phase 2: 내용 분석
추출된 JSON 데이터와 슬라이드 이미지를 기반으로 아래 항목을 분석한다.
#### 화면(페이지) 식별
- 슬라이드 제목에서 "화면", "페이지", 경로(`/`로 시작), URL 패턴 찾기
- 와이어프레임 이미지에서 GNB, 레이아웃 영역 확인
- 중복 레이아웃 슬라이드(상세 설명용) vs 독립 화면 구분
#### UI 컴포넌트 패턴 식별
- 반복 등장하는 도형 레이블 (예: "GNB", "Footer", "Card", "Modal", "Tab")
- 슬라이드 간 동일한 레이아웃 구조 → 공통 컴포넌트 후보
- 이미지에서 시각적으로 동일한 UI 블록
#### 네비게이션 플로우 추론
- 화살표/커넥터 도형으로 연결된 화면 간 전환
- "클릭 시", "탭하면", "→" 등의 텍스트 주석
- 슬라이드 번호 순서 + 제목 키워드로 흐름 유추
#### API 엔드포인트 추출
- 테이블에 "Method", "URL", "API", "Endpoint" 컬럼이 있는 경우
- 발표자 노트에 `GET /api/...`, `POST /api/...` 형태의 텍스트
- 텍스트 박스 내 `http`, `/api`, `fetch`, `axios` 언급
---
### Phase 3: 명세서 생성
아래 7개 섹션으로 구성된 마크다운 문서를 작성한다.
추론 기반 정보는 반드시 `(추정)` 또는 `<확인 필요>` 를 표시한다.
**출력 섹션:**
```
1. 화면 목록 (Pages) — 테이블 형식
2. Nuxt 라우팅 구조 — pages/ 파일 트리
3. 컴포넌트 트리 — components/ 파일 트리 + Props 상세 테이블
4. API 엔드포인트 목록 — 테이블 형식
5. 화면 전환 플로우 — Mermaid flowchart TD
6. 공통 레이아웃 — 레이아웃별 적용 화면 테이블
7. 추가 참고사항 — 미확정 항목 / 확인 필요 목록
```
출력 형식 상세는 `references/output-template.md` 를 참조한다.
---
### Phase 4: 사용자 확인 및 저장
1. **미리보기 제공**
생성된 명세서 전체를 출력하여 사용자가 검토할 수 있도록 한다.
2. **저장 경로 확인**
```
📄 아래 경로에 요구사항 명세서를 저장하려고 합니다:
docs/requirements/<기획서파일명>.md
다른 경로를 원하시면 알려주세요. 진행할까요? (y/n)
```
3. **파일 저장**
- 사용자 승인 후 파일을 저장한다.
- `docs/requirements/` 디렉토리가 없으면 생성한다.
- 저장 완료 후 아래 메시지를 출력한다:
```
✅ 요구사항 명세서 저장 완료
docs/requirements/<파일명>.md
다음 단계:
1) <확인 필요> 항목을 기획자에게 확인하세요.
2) 컴포넌트/API 목록을 개발 태스크로 분해하세요.
3) git add docs/requirements/ && git commit -m "docs: 요구사항 명세서 추가"
```
---
## 출력 형식
`references/output-template.md` 에서 전체 마크다운 구조와 예시를 확인하세요.
### Mermaid 플로우차트 작성 규칙
```mermaid
flowchart TD
A[메인] --> B[이벤트 목록]
B --> C[이벤트 상세]
C --> D{사전등록 완료?}
D -- 완료 --> E[완료 모달]
D -- 미완료 --> F[사전등록 폼]
```
- 화면 노드: `[화면명]` (사각형)
- 조건 분기: `{조건}` (다이아몬드)
- 모달/팝업: `([모달명])` (타원)
- 화살표 레이블: 클릭 대상 버튼명 또는 조건
---
## 주의사항
- **기존 파일 수정 금지**: 프로젝트의 기존 소스 파일은 절대 수정하지 않는다. 명세서 파일만 새로 생성한다.
- **추측 표시 필수**: 기획서에 명시되지 않은 정보를 추론할 경우 반드시 `(추정)` 을 표시한다.
- **확인 필요 목록**: 확신할 수 없는 항목은 섹션 7에 `<확인 필요>` 로 모아 명시한다.
- **이미지 heavy 기획서**: 텍스트가 적고 이미지 위주인 기획서는 `--extract-images` 옵션이 필수다.
- **대용량 PPTX**: 슬라이드가 50장 이상이면 `--slides 1-20` 으로 범위를 지정하여 순차 처리한다.
- **커밋 금지**: 파일 저장 후 `git commit` 은 사용자 명시 요청이 없으면 실행하지 않는다.

View File

@@ -0,0 +1,159 @@
# 요구사항 명세서 출력 템플릿
이 파일은 `requirement-analyzer` 스킬이 생성하는 마크다운 문서의 형식 예시입니다.
실제 출력 시 각 항목을 기획서 내용으로 채우고, 불확실한 항목은 `(추정)` 또는 `<확인 필요>` 로 표시합니다.
---
```markdown
# 요구사항 명세서: <프로젝트명>
> 기획서: `<파일명.pptx>` | 생성일: <YYYY-MM-DD> | 총 슬라이드: <N>장
> 담당 기획자: <확인 필요> | 개발 일정: <확인 필요>
---
## 1. 화면 목록 (Pages)
| # | 화면명 | 라우팅 경로 | 기획서 슬라이드 | 비고 |
|---|--------|------------|--------------|------|
| 1 | 메인 (랜딩) | `/` | 3-5 | 사전등록 CTA 포함 |
| 2 | 사전등록 | `/register` | 6-9 | 폼 입력 화면 |
| 3 | 등록 완료 | `/register/complete` | 10 | 완료 안내 화면 |
| 4 | 이벤트 소개 | `/about` | 11-13 | 스크롤 단일 페이지 |
| 5 | 공지사항 | `/notice` | 14 | (추정) 목록 페이지 |
| 6 | 공지사항 상세 | `/notice/[id]` | 14 | (추정) 동적 라우트 |
---
## 2. Nuxt 라우팅 구조
```
pages/
├── index.vue # 메인 (랜딩)
├── register/
│ ├── index.vue # 사전등록 폼
│ └── complete.vue # 등록 완료
├── about.vue # 이벤트 소개
└── notice/
├── index.vue # 공지사항 목록
└── [id].vue # 공지사항 상세 (추정)
```
**레이아웃:**
```
layouts/
├── default.vue # GNB + Footer 포함 (전체 화면)
└── blank.vue # GNB/Footer 없음 (모달, 완료 화면 등)
```
---
## 3. 컴포넌트 트리
```
components/
├── common/
│ ├── AppHeader.vue # GNB / 상단 네비게이션
│ ├── AppFooter.vue # 하단 푸터
│ └── AppLogo.vue # 로고 (추정)
├── register/
│ ├── RegisterForm.vue # 사전등록 입력 폼
│ ├── RegisterComplete.vue # 완료 화면 내용
│ └── TermsModal.vue # 약관 모달 (추정)
├── notice/
│ ├── NoticeList.vue # 공지사항 목록 (추정)
│ └── NoticeItem.vue # 공지사항 카드 (추정)
└── ui/
├── BaseButton.vue # 공통 버튼
├── BaseModal.vue # 공통 모달 래퍼 (추정)
└── BaseInput.vue # 공통 입력 필드 (추정)
```
### 컴포넌트 상세
| 컴포넌트 | 사용 화면 | Props (추정) | Emits (추정) | 비고 |
|----------|----------|-------------|-------------|------|
| `AppHeader` | 전체 | `transparent?: boolean` | — | 메인에서 투명 배경 |
| `RegisterForm` | 사전등록 | — | `submit` | 폼 상태 내부 관리 |
| `BaseButton` | 전체 | `variant`, `size`, `disabled` | `click` | Tailwind 기반 |
| `TermsModal` | 사전등록 | `visible` | `close`, `agree` | <확인 필요> |
---
## 4. API 엔드포인트 목록
| # | Method | Endpoint | 설명 | 사용 화면 | 비고 |
|---|--------|----------|------|----------|------|
| 1 | POST | `/api/register` | 사전등록 제출 | 사전등록 | 폼 데이터 전송 |
| 2 | GET | `/api/notices` | 공지사항 목록 | 공지사항 | 페이지네이션 (추정) |
| 3 | GET | `/api/notices/:id` | 공지사항 상세 | 공지사항 상세 | (추정) |
| 4 | GET | `/api/event/info` | 이벤트 기본 정보 | 메인, 소개 | <확인 필요> |
> **참고**: API 경로는 기획서 노트/테이블에서 추출했습니다. 백엔드 팀과 확인이 필요합니다.
---
## 5. 화면 전환 플로우
```mermaid
flowchart TD
A[메인 랜딩] --> |사전등록 버튼 클릭| B[사전등록 폼]
A --> |공지사항 메뉴| C[공지사항 목록]
A --> |이벤트 소개 메뉴| D[이벤트 소개]
B --> |약관 보기 클릭| E([약관 모달])
E --> |닫기| B
B --> |제출 성공| F[등록 완료]
B --> |제출 실패| B
C --> |항목 클릭| G[공지사항 상세]
G --> |목록으로| C
F --> |메인으로| A
```
---
## 6. 공통 레이아웃
| 레이아웃 | 적용 화면 | 포함 컴포넌트 | 비고 |
|----------|----------|-------------|------|
| `default` | 메인, 이벤트 소개, 공지사항 | AppHeader, AppFooter | GNB 표시 |
| `blank` | 등록 완료 | — | GNB/Footer 없음 (추정) |
---
## 7. 추가 참고사항
### 확인 필요 항목
- [ ] 약관 모달 내용 및 동의 필수 항목 (슬라이드 8 — 상세 미기재)
- [ ] 공지사항 페이지네이션 방식 (무한스크롤 vs 페이지 버튼)
- [ ] 사전등록 중복 참여 처리 방식 (동일 이메일 재등록 시)
- [ ] `/api/event/info` 엔드포인트 실제 경로 및 응답 스키마
- [ ] 다국어 지원 여부 (한국어 전용 vs 영어 병행)
### 기획서에서 발견된 특이사항
- 슬라이드 7: "등록 마감 후 버튼 비활성화" — 마감일 기준 상태 분기 필요
- 슬라이드 12: 이벤트 소개 섹션 내 동영상 플레이어 포함 — Video 컴포넌트 추가 검토
- 슬라이드 14: 공지사항 페이지 기획 미완성으로 표시됨 (`<확인 필요>`)
### 미정 항목
| 항목 | 현재 상태 | 담당 |
|------|----------|------|
| OG 태그 / SNS 공유 설정 | 미기재 | <확인 필요> |
| GA / 트래킹 이벤트 정의 | 미기재 | <확인 필요> |
| 브라우저 지원 범위 | 미기재 | <확인 필요> |
```
---
## 활용 팁
- `<확인 필요>` 항목은 기획자와 킥오프 미팅 전 체크리스트로 활용하세요.
- `(추정)` 항목은 개발 착수 전 기획서를 재확인하고 제거하세요.
- API 엔드포인트 목록은 백엔드 팀과 함께 검토하여 최종 확정하세요.
- Mermaid 플로우차트는 PR 설명이나 Confluence 페이지에 바로 붙여 사용할 수 있습니다.

View File

@@ -0,0 +1,314 @@
"""
PPT 기획서 추출 스크립트 (extract_pptx.py)
========================================
PPTX 파일을 파싱하여 Claude AI가 분석할 수 있는 JSON 구조로 변환합니다.
의미 분석(화면 매핑, API 추출, 플로우 추론)은 Claude AI가 담당합니다.
사용법:
python extract_pptx.py <pptx경로> [옵션]
옵션:
--extract-images 슬라이드 이미지를 PNG로 추출 (--output-dir 로 저장 경로 지정)
--output-dir <dir> 이미지 추출 디렉토리 (기본: /tmp/pptx_<파일명>/)
--slides <범위> 처리할 슬라이드 범위 (예: 1-10, 5, 3-7,10-12)
--auto-install python-pptx 자동 설치 후 실행
--pretty JSON 출력 시 들여쓰기 적용
출력:
stdout 에 JSON 데이터 출력
"""
import sys
import os
import json
import argparse
import tempfile
import re
# ─────────────────────────────────────────────
# 의존성 확인 및 자동 설치
# ─────────────────────────────────────────────
def _ensure_pptx(auto_install: bool) -> None:
"""python-pptx 설치 여부 확인. 미설치 시 안내 또는 자동 설치."""
try:
import pptx # noqa: F401
except ImportError:
if auto_install:
import subprocess
print("📦 python-pptx 설치 중...", file=sys.stderr)
subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'python-pptx'])
print("✅ python-pptx 설치 완료\n", file=sys.stderr)
else:
print(
"❌ python-pptx 패키지가 설치되어 있지 않습니다.\n"
"\n"
"설치 명령어:\n"
" pip3 install python-pptx\n"
"\n"
"또는 자동 설치 옵션을 사용하세요:\n"
f" python3 {sys.argv[0]} --auto-install <파일경로>",
file=sys.stderr,
)
sys.exit(1)
# ─────────────────────────────────────────────
# 슬라이드 범위 파싱
# ─────────────────────────────────────────────
def parse_slide_range(spec: str, total: int) -> set[int]:
"""
"1-5,8,10-12" 형태의 슬라이드 범위 문자열을 슬라이드 번호 set 으로 변환.
번호는 1-based.
"""
result: set[int] = set()
for part in spec.split(','):
part = part.strip()
if '-' in part:
start, end = part.split('-', 1)
result.update(range(int(start), int(end) + 1))
else:
result.add(int(part))
return {n for n in result if 1 <= n <= total}
# ─────────────────────────────────────────────
# 도형(Shape) 분류
# ─────────────────────────────────────────────
def _shape_type_name(shape) -> str:
"""python-pptx MSO_SHAPE_TYPE 값을 사람이 읽기 쉬운 문자열로 변환."""
from pptx.util import Emu # noqa: F401 — 모듈 로드 확인용
try:
return shape.shape_type.name.lower() # e.g. 'auto_shape', 'picture', 'line'
except Exception:
return 'unknown'
def _is_connector(shape) -> bool:
"""화살표/커넥터 도형 여부 확인."""
try:
# MSO_SHAPE_TYPE.LINE = 9, FREEFORM = 5
return shape.shape_type in (9,)
except Exception:
return False
# ─────────────────────────────────────────────
# 슬라이드 데이터 추출
# ─────────────────────────────────────────────
def extract_slide(slide, slide_number: int, extract_images: bool, output_dir: str) -> dict:
"""단일 슬라이드에서 모든 관련 데이터를 추출하여 dict 반환."""
from pptx.enum.shapes import PP_PLACEHOLDER # noqa: F401
result: dict = {
'number': slide_number,
'title': '',
'texts': [],
'notes': '',
'images': [],
'shapes': [],
'tables': [],
}
# ── 제목 추출 ──────────────────────────────
for shape in slide.shapes:
try:
if shape.is_placeholder:
ph_type = shape.placeholder_format.type
# PP_PLACEHOLDER.TITLE = 1, CENTER_TITLE = 3
if ph_type in (1, 3):
result['title'] = shape.text.strip()
break
except Exception:
pass
# ── 모든 도형 순회 ──────────────────────────
for shape in slide.shapes:
shape_info: dict = {
'type': _shape_type_name(shape),
'name': getattr(shape, 'name', ''),
'left': int(shape.left or 0),
'top': int(shape.top or 0),
'width': int(shape.width or 0),
'height': int(shape.height or 0),
'text': '',
}
# 텍스트 프레임
if shape.has_text_frame:
text = shape.text_frame.text.strip()
shape_info['text'] = text
if text:
result['texts'].append({
'text': text,
'left': shape_info['left'],
'top': shape_info['top'],
'width': shape_info['width'],
'height': shape_info['height'],
'shape_name': shape_info['name'],
})
# 테이블
if shape.has_table:
table = shape.table
headers = [cell.text.strip() for cell in table.rows[0].cells]
rows = [
[cell.text.strip() for cell in row.cells]
for row in table.rows[1:]
]
result['tables'].append({'headers': headers, 'rows': rows})
shape_info['type'] = 'table'
# 이미지
if shape.shape_type == 13: # MSO_SHAPE_TYPE.PICTURE = 13
img_info: dict = {
'name': shape.name,
'left': shape_info['left'],
'top': shape_info['top'],
'width': shape_info['width'],
'height': shape_info['height'],
'path': '',
}
if extract_images:
try:
img_bytes = shape.image.blob
ext = shape.image.ext # e.g. 'png', 'jpeg'
safe_name = re.sub(r'[^\w\-.]', '_', shape.name)
img_filename = f"slide{slide_number:03d}_{safe_name}.{ext}"
img_path = os.path.join(output_dir, img_filename)
os.makedirs(output_dir, exist_ok=True)
with open(img_path, 'wb') as f:
f.write(img_bytes)
img_info['path'] = img_path
except Exception as e:
img_info['error'] = str(e)
result['images'].append(img_info)
shape_info['type'] = 'picture'
# 커넥터/화살표 처리
if _is_connector(shape):
shape_info['type'] = 'connector'
result['shapes'].append(shape_info)
# ── 발표자 노트 ────────────────────────────
try:
if slide.has_notes_slide:
notes_text = slide.notes_slide.notes_text_frame.text.strip()
result['notes'] = notes_text
except Exception:
pass
return result
# ─────────────────────────────────────────────
# 메인 추출 함수
# ─────────────────────────────────────────────
def extract_pptx(
filepath: str,
extract_images: bool = False,
output_dir: str = '',
slide_range: str = '',
pretty: bool = False,
) -> None:
"""
PPTX 파일 전체를 파싱하여 JSON 구조를 stdout 에 출력합니다.
Args:
filepath: PPTX 파일 절대/상대 경로
extract_images: True 이면 슬라이드 이미지를 output_dir 에 PNG로 추출
output_dir: 이미지 추출 디렉토리 (기본: /tmp/pptx_<파일명>/)
slide_range: 처리할 슬라이드 범위 문자열 (예: "1-10", "" = 전체)
pretty: True 이면 JSON 들여쓰기 출력
"""
from pptx import Presentation
if not os.path.exists(filepath):
print(f"❌ 파일을 찾을 수 없습니다: {filepath}", file=sys.stderr)
sys.exit(1)
prs = Presentation(filepath)
total_slides = len(prs.slides)
filename = os.path.basename(filepath)
# 출력 디렉토리 결정
if not output_dir:
stem = re.sub(r'[^\w\-]', '_', os.path.splitext(filename)[0])
output_dir = os.path.join(tempfile.gettempdir(), f'pptx_{stem}')
# 처리 대상 슬라이드 번호 결정
if slide_range:
target_slides = parse_slide_range(slide_range, total_slides)
else:
target_slides = set(range(1, total_slides + 1))
print(
f"🔍 파싱 중: {filename} ({total_slides}장 중 {len(target_slides)}장 처리)",
file=sys.stderr,
)
if extract_images:
print(f"🖼️ 이미지 추출 디렉토리: {output_dir}", file=sys.stderr)
# 슬라이드 추출
slides_data = []
for idx, slide in enumerate(prs.slides, start=1):
if idx not in target_slides:
continue
slide_data = extract_slide(slide, idx, extract_images, output_dir)
slides_data.append(slide_data)
print(f" 슬라이드 {idx}/{total_slides}: {slide_data['title'] or '(제목 없음)'}", file=sys.stderr)
output = {
'filename': filename,
'filepath': os.path.abspath(filepath),
'total_slides': total_slides,
'processed_slides': len(slides_data),
'image_output_dir': output_dir if extract_images else '',
'slides': slides_data,
}
indent = 2 if pretty else None
print(json.dumps(output, ensure_ascii=False, indent=indent))
print(f"\n✅ 추출 완료: {len(slides_data)}개 슬라이드", file=sys.stderr)
# ─────────────────────────────────────────────
# CLI 진입점
# ─────────────────────────────────────────────
def main() -> None:
parser = argparse.ArgumentParser(
description='PPTX 기획서를 JSON 구조로 추출합니다.',
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument('filepath', nargs='?', help='PPTX 파일 경로')
parser.add_argument('--extract-images', action='store_true', help='슬라이드 이미지를 PNG로 추출')
parser.add_argument('--output-dir', default='', help='이미지 추출 디렉토리')
parser.add_argument('--slides', default='', help='처리할 슬라이드 범위 (예: 1-10, 5, 3-7,10)')
parser.add_argument('--auto-install', action='store_true', help='python-pptx 자동 설치')
parser.add_argument('--pretty', action='store_true', help='JSON 들여쓰기 출력')
args = parser.parse_args()
_ensure_pptx(args.auto_install)
if not args.filepath:
parser.print_help()
print("\n❌ PPTX 파일 경로를 입력해 주세요.", file=sys.stderr)
sys.exit(1)
extract_pptx(
filepath=args.filepath,
extract_images=args.extract_images,
output_dir=args.output_dir,
slide_range=args.slides,
pretty=args.pretty,
)
if __name__ == '__main__':
main()

View File

@@ -70,24 +70,26 @@ description: 번역 요청 엑셀 파일의 EN 셀을 기반으로 '번역코드
6. **참조 MD 파일 생성**
- 엑셀 저장 완료 후 로컬 참조용 MD 파일을 생성 또는 갱신한다.
- 파일 위치: `doc/translation/<엑셀파일명>.md` (확장자 `.xlsx` → `.md` 치환)
- 예) `smilegate-ax-2026.xlsx` → `doc/translation/smilegate-ax-2026.md`
- `doc/translation/` 디렉토리가 없으면 자동 생성한다.
- 파일 위치: `docs/translation/<엑셀파일명>.md` (확장자 `.xlsx` → `.md` 치환)
- 예) `smilegate-ax-2026.xlsx` → `docs/translation/smilegate-ax-2026.md`
- `docs/translation/` 디렉토리가 없으면 자동 생성한다.
- 파일 내용: 엑셀의 **전체** 번역코드(기존 + 신규)를 아래 형식으로 작성
- 헤더에서 `KO` 컬럼을 탐지하여 함께 기록한다 (개발 시 번역코드 매칭 참조용)
```markdown
# 번역 코드 참조 — <엑셀파일명>
> 생성일: YYYY-MM-DD
> 원본 파일: <엑셀파일 상대경로>
| 번역코드 | EN |
|----------|----|
| BRAND-smilegate-ax | Smilegate x AX |
| NAV-main | Main |
| ... | ... |
| 번역코드 | KO | EN |
|----------|----|----|
| BRAND-smilegate-ax | 스마일게이트 x AX | Smilegate x AX |
| NAV-main | 메인 | Main |
| ... | ... | ... |
```
- 파일이 이미 존재하면 **덮어쓴다** (엑셀이 단일 소스 오브 트루스).
- 생성 완료 후 경로를 사용자에게 출력한다.
- 파일이 이미 존재하면 **덮어쓰지 않는다**. 새로 생성된 코드 행만 테이블 하단에 추가(append)한다.
- 이미 파일에 존재하는 번역코드는 중복 추가하지 않는다.
- 생성 완료 후 경로와 추가된 행 수를 사용자에게 출력한다.
## 코드 작성 예시 (실제 케이스)