# 챕터 → 씬 변환 가이드

> 마크다운 원고를 인터랙티브 씬 데이터로 변환하는 규칙

---

## 개요

웹소설 원고(마크다운)를 뷰어에서 사용할 수 있는 씬/라인 데이터로 변환합니다.

**변환 스크립트:** `scripts/convert-chapter.ts`

```bash
npx tsx scripts/convert-chapter.ts "content/rovel/{작품명}/chapters/001.md" chapter-001
```

---

## 마크다운 작성 규칙

### 라인 타입별 문법

| 타입 | 마크다운 문법 | 예시 |
|------|-------------|------|
| 나레이션 | 일반 텍스트 | `협회 의무실.` |
| 대화 | 쌍따옴표로 감싸기 | `"선생님, 안녕하세요."` |
| 생각 | 홑따옴표로 감싸기 | `'이건 좀 이상한데...'` |
| 효과음 | `- `로 시작 | `- 삐빅.` |
| 씬 구분 | `***` | (빈 줄로 감싸서 사용) |
| 시스템 카드 | `[제목]`으로 시작 | `[진맥 판독]` |

### 시스템 카드 형식

```markdown
[진맥 판독]
대상: 박준혁
맥상: 침세(沈細), 삽맥(澁脈)
해석: 기혈 순환 정체, 어혈 잔존
부위: 좌측 어깨
```

- 첫 줄: `[제목]` 형식
- 이후 줄: `필드명: 값` 형식
- 빈 줄이나 다른 라인 타입을 만나면 종료

### 씬 구분자

```markdown
​
***
​
```

- `***`로 씬을 구분
- 앞뒤로 빈 줄 권장
- `​` (제로폭 공백)은 무시됨

---

## 변환 규칙

### 라인 타입 감지

```typescript
// 우선순위 순서
1. `[` 로 시작 (단, `[다음` 제외) → system
2. `- ` 로 시작 → sfx
3. `"` 로 시작하고 `"` 로 끝남 → dialogue
4. `'` 로 시작하고 `'` 로 끝남 → thought
5. 그 외 → narration
```

### 화자 감지

대화(`dialogue`) 라인에서 화자를 추론합니다:

1. **이전 3줄 컨텍스트**: 캐릭터 이름이 언급되면 해당 캐릭터로 지정
2. **대사 내용 분석**: 특징적 말투로 추론 (예: "본좌" → 자화연)
3. **주인공 여부**: "나는", "내가" 언급 시 isProtagonist = true

```typescript
// 예시: 화자 추론
"본좌에게 명령이냐?" → 자화연 (char-jahwayeon-001)
"선생님, 감사합니다." → 박준혁 (char-parkjunhyuk-001)
```

### 씬 메타데이터 감지

씬 내용에서 자동 추출:

| 메타데이터 | 감지 패턴 |
|-----------|----------|
| 시간 | `밤 11시`, `오후 5시` 등 |
| 장소 | `의무실`, `협회` 등 키워드 |
| 분위기 | `살기`, `긴장`, `평온` 등 |

### 시스템 카드 타입

| 제목 키워드 | 타입 | 플레이 가능 |
|------------|------|-----------|
| 진맥 판독, 분석, 스캔 | diagnosis | O |
| 잠재력 분석 | potential | O |
| 환자 심리 분석, 환자 상태 | psychology | O |
| 은밀 치유 | skill_active | O |
| 신규 기능 감지, 잠재 해방 | skill_unlock | X |
| 경고, 숨겨진 | warning | X |
| 그 외 | info_update | X |

### is_replyable (플레이 가능 씬)

씬에 플레이 가능한 시스템 카드가 1개 이상 있으면 `is_replyable = 1`

---

## 주의사항

### 작성 시 주의

1. **따옴표 일관성**: 대화는 반드시 `""`로, 생각은 `''`로
2. **효과음 형식**: `- ` 뒤에 공백 필수
3. **시스템 카드**: 빈 줄로 다른 내용과 구분
4. **씬 구분**: `***`만 단독으로 사용

### 변환 시 주의

1. **캐릭터 ID 매핑**: 새 캐릭터 추가 시 스크립트에 매핑 추가 필요
2. **SQL 이스케이프**: 작은따옴표는 `''`로 이스케이프됨
3. **시스템 카드 테이블**: 현재 스키마에 없음 → `lines` 테이블에 `type: "system"`으로 저장

### 현재 스키마 제약

```
scenes 테이블: is_playable 대신 is_replyable 사용
lines 테이블: system_card_id 컬럼 없음
system_cards 테이블: 현재 없음 (추후 마이그레이션 필요)
```

---

## 변환 워크플로우

```
1. 마크다운 원고 작성
   content/rovel/{작품명}/chapters/XXX.md

2. 변환 스크립트 실행
   npx tsx scripts/convert-chapter.ts {파일경로} {chapter-id}

3. 생성된 SQL 확인
   content/rovel/{작품명}/chapters/XXX-seed.sql

4. seed-d1.sql에 병합
   cat XXX-seed.sql >> scripts/seed-d1.sql

5. D1에 적용
   npx wrangler d1 execute rovel-db --local --file=scripts/seed-d1.sql
   npx wrangler d1 execute rovel-db --remote --file=scripts/seed-d1.sql

6. 배포
   npx @cloudflare/next-on-pages
   npx wrangler pages deploy .vercel/output/static --project-name=rovel
```

---

## 예시

### 입력 (마크다운)

```markdown
# 1화. 퇴근은 아직

협회 의무실.

한약 냄새가 은은하게 퍼져있는 이 작은 공간.

- 한숨.

'야간 당직자가 지각하고 있으니까.'

***

- 삐빅.

"한 선생님, 퇴근 아직이세요?"

김 과장이었다.

[진맥 판독]
대상: 김 과장
맥상: 허맥(虛脈)
해석: 과로 상태
```

### 출력 (구조)

```
Scene 1: 프롤로그
├── line-001-01-001: narration "협회 의무실."
├── line-001-01-002: narration "한약 냄새가..."
├── line-001-01-003: sfx "한숨."
└── line-001-01-004: thought "야간 당직자가..."

Scene 2: 김 과장 등장
├── line-001-02-001: sfx "삐빅."
├── line-001-02-002: dialogue "한 선생님..." (speaker: null)
├── line-001-02-003: narration "김 과장이었다."
└── line-001-02-004: system "[진맥 판독]..." (is_replyable: 1)
```
