# 05 — 시드 데이터 전략 (#204) > 부모 설계서: [README.md](./README.md) ## 1. 전략 결정: build-time 번들 + first-run JSON import ### 검토한 대안 | 옵션 | 장점 | 단점 | 채택 | |------|------|------|------| | A. build-time codegen (`*.dart` 생성) | 컴파일 타임 안전, JSON parse 실패 0 | 시드 변경 시 codegen 강제. PR diff 가 dart 자동 생성. | ✗ | | **B. asset 번들 JSON + 첫 실행 import** | SoT `schema/*.json` 호환 형식 그대로. 사람이 읽기 쉬움. diff 명료. | 첫 실행 시 ~1 초 추가 시간. parse 실패 가능성 (테스트로 커버) | **✓** | | C. 원격 fetch | 동적 업데이트 | 네트워크 의존 (Phase 1 out of scope) | ✗ | ## 2. 파일 배치 ``` app/assets/seed/ ├── protocols.json # Array — ~34 항목 ├── break_protocols.json # Array — 8 항목 ├── common_frames.json # Array — 5 항목 ├── methodologies.json # Array — 21 항목 ├── frame_patterns.json # Array — ~30 항목 ├── reward_menu_items.json # Array — ~30 항목 └── references.json # Array — ~50 항목 ``` - 각 파일 형식 = `schema/.schema.json` 을 만족하는 객체 배열. - 인코딩 = UTF-8 (한국어 텍스트). - `pubspec.yaml` 의 `flutter.assets:` 에 `assets/seed/` 등록. ## 3. 로딩 순서 (참조 무결성) ``` 1. references.json ← 다른 시드가 reference_ids 로 참조 2. protocols.json 3. break_protocols.json 4. common_frames.json 5. methodologies.json 6. frame_patterns.json 7. reward_menu_items.json ``` - 각 파일 batch insert 는 단일 트랜잭션. - 전체 시드 import 도 단일 transaction. 1 파일이라도 실패하면 rollback. ## 4. 첫 실행 감지 `meta_kv` 테이블에 `key='seeded_v1'` 행 존재 여부로 판단. ```dart Future _alreadySeeded(AppDatabase db) async { final row = await db.metaKvDao.find('seeded_v1'); return row?.value == 'true'; } ``` ## 5. import 시퀀스 (의사코드 — fn-seed-importer.md 참조) ```dart Future importIfNeeded() async { if (await _alreadySeeded(db)) return; await db.transaction(() async { for (final entry in _seedOrder) { final raw = await rootBundle.loadString(entry.assetPath); final list = jsonDecode(raw) as List; for (final json in list) { final adapted = entry.adapter(json as Map); await entry.dao.insertAll(adapted); } } await _ensureLocalDefaultUser(); await db.metaKvDao.put('seeded_v1', 'true'); }); } ``` ## 6. 시드 데이터 출처 ↔ 카탈로그 매핑 (검증용) | 시드 파일 | SoT 마크다운 | 추출 단위 | 추산 행 | |-----------|-------------|----------|---------| | protocols.json | huberman-protocols.md §1~§5 + nutrition/diet-protocols.md §1 | `###` 헤더 단위 | ~34 | | break_protocols.json | habit-breaking-protocols.md §2 | `###` 헤더 단위 (8 카테고리) | 8 | | common_frames.json | habit-breaking-protocols.md §1 | `###` 5 개 | 5 | | methodologies.json | habit-todo-methodologies.md §1~§21 | `###` 헤더 단위 | 21 | | frame_patterns.json | habit-todo-methodologies.md "흔한 끊기 목표 변환 30선" | 표 row 단위 | ~30 | | reward_menu_items.json | habit-todo-methodologies.md "권장 리워드 메뉴 30선" | 표 row 단위 | ~30 | | references.json | 4 SoT 의 각 출처 섹션 합계 | 인용 1 건 | ~50 | ## 7. 손 작성 vs 자동 추출 본 Phase 는 **손 작성** (Developer 가 SoT 마크다운을 보고 JSON 작성). 이유: - 자동 추출 파서가 또 다른 코드 (스크립트) 의 SoT 가 됨 — 본 Phase 범위 초과. - 손 작성으로 카탈로그 schema 의 비논리적 누락을 발견할 수 있음 (피드백 루프). Phase 2 에서 `scripts/extract_seed.py` 검토 (OQ-2). ## 8. user 기본 row 생성 ```dart Future _ensureLocalDefaultUser() async { await db.users.insertOnConflictUpdate(UsersCompanion.insert( id: 'u_local_default', createdAt: DateTime.now().toIso8601String(), locale: const Value('ko-KR'), timezone: const Value('Asia/Seoul'), )); } ``` ## 9. 테스트 fixture ``` test/fixtures/seed/ ├── protocols_small.json # 2 항목 (1 health + 1 diet) ├── break_protocols_small.json # 1 항목 ├── common_frames_small.json # 1 항목 ├── methodologies_small.json # 3 항목 (core engine 3 개) ├── frame_patterns_small.json # 2 항목 ├── reward_menu_items_small.json # 5 항목 (T0~T4 각 1) └── references_small.json # 2 항목 ``` - `SeedImporter` 의 단위 테스트는 fixture 디렉토리로 path override. ## 10. 운영 메모 - 시드 변경 시 (v1 출시 후 카탈로그 한 행 추가): asset JSON 수정 → schemaVersion 그대로 → migration 으로 신규 row insert 별도 처리. 자동 import 는 `seeded_v1` flag 가 true 라 다시 안 돔. - 카탈로그 갱신은 별도 메커니즘 (v2 또는 `seeded_v2` 플래그 신설) — Phase 2 결정.