三國志 AI

Software Design Plan (SDP)

三國志 AI — SDP

Software Design Plan v1.0 · 2026-02-27 · 하이브리드 프로토타입

← 📋 SRS (요구사항 명세서)

1 아키텍처 개요

1.1 엔진 모듈 구조

게임 엔진은 8개 핵심 모듈로 구성되며, 각 모듈은 독립적으로 테스트 가능하다.

🔄
TurnEngine
턴 루프 관리
전체 흐름 오케스트레이션
📜
QuestEngine
퀘스트 활성화/비활성화
조건 평가·결과 처리
🧠
AdvisorAI
참모 제안 생성
능력치→퀘스트 매핑
📖
NarrationEngine
소설체 묘사 생성
300~500단어
🌍
WorldSim
NPC 세력 시뮬레이션
월드 상태 변화
🗺️
MissionTree
325개 미션 관리
분기·플래그 처리
💾
SaveSystem
자동/수동 저장
상태 직렬화
🤖
AIRouter
듀얼 모델 라우팅
경량/고급 분배

1.2 계층 구조

┌─────────────────────────────────────────────────────┐ │ UI Layer │ │ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │ │ │ 내레이션 │ │ 선택지UI │ │ 상태패널/지도 │ │ │ └─────┬────┘ └────┬─────┘ └────────┬─────────┘ │ ├────────┼────────────┼─────────────────┼─────────────┤ │ └────────────┼─────────────────┘ │ │ ▼ │ │ ┌──────────────┐ │ │ │ TurnEngine │ ← 전체 오케스트레이터 │ │ └──────┬───────┘ │ │ │ │ │ ┌───────────────┼───────────────┐ │ │ ▼ ▼ ▼ │ │ ┌─────────┐ ┌───────────┐ ┌────────────┐ │ │ │ Quest │ │ Advisor │ │ Narration │ │ │ │ Engine │ │ AI │ │ Engine │ │ │ └────┬────┘ └─────┬─────┘ └─────┬──────┘ │ │ │ │ │ │ │ └─────────────┼──────────────┘ │ │ ▼ │ │ ┌──────────────────────────────┐ │ │ │ GameState │ │ │ │ (World · Characters · Flags)│ │ │ └──────────────┬───────────────┘ │ │ │ │ │ ┌─────────────┼──────────────┐ │ │ ▼ ▼ ▼ │ │ ┌─────────┐ ┌──────────┐ ┌──────────┐ │ │ │WorldSim │ │MissionTree│ │SaveSystem│ │ │ └─────────┘ └──────────┘ └──────────┘ │ │ │ │ ┌──────────┐ │ │ │ AIRouter │ ← 모든 AI 호출 경유 │ │ └──────────┘ │ │ │ │ │ │ ┌─────┘ └─────┐ │ │ ▼ ▼ │ │ ┌──────────┐ ┌──────────┐ │ │ │경량 모델 │ │고급 모델 │ │ │ │(Flash등) │ │(Opus 등) │ │ │ └──────────┘ └──────────┘ │ └─────────────────────────────────────────────────────┘

1.3 기술 스택

계층기술비고
프론트엔드Vanilla JS + HTML/CSS프레임워크 없음 (의존성 최소화)
상태 관리순수 JS 객체 + 이벤트 버스불변 상태 패턴
API 프록시Cloudflare WorkersAI API 키 보호, 토큰 관리
호스팅Cloudflare Pages정적 파일 서빙
저장localStorage / sessionStoragePhase 2에서 서버 확장
AI 모델Gemini Flash, GPT-4o-mini, Claude Haiku (경량)
Claude Opus, GPT-4.5, Gemini Ultra (고급)
듀얼 모델 라우팅

2 데이터 모델

2.1 퀘스트 JSON 스키마

// Quest.schema.json { "id": "Q_DOM_001", // 고유 식별자 "title": "허창 시장 건설", // 퀘스트 제목 "description": "허창의 노후한 시장을...", // 소설체 설명 "category": "domestic", // 7가지 중 하나 "cost": { "days": 7, // 소요 일수 "gold": 500, // 필요 금 "grain": 0, // 필요 곡물 "troops": 0 // 필요 병력 }, "rewards": { "stats": { "politics": +1 }, // 스탯 변화 "resources": { "goldIncome": +50 }, // 자원 변화 "relations": {}, // 관계 변화 "flags": ["xuchang_market_built"] // 플래그 설정 }, "prerequisites": { "flags": ["controls_xuchang"], // 필요 플래그 "stats": { "politics": 30 }, // 최소 스탯 "relations": {} // 필요 관계값 }, "difficulty": 2, // 1~5 (쉬움~매우어려움) "risk": 1, // 1~5 (안전~매우위험) "relatedMission": null, // 연관 미션 ID "requiredAdvisorStat": "politics", // 제안에 필요한 참모 능력치 "minAdvisorStatValue": 60, // 최소 능력치 값 "characterSpecific": "cao_cao" // null이면 공용 }

2.2 게임 상태 (GameState) 구조

// GameState 최상위 구조 { "meta": { "version": "1.0.0", "character": "cao_cao", // 현재 플레이 캐릭터 "turn": 1, // 현재 턴 번호 "date": { "year": 190, "month": 3, "day": 1 }, "totalDaysElapsed": 0 }, "player": { "stats": { "politics": 78, "strategy": 90, "leadership": 85, "combat": 72, "charisma": 80 }, "resources": { "gold": 2000, "grain": 8000, "troops": 5000 }, "territories": ["dongjun"], "income": { "gold": 100, "grain": 200 } }, "advisors": [ { "id": "xun_yu", "name": "순욱", "stats": { "politics": 95, "strategy": 91, "leadership": 48, "combat": 30, "charisma": 85 }, "personality": "cautious_strategist", "loyalty": 95, "active": true } // ... more advisors ], "quests": { "active": ["Q_MIL_001", "Q_DOM_003", "Q_PER_001", "Q_DIP_002", "Q_PSN_001"], "inProgress": [ { "id": "Q_MIL_001", "startTurn": 1, "daysRemaining": 14, "assignedAdvisor": "xiahou_dun" } ], "completed": [], "failed": [] }, "missions": { "current": "M_YELLOW_TURBAN_REMNANTS", "completedMissions": [], "branchHistory": [] }, "flags": { "game_started": true, "controls_dongjun": true }, "world": { "factions": { "yuan_shao": { "territories": ["jizhou"], "troops": 50000, "hostility": 30 }, "yuan_shu": { "territories": ["nanyang"], "troops": 20000, "hostility": 20 } // ... more factions }, "season": "spring", "weather": "clear" }, "conversationHistory": [] // 최근 N턴 AI 컨텍스트 }

2.3 참모 성격 유형

성격 코드한국어제안 톤예시 인물
cautious_strategist신중한 모사위험 최소화, 장기 안목 강조순욱, 노숙
aggressive_tactician공격적 전략가기회 포착, 과감한 공세 제안곽가, 주유
loyal_warrior충의의 무장직접 출전 자원, 명예 중시관우, 황개
fierce_fighter호방한 맹장즉각 행동, 단순 명쾌장비, 정보
diplomatic_scholar외교적 문관평화적 해결, 동맹 선호간옹, 손건
pragmatic_administrator실리적 행정가효율·비용 중심, 실리 추구미축, 조무

2.4 카테고리 enum 정의

const QuestCategory = { DOMESTIC: 'domestic', // 내정 🏛️ MILITARY: 'military', // 군사 ⚔️ PERSONNEL: 'personnel', // 인사 👤 DIPLOMACY: 'diplomacy', // 외교 🤝 JUDICIAL: 'judicial', // 사법 ⚖️ PERSONAL: 'personal', // 개인 🧘 MISSION: 'mission' // 미션연동 📜 }; const AdvisorStat = { POLITICS: 'politics', // 정치 → domestic, judicial STRATEGY: 'strategy', // 지략 → military(계략), mission LEADERSHIP: 'leadership', // 통솔 → military(직접) DIPLOMACY: 'diplomacy', // 외교 → diplomacy CHARISMA: 'charisma' // 매력 → personnel, personal };

3 퀘스트 엔진 설계

3.1 핵심 클래스 구조

class QuestEngine { // 주요 메서드 getActiveQuests() → Quest[] // 현재 활성 퀘스트 목록 evaluatePrerequisites(q) → boolean // 선행 조건 충족 여부 activateQuest(questId) → void // 퀘스트 활성화 completeQuest(questId) → Rewards // 퀘스트 완료 + 보상 처리 failQuest(questId) → Penalty // 퀘스트 실패 + 페널티 처리 refreshActivePool() → void // 활성 풀 갱신 (최소 5개 유지) getMissionLinkedQuests() → Quest[] // 미션 연동 퀘스트 조회 getQuestsByCategory(cat) → Quest[] // 카테고리별 퀘스트 필터 }

3.2 활성화/비활성화 로직

1
조건 스캔 — 전체 퀘스트 풀에서 prerequisites가 충족된 것을 후보로 선정
2
미션 우선 — 미션 연동 퀘스트 중 시점이 근접한 것을 강제 포함 (최대 2개)
3
카테고리 분산 — 7가지 카테고리에서 골고루 선택. 동일 카테고리 최대 2개
4
난이도 밸런스 — 현재 플레이어 수준에 맞는 난이도 분포 유지 (쉬움 2 : 보통 2 : 어려움 1)
5
최소 5개 보장 — 위 과정으로 5개 미만이면, 조건이 가장 근접한 퀘스트를 추가 (조건 일부 완화)

3.3 동시 5개 유지 알고리즘

function refreshActivePool(gameState) { const MIN_ACTIVE = 5; const MAX_PER_CATEGORY = 2; // Step 1: 완료/실패 퀘스트 제거 removeFinishedQuests(gameState); // Step 2: 미션 연동 퀘스트 강제 삽입 const missionQuests = getMissionLinkedQuests(gameState) .filter(q => isTimeUrgent(q, gameState.meta.date)) .slice(0, 2); activateAll(missionQuests); // Step 3: 카테고리 분산 선택 const categoryCounts = countByCategory(gameState.quests.active); const candidates = getAllEligibleQuests(gameState) .filter(q => categoryCounts[q.category] < MAX_PER_CATEGORY) .sort((a, b) => priorityScore(b) - priorityScore(a)); // Step 4: 부족분 채우기 while (gameState.quests.active.length < MIN_ACTIVE && candidates.length > 0) { const next = pickDiverseCategory(candidates, categoryCounts); activateQuest(next); categoryCounts[next.category]++; } // Step 5: 여전히 부족하면 조건 완화 if (gameState.quests.active.length < MIN_ACTIVE) { const relaxed = getNearEligibleQuests(gameState); activateAll(relaxed.slice(0, MIN_ACTIVE - gameState.quests.active.length)); } }

3.4 퀘스트 결과 처리

결과처리비고
성공 rewards 적용 (스탯↑, 자원↑, 관계↑, 플래그 설정) 참모 충성도 +1~3
실패 cost 일부 손실 + 부정적 효과 (민심↓, 사기↓) 참모 충성도 -1, 재도전 쿨다운 7일
무시 시간 경과 후 자연 소멸 + 미약한 부정적 효과 예: 도적 토벌 무시 → 치안 -5
대성공 rewards 1.5배 + 추가 보너스 (인재 발견, 특수 플래그) 확률: 10~20% (참모 능력치 연동)

4 참모 AI 설계

4.1 능력치 → 제안 매핑 테이블

const STAT_TO_QUEST_MAP = { politics: { categories: ['domestic', 'judicial'], thresholds: { 30: 'basic', // 기초 퀘스트만 60: 'intermediate',// 중급 퀘스트 해금 80: 'advanced', // 고급 퀘스트 해금 95: 'legendary' // 전설 퀘스트 해금 }, bonuses: { costReduction: 0.15, // 능력치 80+ → 비용 15% 절감 successBonus: 0.10 // 능력치 80+ → 성공률 10% 상승 } }, strategy: { categories: ['military', 'mission'], // 계략 계열 thresholds: { 30: 'basic', 60: 'intermediate', 80: 'advanced', 95: 'legendary' }, special: ['espionage', 'counter_intelligence', 'predict_enemy'] }, leadership: { categories: ['military'], // 직접 군사 thresholds: { 30: 'basic', 60: 'intermediate', 80: 'advanced', 95: 'legendary' }, special: ['mass_training', 'siege', 'forced_march'] }, diplomacy: { categories: ['diplomacy'], thresholds: { 30: 'basic', 60: 'intermediate', 80: 'advanced', 95: 'legendary' } }, charisma: { categories: ['personnel', 'personal'], thresholds: { 30: 'basic', 60: 'intermediate', 80: 'advanced', 95: 'legendary' } } };

4.2 선택지 생성 로직

class AdvisorAI { /** * 매 턴 3~6개 선택지를 생성한다. * @param gameState - 현재 게임 상태 * @returns Suggestion[] - 참모별 제안 목록 */ async generateSuggestions(gameState) { const activeAdvisors = gameState.advisors.filter(a => a.active); const activeQuests = gameState.quests.active; const suggestions = []; // Step 1: 각 참모가 자신의 능력치에 맞는 퀘스트를 가져옴 for (const advisor of activeAdvisors) { const bestStat = getHighestStat(advisor); const matchingQuests = activeQuests .filter(q => matchesAdvisorStat(q, bestStat)); if (matchingQuests.length > 0) { const quest = pickBestQuest(matchingQuests, advisor); const dialogue = await generateDialogue(advisor, quest, gameState); suggestions.push({ advisor, quest, dialogue }); } } // Step 2: 퀘스트 없는 스토리 진행 선택지 추가 (0~2개) const storyOptions = await generateStoryOptions(gameState); suggestions.push(...storyOptions); // Step 3: 3~6개 범위로 조절 return clampSuggestions(suggestions, 3, 6); } }

4.3 참모 대사 생성 (AI 프롬프트)

// 참모 제안 대사 생성 프롬프트 템플릿 const ADVISOR_PROMPT = ` 당신은 삼국지 시대의 {advisor.name}입니다. 성격: {advisor.personality_description} 능력치: 정치 {advisor.stats.politics}, 지략 {advisor.stats.strategy}, 통솔 {advisor.stats.leadership}, 무력 {advisor.stats.combat}, 매력 {advisor.stats.charisma} 현재 상황: - 시기: {date.year}년 {date.month}월 - 주공: {player.character_name} - 영지: {player.territories} - 자원: 금 {player.resources.gold}, 곡물 {player.resources.grain}, 병력 {player.resources.troops} 제안할 퀘스트: - 제목: {quest.title} - 내용: {quest.description} - 비용: {quest.cost} 지침: 1. {advisor.name}의 말투와 성격에 맞게 자연스러운 대사를 작성하세요. 2. 삼국지연의 격식체를 사용하세요. 3. 퀘스트를 직접 "퀘스트"라고 부르지 마세요. 자연스러운 조언/제안으로 표현하세요. 4. 50~100자 내외로 간결하게 작성하세요. 5. 비용과 기대 효과를 자연스럽게 포함하세요. `;

4.4 참모 부재 시 처리

상황처리
해당 능력치 참모 없음 관련 카테고리 퀘스트가 제안되지 않음. 플레이어가 '직접 말하기'로 시도 가능하나 성공률 50% 감소
참모 능력치 30 미만 기초 퀘스트만 제안 가능. 비용 +30%, 성공률 -20%
참모 충성도 30 미만 제안 품질 저하 (위험한 선택지를 안전한 것처럼 포장 가능)
참모 건강 문제 해당 참모 비활성 → 다른 참모가 해당 분야 커버 (능력치 낮아도)

5 내레이션 엔진

5.1 현황 묘사 생성 (300~500단어)

매 턴 시작 시, NarrationEngine이 현재 상황을 소설체로 묘사한다. AI를 사용하되, 구조화된 프롬프트로 일관성을 유지한다.

class NarrationEngine { /** * 턴 시작 시 현황 묘사를 생성한다. * @param gameState - 현재 게임 상태 * @param recentEvents - 최근 발생한 이벤트 목록 * @returns string - 300~500단어 소설체 묘사 */ async generateTurnNarration(gameState, recentEvents) { const context = buildNarrationContext(gameState, recentEvents); const prompt = NARRATION_PROMPT_TEMPLATE.replace(placeholders, context); // 일반 턴 → 경량 모델 // 미션 분기점 → 고급 모델 const modelTier = isMissionCritical(gameState) ? 'premium' : 'light'; const narration = await aiRouter.generate(prompt, modelTier); return validateWordCount(narration, 300, 500); } }

5.2 내레이션 프롬프트 구조

const NARRATION_PROMPT = ` 당신은 삼국지연의 스타일의 소설가입니다. 현재 게임 상황을 300~500단어의 소설체로 묘사하세요. ## 필수 포함 요소 1. **시간/계절/날씨** — 현재 시기의 분위기 2. **장소** — 영지의 풍경, 건물, 분위기 3. **최근 사건 결과** — 이전 턴의 선택 결과가 어떻게 반영되었는지 4. **긴장 요소** — 주변 세력의 동향, 잠재적 위협 5. **인물 동태** — 참모들과 장수들의 모습 ## 문체 규칙 - 삼국지연의 격식체 사용 - "~하였다", "~이라" 등 과거형/서술체 - 직접 인용 대사 1~2개 포함 - 게임 용어(퀘스트, 스탯 등) 절대 사용 금지 - 자연스러운 역사 소설 느낌 유지 ## 현재 상황 데이터 {context_json} `;

5.3 퀘스트 결과 서술

퀘스트 완료/실패 시에도 결과를 소설체로 서술한다. 이 서술은 다음 턴의 현황 묘사에 자연스럽게 통합된다.

서술 유형길이모델
일반 퀘스트 성공50~100단어경량
일반 퀘스트 실패50~100단어경량
미션 연동 퀘스트 결과100~200단어고급
전투 결과200~300단어고급
외교 교섭 결과100~150단어경량

6 턴 처리 흐름도

6.1 TurnEngine 상세 흐름

class TurnEngine { async processTurn(gameState, playerChoice) { // ═══════════════════════════════════════════════ // Phase A: 결과 처리 (이전 턴의 선택) // ═══════════════════════════════════════════════ const results = await processPlayerChoice(playerChoice, gameState); applyResults(results, gameState); // ═══════════════════════════════════════════════ // Phase B: 시간 경과 // ═══════════════════════════════════════════════ advanceTime(gameState, results.daysElapsed); processIncomeAndExpenses(gameState); updateQuestTimers(gameState); // ═══════════════════════════════════════════════ // Phase C: 월드 시뮬레이션 // ═══════════════════════════════════════════════ await worldSim.simulateNPCActions(gameState); await missionTree.checkTriggers(gameState); processRandomEvents(gameState); // ═══════════════════════════════════════════════ // Phase D: 퀘스트 갱신 // ═══════════════════════════════════════════════ questEngine.processCompletions(gameState); questEngine.refreshActivePool(gameState); // ═══════════════════════════════════════════════ // Phase E: 새 턴 준비 // ═══════════════════════════════════════════════ const narration = await narrationEngine.generateTurnNarration(gameState, results); const suggestions = await advisorAI.generateSuggestions(gameState); // ═══════════════════════════════════════════════ // Phase F: 자동 저장 + UI 렌더링 // ═══════════════════════════════════════════════ saveSystem.autoSave(gameState); return { narration, suggestions, gameState }; } }

6.2 시각적 흐름도

플레이어 선택 │ ▼ ┌──────────────┐ ┌──────────────┐ │ Phase A │ │ 선택 해석 │ │ 결과 처리 │◄────│ (AI/규칙) │ └──────┬───────┘ └──────────────┘ │ ▼ ┌──────────────┐ │ Phase B │ ← 자원 수입/지출, 퀘스트 타이머 감소 │ 시간 경과 │ └──────┬───────┘ │ ▼ ┌──────────────┐ ┌──────────────┐ │ Phase C │────▶│ MissionTree │ ← 미션 트리거 체크 │ 월드 시뮬 │ │ 분기 평가 │ └──────┬───────┘ └──────────────┘ │ ▼ ┌──────────────┐ ┌──────────────┐ │ Phase D │────▶│ 최소 5개 │ ← 활성 퀘스트 풀 보충 │ 퀘스트 갱신 │ │ 유지 알고리즘│ └──────┬───────┘ └──────────────┘ │ ▼ ┌──────────────┐ ┌──────────────┐ │ Phase E │────▶│ AdvisorAI │ ← 참모 제안 3~6개 │ 새 턴 준비 │ │ 선택지 생성 │ └──────┬───────┘ └──────────────┘ │ ▼ ┌──────────────┐ │ Phase F │ ← 자동 저장 + 렌더링 │ 저장 & UI │ └──────────────┘ │ ▼ 플레이어에게 표시 (내레이션 + 선택지)

6.3 비동기 처리 순서

AI 호출이 포함된 Phase는 비동기로 처리되며, 로딩 상태를 UI에 표시한다:

PhaseAI 호출예상 소요병렬 가능
A. 결과 처리선택에 따라 (전투 등)0~2초
B. 시간 경과없음 (순수 계산)<50ms
C. 월드 시뮬NPC 행동 결정 (경량)0.5~1초
D. 퀘스트 갱신없음 (순수 계산)<50ms
E-1. 내레이션소설체 생성1~3초E-1 ∥ E-2
E-2. 참모 제안대사 생성 (3~6건)1~3초E-1 ∥ E-2
F. 저장없음<10ms

총 예상 소요: 일반 턴 2~4초, 미션 분기점 4~8초

7 모듈 의존성

7.1 호출 관계 매트릭스

행(Row)이 호출하는 쪽, 열(Column)이 호출받는 쪽이다.

호출자 ↓ / 피호출자 → Quest
Engine
Advisor
AI
Narration
Engine
World
Sim
Mission
Tree
Save
System
AI
Router
Game
State
TurnEngine
QuestEngine
AdvisorAI
NarrationEngine
WorldSim
MissionTree
SaveSystem

7.2 의존성 규칙

  • TurnEngine은 모든 모듈을 오케스트레이션하는 유일한 진입점
  • GameState는 모든 모듈이 읽지만, 수정은 TurnEngine을 통해서만
  • AIRouter는 NarrationEngine, AdvisorAI, WorldSim이 사용 (AI 호출 통합)
  • MissionTree는 QuestEngine과 WorldSim이 참조 (미션 트리거/플래그)
  • 순환 의존성 없음 — 단방향 호출 유지

7.3 파일 구조 (예정)

samguk-ai/ ├── index.html // 메인 HTML ├── css/ │ └── game.css // 전체 스타일 ├── js/ │ ├── engine/ │ │ ├── TurnEngine.js // 턴 오케스트레이터 │ │ ├── QuestEngine.js // 퀘스트 관리 │ │ ├── AdvisorAI.js // 참모 제안 생성 │ │ ├── NarrationEngine.js // 소설체 묘사 │ │ ├── WorldSim.js // NPC 세력 시뮬 │ │ ├── MissionTree.js // 미션 분기 관리 │ │ ├── SaveSystem.js // 저장/로드 │ │ └── AIRouter.js // AI 모델 라우팅 │ ├── state/ │ │ └── GameState.js // 게임 상태 관리 │ ├── ui/ │ │ ├── NarrationView.js // 내레이션 표시 │ │ ├── ChoiceView.js // 선택지 UI │ │ ├── StatusPanel.js // 상태 패널 │ │ └── MapView.js // 지도 화면 │ └── main.js // 앱 진입점 ├── data/ │ ├── quests/ │ │ ├── cao_cao.json // 조조 퀘스트 (~1000개) │ │ ├── liu_bei.json // 유비 퀘스트 (~1000개) │ │ ├── sun_jian.json // 손견 퀘스트 (~1000개) │ │ └── common.json // 공용 퀘스트 │ ├── missions/ │ │ └── mission_tree.json // 325×3 미션 분기 │ ├── characters/ │ │ └── characters.json // 783명 캐릭터 데이터 │ └── world/ │ └── map.json // 지도/거점 데이터 ├── assets/ │ ├── portraits/ // 초상화 20장 │ ├── audio/ // BGM, 효과음 │ └── maps/ // 지도 이미지 └── worker/ └── index.js // CF Worker (API 프록시)

8 테스트 계획

8.1 단위 테스트 (Unit Tests)

모듈테스트 항목케이스 수 (예상)
QuestEngine • prerequisites 평가 정확성
• 활성 풀 최소 5개 유지
• 카테고리 분산 검증
• 미션 연동 퀘스트 강제 삽입
• 보상/페널티 적용 정확성
• 중복 활성화 방지
~30
AdvisorAI • 능력치→카테고리 매핑 정확성
• 제안 수 3~6개 범위
• 참모 부재 시 처리
• 충성도 저하 시 품질 변화
• 선택지 구성 (퀘스트+스토리 혼합)
~20
GameState • 자원 변화 정확성
• 플래그 설정/확인
• 직렬화/역직렬화
• 불변 상태 패턴 준수
~15
MissionTree • 트리거 조건 평가
• 분기 선택 처리
• 플래그 연동
• 시점 기반 강제 트리거
~20
WorldSim • NPC 행동 결정 로직
• 세력 관계 변화
• 자원 시뮬레이션
~15
SaveSystem • 저장/로드 완전성
• 슬롯 관리
• 버전 호환성
~10

총 단위 테스트: ~110 케이스

8.2 통합 테스트 (Integration Tests)

시나리오검증 항목우선순위
전체 턴 사이클 Phase A~F가 순서대로 정상 실행. 상태 일관성 유지. P0
퀘스트 라이프사이클 활성화 → 진행 → 완료/실패 → 보상 → 새 퀘스트 활성화 P0
참모 제안 → 퀘스트 실행 참모가 제안한 퀘스트를 선택하면 올바르게 시작됨 P0
미션 ↔ 퀘스트 연동 미션 시점 접근 → 연동 퀘스트 활성화 → 퀘스트 결과가 미션 분기에 영향 P1
저장 → 로드 → 계속 플레이 저장 후 로드하면 모든 상태가 동일하게 복원됨 P0
10턴 연속 플레이 메모리 누수 없음, 상태 일관성, 퀘스트 풀 고갈 없음 P1
AI 호출 실패 대응 AI API 에러 시 폴백 텍스트로 정상 진행 P1
캐릭터 3명 완주 조조/유비/손견 각각 전체 시나리오 진행 가능 P2

8.3 테스트 도구

  • 단위 테스트: Vitest (빠르고 ES 모듈 네이티브 지원)
  • 통합 테스트: Playwright (브라우저 기반 E2E)
  • AI 모킹: AI API 호출을 mock하여 결정적 테스트 보장
  • 커버리지 목표: 핵심 엔진 모듈 80% 이상

9 마일스톤 (SRS 연동)

SRS의 마일스톤과 1:1 대응하며, 각 마일스톤의 기술적 산출물을 명시한다.

M1 · Week 1~2 — 코어 리팩토링 & 기반 구축
기술 산출물
• GameState 클래스 + 직렬화
• TurnEngine 기본 루프 (Phase A~F 스켈레톤)
• SaveSystem (localStorage + sessionStorage)
• AIRouter 인터페이스 + 모킹
• 프로젝트 구조 셋업 (빌드, 테스트, CI)
M2 · Week 3~4 — 퀘스트 엔진 & 참모 AI
기술 산출물
• QuestEngine 전체 구현 (활성화/비활성화/결과처리)
• AdvisorAI 제안 생성 파이프라인
• 능력치→퀘스트 매핑 테이블
• 조조 퀘스트 데이터 100개 (프로토타입용)
• 퀘스트 단위 테스트 30개
M3 · Week 5~6 — AI 통합 & 내레이션
기술 산출물
• AIRouter 실제 API 연동 (Gemini Flash, Claude Haiku 등)
• NarrationEngine 300~500단어 생성 검증
• 참모 대사 프롬프트 6가지 성격 유형
• CF Worker API 프록시
• 10턴 연속 통합 테스트 통과
M4 · Week 7~8 — 4대 시스템 & 미션 연동
기술 산출물
• 전투/내정/외교/인사 시스템 최소 구현
• MissionTree 325개 분기 데이터 로딩
• WorldSim NPC 행동 AI
• 퀘스트↔미션 연동 통합 테스트
• 조조 시나리오 30턴 플레이 가능
M5 · Week 9~10 — UI/UX & 시각/오디오
기술 산출물
• NarrationView + ChoiceView 완성
• StatusPanel + MapView 기본 구현
• 초상화 20장 연동
• BGM/효과음 시스템
• TTS 통합 (핵심 대사)
M6 · Week 11~12 — 알파 테스트
기술 산출물
• 3 캐릭터 퀘스트 데이터 완성 (각 ~1000개)
• 퀘스트 밸런싱 (비용/보상 조정)
• 버그 수정 & 에지 케이스 처리
• MJ 플레이 테스트 피드백 반영
M7 · Week 13~14 — 베타 테스트
기술 산출물
• 커뮤니티 배포 (CF Pages PWA)
• 에러 리포팅 시스템
• 피드백 수집 + 반복 개선
• 성능 최적화 (AI 호출 캐싱 등)

마일스톤별 완료 기준

마일스톤완료 기준
M1TurnEngine이 빈 게임 상태로 7단계 루프를 1회 실행 가능
M2퀘스트 엔진이 활성 5개 유지하며 참모 제안이 3~6개 생성됨
M3실제 AI가 소설체 300~500단어 묘사를 생성하고 참모 대사를 출력
M4조조로 30턴 연속 플레이 가능 (미션 분기 1개 이상 통과)
M5시각 UI로 전체 게임 플레이 가능 (텍스트 콘솔 아닌 실제 UI)
M6MJ가 캐릭터 1명을 완주하고 "재미있다"고 판단
M7외부 테스터 5명이 2시간 이상 플레이하고 긍정적 피드백