- Add 3 AI agents (writing, revision, story-continuity specialists) - Add 4 slash commands (rovel.create, write, complete, seed) - Add novel creation/writing rules - Add Novelpia reference data (115 works, 3328 chapters) - Add CLAUDE.md and README.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
454 lines
13 KiB
Markdown
454 lines
13 KiB
Markdown
---
|
|
description: 웹소설 챕터를 씬/라인 데이터로 변환하여 D1 시드 SQL 생성
|
|
---
|
|
|
|
## User Input
|
|
|
|
```text
|
|
$ARGUMENTS
|
|
```
|
|
|
|
User input **must** be considered (if not empty).
|
|
|
|
## Overview
|
|
|
|
마크다운 원고를 인터랙티브 씬/라인 데이터로 변환하여 D1 시드 SQL을 생성합니다.
|
|
|
|
**실행 방법:**
|
|
- `/rovel.seed content/rovel/협회 소속 양호선생님/chapters/001.md` (단일 파일)
|
|
- `/rovel.seed 협회 소속 양호선생님 1화` (작품명 + 화수)
|
|
- `/rovel.seed 협회 소속 양호선생님 1-8화` (범위)
|
|
- `/rovel.seed 협회 소속 양호선생님 전체` (모든 챕터)
|
|
|
|
**워크플로우:**
|
|
```
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ Phase 1: 입력 파싱 │
|
|
│ - 파일 경로 또는 작품명+화수 파싱 │
|
|
│ - 작품 폴더 및 캐릭터 ID 매핑 로드 │
|
|
└─────────────────────────────────────────────────────────┘
|
|
↓
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ Phase 2: 마크다운 파싱 │
|
|
│ - 씬 구분 (*** 기준) │
|
|
│ - 라인 타입 감지 (대화/나레이션/효과음 등) │
|
|
│ - 시스템 카드 파싱 │
|
|
└─────────────────────────────────────────────────────────┘
|
|
↓
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ Phase 3: 메타데이터 추출 │
|
|
│ - 화자 추론 (컨텍스트 + 말투 분석) │
|
|
│ - 씬 정보 추출 (시간/장소/분위기) │
|
|
│ - is_playable 판정 │
|
|
└─────────────────────────────────────────────────────────┘
|
|
↓
|
|
┌─────────────────────────────────────────────────────────┐
|
|
│ Phase 4: SQL 생성 │
|
|
│ - INSERT 문 생성 │
|
|
│ - 파일 저장 및 안내 │
|
|
└─────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 1: 입력 파싱
|
|
|
|
### 입력 유형 분석
|
|
|
|
```yaml
|
|
Case A - 절대/상대 경로:
|
|
입력: content/rovel/협회 소속 양호선생님/chapters/001.md
|
|
해석: 해당 파일 직접 처리
|
|
|
|
Case B - 작품명 + 화수:
|
|
입력: 협회 소속 양호선생님 3화
|
|
해석: content/rovel/협회 소속 양호선생님/chapters/003.md
|
|
|
|
Case C - 작품명 + 범위:
|
|
입력: 협회 소속 양호선생님 1-8화
|
|
해석: 001.md ~ 008.md 순차 처리
|
|
|
|
Case D - 작품명 + 전체:
|
|
입력: 협회 소속 양호선생님 전체
|
|
해석: chapters/ 폴더의 모든 .md 파일
|
|
```
|
|
|
|
### 캐릭터 ID 매핑 로드
|
|
|
|
작품 폴더의 `인물목록.md`를 읽어 캐릭터 이름 → ID 매핑 생성:
|
|
|
|
```yaml
|
|
매핑 생성 규칙:
|
|
- 주인공 (한시우): char-{영문이름}-001
|
|
- 히로인/주요 인물: char-{영문이름}-001
|
|
- 조연: char-{영문이름}-001
|
|
|
|
예시 (협회 소속 양호선생님):
|
|
한시우: char-hansiuw-001 (주인공)
|
|
자화연: char-jahwayeon-001
|
|
박준혁: char-parkjunhyuk-001
|
|
김 과장: char-kimgwajang-001
|
|
루나: char-luna-001
|
|
```
|
|
|
|
### ID 체계
|
|
|
|
```yaml
|
|
작품 ID: work-{작품영문코드}-001
|
|
예: work-yanghosam-001
|
|
|
|
챕터 ID: chapter-{3자리화수}
|
|
예: chapter-001, chapter-008
|
|
|
|
씬 ID: scene-{3자리화수}-{2자리순서}
|
|
예: scene-001-01, scene-008-03
|
|
|
|
라인 ID: line-{3자리화수}-{2자리씬순서}-{3자리라인순서}
|
|
예: line-001-01-001, line-008-03-015
|
|
|
|
시스템 카드 ID: syscard-{3자리화수}-{2자리씬순서}-{2자리순서}
|
|
예: syscard-001-02-01
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 2: 마크다운 파싱
|
|
|
|
### 라인 타입 감지 규칙
|
|
|
|
```typescript
|
|
// 우선순위 순서로 적용
|
|
1. 빈 줄만 있음 → skip
|
|
2. `***` → divider (씬 구분)
|
|
3. `[제목]` 로 시작 (단, [다음 제외) → system (시스템 카드 시작)
|
|
4. `- ` 로 시작 → sfx (효과음)
|
|
5. `"` 로 시작하고 `"` 로 끝남 → dialogue (대화)
|
|
6. `'` 로 시작하고 `'` 로 끝남 → thought (속마음)
|
|
7. 그 외 → narration (나레이션)
|
|
```
|
|
|
|
### 시스템 카드 파싱
|
|
|
|
```markdown
|
|
입력:
|
|
[진맥 판독]
|
|
[대상: 박준혁, 28세, B급 헌터]
|
|
[외상: 좌측 어깨 베임 (깊이 1.2cm)]
|
|
[내상: 마력 과다 사용으로 인한 피로 누적]
|
|
|
|
출력:
|
|
{
|
|
type: "diagnosis",
|
|
title: "진맥 판독",
|
|
content: [
|
|
{ label: "대상", value: "박준혁, 28세, B급 헌터" },
|
|
{ label: "외상", value: "좌측 어깨 베임 (깊이 1.2cm)" },
|
|
{ label: "내상", value: "마력 과다 사용으로 인한 피로 누적" }
|
|
],
|
|
summary: "대상: 박준혁",
|
|
isPlayable: true
|
|
}
|
|
```
|
|
|
|
### 시스템 카드 타입 매핑
|
|
|
|
| 제목 키워드 | 타입 | isPlayable |
|
|
|------------|------|------------|
|
|
| 진맥 판독, 분석, 스캔 | diagnosis | true |
|
|
| 잠재력 분석 | potential | true |
|
|
| 환자 심리 분석, 환자 상태 | psychology | true |
|
|
| 은밀 치유 | skill_active | true |
|
|
| 신규 기능 감지, 잠재 해방 | skill_unlock | false |
|
|
| 경고, 숨겨진 | warning | false |
|
|
| 일일 진료 현황, 권장 행동 | info_update | false |
|
|
| 그 외 | info_update | false |
|
|
|
|
---
|
|
|
|
## Phase 3: 메타데이터 추출
|
|
|
|
### 화자 추론 규칙
|
|
|
|
```yaml
|
|
1. 이전 3줄 컨텍스트 분석:
|
|
"자화연이 고개를 들었다."
|
|
"......" → speaker: char-jahwayeon-001
|
|
|
|
2. 대사 내 특징적 표현:
|
|
"본좌" → 자화연
|
|
"선생님" → 박준혁 (주인공에게 말할 때)
|
|
|
|
3. 주인공 여부:
|
|
- 내면 독백 ('생각') → isProtagonist: true
|
|
- "나는", "내가" 주어 → isProtagonist: true
|
|
```
|
|
|
|
### 씬 메타데이터 추출
|
|
|
|
```yaml
|
|
시간 감지:
|
|
- "밤 11시", "새벽 2시", "오후 5시" 등
|
|
- 첫 번째 매칭 사용
|
|
|
|
장소 감지:
|
|
- "의무실", "협회", "던전" 등 키워드
|
|
- 씬 첫 부분에서 추출
|
|
|
|
분위기 감지:
|
|
- "살기", "긴장", "평온", "따뜻" 등
|
|
- 시스템 카드나 나레이션에서 추출
|
|
```
|
|
|
|
### is_playable 판정
|
|
|
|
```yaml
|
|
씬이 플레이 가능한 경우:
|
|
1. isPlayable: true인 시스템 카드가 1개 이상
|
|
2. 주요 캐릭터와의 대화 장면
|
|
3. 선택지가 제시될 수 있는 상황
|
|
|
|
is_playable: true 조건:
|
|
- diagnosis, potential, psychology, skill_active 타입 카드 존재
|
|
- 주요 히로인과 첫 대면 장면
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 4: SQL 생성
|
|
|
|
### 출력 파일
|
|
|
|
```yaml
|
|
단일 파일:
|
|
입력: 001.md
|
|
출력: 001-seed.sql
|
|
|
|
범위/전체:
|
|
입력: 1-8화
|
|
출력: chapters-seed.sql (병합된 파일)
|
|
```
|
|
|
|
### SQL 템플릿
|
|
|
|
```sql
|
|
-- Chapter chapter-{NNN} scenes and lines
|
|
-- Auto-generated from markdown
|
|
|
|
-- Delete existing data for this chapter
|
|
DELETE FROM system_cards WHERE line_id IN (SELECT id FROM lines WHERE scene_id IN (SELECT id FROM scenes WHERE chapter_id = 'chapter-{NNN}'));
|
|
DELETE FROM lines WHERE scene_id IN (SELECT id FROM scenes WHERE chapter_id = 'chapter-{NNN}');
|
|
DELETE FROM scenes WHERE chapter_id = 'chapter-{NNN}';
|
|
|
|
-- 챕터가 없으면 생성
|
|
INSERT OR IGNORE INTO chapters (id, work_id, number, title, is_free, price, status, view_count, created_at, updated_at)
|
|
VALUES (
|
|
'chapter-{NNN}',
|
|
'{work_id}',
|
|
{number},
|
|
'{title}',
|
|
{is_free},
|
|
{price},
|
|
'published',
|
|
0,
|
|
{timestamp},
|
|
{timestamp}
|
|
);
|
|
|
|
-- Scene 1: {title}
|
|
INSERT INTO scenes (id, chapter_id, "order", title, location, time, mood, is_playable, play_description, character_ids, created_at) VALUES (
|
|
'scene-{NNN}-{NN}',
|
|
'chapter-{NNN}',
|
|
{order},
|
|
'{title}',
|
|
'{location}',
|
|
'{time}',
|
|
'{mood}',
|
|
{is_playable},
|
|
'{play_description}',
|
|
'{character_ids_json}',
|
|
{timestamp}
|
|
);
|
|
|
|
-- Lines for scene {N}
|
|
INSERT INTO lines (id, scene_id, "order", type, content, speaker_id, is_protagonist, created_at) VALUES
|
|
('line-{NNN}-{NN}-{NNN}', 'scene-{NNN}-{NN}', {order}, '{type}', '{content}', {speaker_id}, {is_protagonist}, {timestamp}),
|
|
...;
|
|
|
|
-- System cards for scene {N}
|
|
INSERT INTO system_cards (id, line_id, type, title, content, summary, is_playable, choice_label, created_at) VALUES
|
|
('syscard-{NNN}-{NN}-{NN}', 'line-{NNN}-{NN}-{NNN}', '{type}', '{title}', '{content_json}', '{summary}', {is_playable}, '{choice_label}', {timestamp});
|
|
```
|
|
|
|
### SQL 이스케이프 규칙
|
|
|
|
```yaml
|
|
작은따옴표: ' → ''
|
|
줄바꿈: \n → 그대로 (TEXT 필드)
|
|
JSON: 쌍따옴표 사용, 이스케이프
|
|
NULL: speaker_id 없으면 NULL (문자열 'NULL' 아님)
|
|
```
|
|
|
|
---
|
|
|
|
## 실행 예시
|
|
|
|
### 예시 1: 단일 파일
|
|
|
|
```
|
|
/rovel.seed content/rovel/협회 소속 양호선생님/chapters/008.md
|
|
```
|
|
|
|
**출력:**
|
|
```
|
|
## 씬 변환 완료
|
|
|
|
**파일**: `content/rovel/협회 소속 양호선생님/chapters/008.md`
|
|
**챕터 ID**: chapter-008
|
|
|
|
### 생성된 데이터
|
|
|
|
| 항목 | 개수 |
|
|
|------|------|
|
|
| 씬 | 12개 |
|
|
| 라인 | 156개 |
|
|
| 시스템 카드 | 3개 |
|
|
| 플레이 가능 씬 | 2개 |
|
|
|
|
### 등장 캐릭터
|
|
|
|
| 캐릭터 | ID | 대사 수 |
|
|
|--------|-----|---------|
|
|
| 루나 | char-luna-001 | 24 |
|
|
| 한시우 | (주인공) | 18 |
|
|
|
|
### 생성된 SQL
|
|
|
|
**파일**: `content/rovel/협회 소속 양호선생님/chapters/008-seed.sql`
|
|
|
|
### 다음 단계
|
|
|
|
1. SQL 확인:
|
|
cat content/rovel/협회 소속 양호선생님/chapters/008-seed.sql
|
|
|
|
2. 로컬 D1 적용:
|
|
npx wrangler d1 execute rovel-db --local --file=content/rovel/협회 소속 양호선생님/chapters/008-seed.sql
|
|
|
|
3. 원격 D1 적용:
|
|
npx wrangler d1 execute rovel-db --remote --file=content/rovel/협회 소속 양호선생님/chapters/008-seed.sql
|
|
```
|
|
|
|
### 예시 2: 범위 변환
|
|
|
|
```
|
|
/rovel.seed 협회 소속 양호선생님 1-8화
|
|
```
|
|
|
|
**출력:**
|
|
```
|
|
## 씬 변환 완료 (8개 챕터)
|
|
|
|
### 변환 결과
|
|
|
|
| 화수 | 씬 | 라인 | 시스템 카드 | 플레이 가능 |
|
|
|------|-----|------|-------------|-------------|
|
|
| 1화 | 12 | 89 | 2 | 1 |
|
|
| 2화 | 10 | 102 | 3 | 2 |
|
|
| ... | | | | |
|
|
| 8화 | 14 | 156 | 3 | 2 |
|
|
| **합계** | **92** | **1,024** | **18** | **12** |
|
|
|
|
### 생성된 SQL
|
|
|
|
**파일**: `content/rovel/협회 소속 양호선생님/chapters-seed.sql`
|
|
|
|
### 다음 단계
|
|
|
|
로컬 D1 적용:
|
|
npx wrangler d1 execute rovel-db --local --file=content/rovel/협회 소속 양호선생님/chapters-seed.sql
|
|
|
|
원격 D1 적용:
|
|
npx wrangler d1 execute rovel-db --remote --file=content/rovel/협회 소속 양호선생님/chapters-seed.sql
|
|
```
|
|
|
|
---
|
|
|
|
## 캐릭터 ID 매핑 (협회 소속 양호선생님)
|
|
|
|
```yaml
|
|
주인공:
|
|
한시우: char-hansiuw-001
|
|
|
|
히로인:
|
|
자화연: char-jahwayeon-001
|
|
루나: char-luna-001
|
|
|
|
조연:
|
|
박준혁: char-parkjunhyuk-001
|
|
김 과장: char-kimgwajang-001
|
|
김수진: char-kimsujin-001
|
|
```
|
|
|
|
### 새 캐릭터 등장 시
|
|
|
|
인물목록.md에 없는 새 캐릭터가 등장하면:
|
|
1. 경고 메시지 출력
|
|
2. 임시 ID 생성 (`char-{이름영문}-temp`)
|
|
3. 인물목록.md 업데이트 권장
|
|
|
|
---
|
|
|
|
## Reference Files
|
|
|
|
| 유형 | 경로 | 용도 |
|
|
|------|------|------|
|
|
| 변환 가이드 | `rules/chapter-to-scene.md` | 마크다운 문법 참조 |
|
|
| 인물목록 | `content/rovel/{작품명}/인물목록.md` | 캐릭터 ID 매핑 |
|
|
| 기존 시드 | `scripts/seed-d1.sql` | SQL 형식 참조 |
|
|
| DB 스키마 | `src/server/db/schema.ts` | 테이블 구조 참조 |
|
|
|
|
---
|
|
|
|
## Important Notes
|
|
|
|
### 변환 시 주의사항
|
|
|
|
```yaml
|
|
1. 캐릭터 ID 매핑:
|
|
- 인물목록.md 먼저 확인
|
|
- 없는 캐릭터는 경고 후 임시 ID
|
|
|
|
2. 시스템 카드:
|
|
- [제목] 다음 줄들이 필드
|
|
- 빈 줄이나 다른 타입 만나면 종료
|
|
- JSON 형식으로 content 저장
|
|
|
|
3. 화자 추론:
|
|
- 이전 컨텍스트 우선
|
|
- 말투 패턴으로 보조
|
|
- 불확실하면 NULL
|
|
|
|
4. SQL 이스케이프:
|
|
- 작은따옴표는 ''로
|
|
- NULL은 문자열 아닌 키워드
|
|
```
|
|
|
|
### 스키마 호환성
|
|
|
|
```yaml
|
|
현재 스키마:
|
|
- scenes.is_playable: 플레이 가능 여부
|
|
- scenes.character_ids: JSON 배열
|
|
- system_cards: lines.id로 연결
|
|
|
|
체크 필요:
|
|
- works 테이블에 작품 존재 여부
|
|
- characters 테이블에 캐릭터 존재 여부
|
|
```
|
|
|
|
### 작품별 설정
|
|
|
|
새 작품 추가 시:
|
|
1. 작품 ID 결정 (`work-{코드}-001`)
|
|
2. 캐릭터 ID 매핑 정의
|
|
3. 인물목록.md에 캐릭터 정보 확인
|