# 아키텍처 Ascii Never Die의 시스템 설계 문서. ## 계층 구조 ``` ┌─────────────────────────────────────────────────┐ │ Presentation │ │ features/ (화면, 위젯, 컨트롤러) │ │ front / new_character / game / arena / settings │ ├─────────────────────────────────────────────────┤ │ Domain │ │ core/engine/ (게임 로직 서비스) │ │ core/model/ (데이터 모델) │ ├─────────────────────────────────────────────────┤ │ Data │ │ data/ (정적 게임 데이터) │ │ core/storage/ (세이브/설정 저장소) │ │ core/infrastructure/ (광고, IAP) │ └─────────────────────────────────────────────────┘ 의존 방향: Presentation → Domain → Data (역방향 금지) ``` ## 핵심 데이터 흐름 ``` ProgressLoop (타이머) │ ▼ tickOnce() ProgressService.tick() │ ├─→ CombatTickService (전투 틱 처리) ├─→ LootHandler (전리품 처리) ├─→ ExpHandler (경험치/레벨업) ├─→ SkillService (스킬 진행) ├─→ QuestCompletionHandler(퀘스트 완료) ├─→ StoryService (스토리 진행) └─→ GameMutations (상태 변경 적용) │ ▼ GameState (Stream) │ ├─→ UI 갱신 (StreamBuilder) └─→ SaveManager 자동 저장 ``` ### 전투 사이클 ``` TaskGenerator.생성() → 몬스터 조우 │ ▼ CombatTickService.처리() ├─→ PlayerAttackProcessor (플레이어 공격) ├─→ CombatCalculator (데미지 계산) └─→ 결과 판정 ├─ 승리 → LootHandler → ExpHandler → 다음 태스크 └─ 패배 → DeathHandler → ResurrectionService ``` ## 디렉토리별 책임 ### `data/` -- 정적 게임 데이터 Config.dfm에서 추출한 종족, 직업, 스킬, 포션, 스토리 데이터. Dart const로 관리하며 런타임 변경 없음. | 파일 | 역할 | |------|------| | `pq_config_data.dart` | 게임 원본 정적 데이터 (몬스터, 아이템, 주문 등) | | `class_data.dart` | 직업 정의 + 특성 | | `race_data.dart` | 종족 정의 + 특성 | | `skill_data.dart` | 68개 스킬 정의 | | `potion_data.dart` | 포션 데이터 | | `story_data.dart` | 스토리/액트 데이터 | | `game_text_l10n.dart` | 게임 텍스트 다국어 매핑 | ### `core/engine/` -- 게임 로직 (30개 서비스) 타이머 기반 메인 루프에서 호출되는 순수 게임 로직. UI 의존 없음. | 서비스 | 역할 | |--------|------| | `progress_loop.dart` | 타이머 기반 메인 루프 (틱 발행) | | `progress_service.dart` | 틱 수신 → 서비스 오케스트레이션 | | `combat_tick_service.dart` | 전투 틱 처리 | | `combat_calculator.dart` | 데미지/방어/크리티컬 계산 | | `player_attack_processor.dart` | 플레이어 공격 처리 | | `death_handler.dart` | 사망 처리 | | `resurrection_service.dart` | 부활 처리 | | `loot_handler.dart` | 전리품 드롭 | | `exp_handler.dart` | 경험치/레벨업 | | `item_service.dart` | 아이템 생성/비교 | | `shop_service.dart` | 상점 매매 | | `market_service.dart` | 시장 거래 | | `skill_service.dart` | 스킬 진행/레벨업 | | `skill_auto_selector.dart` | 스킬 자동 선택 | | `potion_service.dart` | 포션 수집/사용 | | `quest_completion_handler.dart` | 퀘스트 완료 처리 | | `story_service.dart` | 스토리/액트 진행 | | `act_progression_service.dart` | 액트 전환 | | `arena_service.dart` | 아레나 랭킹/매칭 | | `arena_combat_simulator.dart` | 아레나 전투 시뮬레이션 | | `chest_service.dart` | 보물상자 처리 | | `reward_service.dart` | 보상 분배 | | `return_rewards_service.dart` | 복귀 보상 계산 | | `stat_calculator.dart` | 스탯 총합 계산 | | `task_generator.dart` | 태스크(전투/이동/상점) 생성 | | `game_mutations.dart` | GameState 변경 함수 | | `character_roll_service.dart` | 캐릭터 스탯 롤 (3d6) | ### `core/model/` -- 데이터 모델 freezed + json_serializable로 불변(immutable) 모델 생성. 직렬화/역직렬화 자동 처리. 주요 모델: `GameState`, `SaveData`, `CombatStats`, `EquipmentItem`, `ItemStats`, `MonetizationState`, `SkillSystemState`, `ProgressState`, `HallOfFame` ### `core/storage/` -- 저장 시스템 | 파일 | 역할 | |------|------| | `save_manager.dart` | 자동/수동 저장 관리 | | `save_service.dart` | 세이브 슬롯 CRUD | | `save_repository.dart` | 파일시스템 I/O | | `save_integrity.dart` | HMAC-SHA256 무결성 검증 | | `settings_repository.dart` | SharedPreferences 설정 | | `hall_of_fame_storage.dart` | 명예의 전당 저장 | | `statistics_storage.dart` | 통계 저장 | ### `core/infrastructure/` -- 외부 서비스 | 파일 | 역할 | |------|------| | `ad_service.dart` | Google AdMob 래퍼 (IAdService 구현) | | `iap_service.dart` | in_app_purchase 래퍼 (IIAPService 구현) | ### `features/` -- 화면 (Presentation) 각 화면은 독립된 디렉토리. controllers/managers/pages/widgets로 세분화. | 디렉토리 | 화면 | 구성 | |----------|------|------| | `front/` | 타이틀/세이브 선택 | 프론트 스크린, 세이브 피커 | | `new_character/` | 캐릭터 생성 | 종족/직업 선택, 스탯 롤, 이름 입력 | | `game/` | 메인 게임 | 7개 탭 페이지, 모바일/데스크톱 레이아웃 | | `arena/` | 아레나 PvP | 셋업, 전투, 결과 | | `hall_of_fame/` | 명예의 전당 | 영웅 목록, 상세 정보 | | `settings/` | 설정 | 사운드, 언어, 계정 | ### `shared/` -- 공용 컴포넌트 - `animation/` -- ASCII 아트 애니메이션 시스템 (Canvas 기반 렌더링, 캐릭터/몬스터/무기 프레임) - `widgets/` -- 레트로 UI 위젯 (RetroButton, RetroPanel, RetroProgressBar, RetroDialog 등) - `theme/` -- ASCII 컬러 팔레트, 레트로 테마 상수 ## DI 구조 GetIt 서비스 로케이터 패턴으로 인터페이스 기반 의존성 주입. ``` core/di/ ├── service_locator.dart # GetIt 인스턴스 + 등록 ├── i_ad_service.dart # 광고 서비스 인터페이스 └── i_iap_service.dart # IAP 서비스 인터페이스 ``` ```dart // 등록 (main.dart에서 1회 호출) sl.registerLazySingleton(() => IAPService.createInstance()); sl.registerLazySingleton(() => AdService.createInstance()); // 사용 final iap = sl(); ``` 인터페이스를 통해 테스트 시 목(mock) 교체 가능. ## 수익화 시스템 ### IAP (인앱결제) - 상품: `remove_ads_and` (광고 제거 + 프리미엄) - 구매 상태: `flutter_secure_storage`에 암호화 저장 - 영수증 검증: Google Play RSA 서명 로컬 검증 (`pointycastle`) - 앱 재설치 시 자동 복원 지원 - `MonetizationState` (freezed 모델)로 구매/광고 상태 통합 관리 ### AdMob (광고) - 리워드 광고: 속도 부스트, 복귀 보상 2배 - 인터스티셜 광고: 부활 시 - IAP 구매자는 모든 광고 자동 비활성화 - 릴리즈 빌드에서 치트 메뉴 완전 차단 (`kDebugMode` 가드) ## 저장 시스템 ### 세이브 파일 - JSON 직렬화 (`SaveData` → `json_serializable`) - HMAC-SHA256 체크섬으로 무결성 검증 (`save_integrity.dart`) - 파일시스템 기반 저장 (`path_provider`) - 자동 저장: 레벨업, 퀘스트 완료, 주기적 타이머 ### 설정 - `SharedPreferences`로 경량 설정 저장 (사운드, 언어 등) ### 보안 저장 - IAP 구매 상태: `flutter_secure_storage` (플랫폼 키체인/키스토어) ## 테스트 전략 ``` test/ ├── core/ │ ├── engine/ # 게임 엔진 서비스 단위 테스트 (12개) │ ├── model/ # 모델 직렬화/상태 테스트 (2개) │ ├── storage/ # 저장소 테스트 (1개) │ └── util/ # 유틸리티/밸런스 테스트 (3개) ├── features/ # 위젯/컨트롤러 테스트 (3개) ├── regression/ # 결정적 게임 시뮬레이션 회귀 테스트 (1개) └── helpers/ # 목 팩토리, 테스트 셋업 ``` ### 테스트 원칙 - **엔진 로직 우선**: 게임 엔진 서비스에 집중 (전투, 아이템, 스킬, 상점, 아레나, 포션, 복귀보상) - **결정적 시뮬레이션**: `DeterministicRandom`으로 동일 시드 → 동일 결과 보장 - **목 팩토리**: `test/helpers/mock_factories.dart`로 GameState, CombatStats 등 재사용 가능한 테스트 데이터 제공 - **회귀 테스트**: 전체 게임 루프를 N틱 시뮬레이션하여 밸런스 변경 감지 ### 실행 ```bash flutter test # 전체 실행 flutter test test/core/engine/ # 엔진 테스트만 ```