- OQ-1: dose_variants 정규화 결정을 ADR-0002 로 승격 (ADR-0001 = 왜, ADR-0002 = 어떻게). - OQ-3: nutrition diet 패턴 5개를 별도 diet_pattern 카탈로그(19번째 SoT)로 분리. · 02-catalog §8 신규, 인덱스 IDX_diet_patterns_evidence / IDX_diet_patterns_kfit. · 05-seed: diet_patterns.json (5행) 추가, 로딩 순서 끝에 배치. · 04-migrations: v1 테이블 합계 = Catalog 8 + User 11 + 부속 1 + meta_kv = 21. - README §2/§3/§6/§11 갱신: 18→19 SoT, AC-2 에 diet_pattern=5 검증 추가. - README §12 OQ → Resolved Open Questions 표 (OQ-1~OQ-8 결정 결과). - habit_dose_variant → habit_dose_variants 표기 통일. - fn-weekly-minimum-ratio, 03-drift-schema-user 의 ADR-0002 cross-link. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
6.0 KiB
6.0 KiB
05 — 시드 데이터 전략 (#204)
부모 설계서: 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<Protocol> — ~34 항목
├── break_protocols.json # Array<BreakProtocol> — 8 항목
├── common_frames.json # Array<CommonFrame> — 5 항목
├── methodologies.json # Array<Methodology> — 21 항목
├── frame_patterns.json # Array<FramePattern> — ~30 항목
├── reward_menu_items.json # Array<RewardMenuItem> — ~30 항목
├── diet_patterns.json # Array<DietPattern> — 5 항목 (OQ-3 결정)
└── references.json # Array<Reference> — ~50 항목
총 8 시드 파일 (Catalog 카탈로그 1:1). user-data 테이블은 시드 없음.
- 각 파일 형식 =
schema/<entity>.schema.json을 만족하는 객체 배열. - 인코딩 = UTF-8 (한국어 텍스트).
pubspec.yaml의flutter.assets:에assets/seed/등록.
3. 로딩 순서 (참조 무결성)
1. references.json ← 다른 시드가 reference_ids 로 참조
2. protocols.json ← diet_patterns 가 linked_protocol_ids 로 참조
3. break_protocols.json
4. common_frames.json
5. methodologies.json
6. frame_patterns.json
7. reward_menu_items.json
8. diet_patterns.json ← protocols + references 참조 (마지막)
- 각 파일 batch insert 는 단일 트랜잭션.
- 전체 시드 import 도 단일 transaction. 1 파일이라도 실패하면 rollback.
4. 첫 실행 감지
meta_kv 테이블에 key='seeded_v1' 행 존재 여부로 판단.
Future<bool> _alreadySeeded(AppDatabase db) async {
final row = await db.metaKvDao.find('seeded_v1');
return row?.value == 'true';
}
5. import 시퀀스 (의사코드 — fn-seed-importer.md 참조)
Future<void> 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<String, dynamic>);
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 |
| diet_patterns.json | nutrition/diet-protocols.md §2 | 5 개 식이 패턴 (### 헤더 단위) |
5 |
| references.json | 4 SoT 의 각 출처 섹션 합계 | 인용 1 건 | ~50 |
diet_patterns.json 시드 5 행 (예시 id)
| id | name | 비고 |
|---|---|---|
mediterranean |
지중해 식단 | PREDIMED RCT — evidence_strength=strong |
low_carb_keto |
저탄수/케토 | 당뇨 약물 복용자 medical_warning |
tre_if |
TRE / 간헐적 단식 | huberman §1.5 linked_protocol |
plant_based |
식물성 | k_diet 와 starter_levers 일부 공유 |
k_diet |
한식 기반 | korean_context_fit=high, starter '첫 끼 단백질 +30g' |
7. 손 작성 vs 자동 추출
본 Phase 는 손 작성 (Developer 가 SoT 마크다운을 보고 JSON 작성). 이유:
- 자동 추출 파서가 또 다른 코드 (스크립트) 의 SoT 가 됨 — 본 Phase 범위 초과.
- 손 작성으로 카탈로그 schema 의 비논리적 누락을 발견할 수 있음 (피드백 루프).
Phase 2 에서 scripts/extract_seed.py 검토 (OQ-2).
8. user 기본 row 생성
Future<void> _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)
├── diet_patterns_small.json # 2 항목 (k_diet + mediterranean)
└── references_small.json # 2 항목
SeedImporter의 단위 테스트는 fixture 디렉토리로 path override.
10. 운영 메모
- 시드 변경 시 (v1 출시 후 카탈로그 한 행 추가): asset JSON 수정 → schemaVersion 그대로 → migration 으로 신규 row insert 별도 처리. 자동 import 는
seeded_v1flag 가 true 라 다시 안 돔. - 카탈로그 갱신은 별도 메커니즘 (v2 또는
seeded_v2플래그 신설) — Phase 2 결정.