[02-Architect] #226 design spec + ADR-0004
- docs/design/226-catalog-gallery/README.md — 12 changed files, DisplayCategory enum, sealed CatalogItem, 5 scenarios - docs/design/226-catalog-gallery/fn-catalog_repository.md — 3-source unification algorithm + helpers - docs/design/226-catalog-gallery/fn-migration_v1_to_v2.md — first schema migration (DROP + reseed pattern) - docs/adr/0004-catalog-recategorization-and-first-migration.md — Catalog 재분류 + 첫 마이그레이션 정책 Refs #226
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
# ADR-0004: Catalog 재분류 (8 displayCategory) + 첫 schema 마이그레이션 정책
|
||||
|
||||
> **상태**: Accepted
|
||||
> **날짜**: 2026-06-12 · **결정자**: [AI] Architect · **관련 이슈**: #226
|
||||
|
||||
## 맥락 (Context)
|
||||
|
||||
Phase 2-A (#218) 종료 시점에 다음 두 문제가 동시에 드러났다.
|
||||
|
||||
1. **선택 마비**. 빈 `HabitListScreen` + 자유 입력 단일 경로만 본 첫 진입 사용자가 Tiny Habits 의 "어떤 작은 행동부터?" 에 멈췄다. 시드 카탈로그 (protocols 34 + frame 30 + reward 30 + break 8 + diet 5 = 107) 가 APK 에 포함되나 UI 노출 0.
|
||||
2. **카테고리 미스매치**. 기존 `Protocols.category` CHECK 제약은 `health|meditation|motivation|habit|learning|diet` 6 값으로, Planner 가 정의한 8 displayCategory (빛/일주기·수면·운동·영양·집중·회복·감정·없애기) 와 직접 매핑되지 않는다. seed JSON 에 새 분류를 박아넣으면 CHECK 위배.
|
||||
|
||||
또한 본 이슈는 **앱 출시 이후 첫 schema 변경** 이다 — Phase 1 (#204) 의 `schemaVersion = 1` 이 `onUpgrade: assert(false, 'Phase 1 has no upgrade path')` 로 마감되어 있어, 마이그레이션 정책의 첫 사례를 세워야 한다.
|
||||
|
||||
## 결정 (Decision)
|
||||
|
||||
다음 3개 결정을 함께 채택한다.
|
||||
|
||||
1. **`DisplayCategory` enum 도입** (UI/조회 모델 레이어). 값 8개: `lightCircadian, sleep, movement, nutrition, focusCognition, recoveryStress, emotionRelationship, breakHabit`. Protocol 의 source `category` 와는 분리된 별도 개념.
|
||||
2. **`Protocols.category` CHECK 를 7 값으로 갱신** (breakHabit 제외 — Break 는 별도 테이블이라 매핑 1:1 자동). schema v1 → v2 마이그레이션으로 적용.
|
||||
3. **첫 마이그레이션 전략 = "DROP + reseed"** — read-only catalog 테이블에 한해 `m.deleteTable(protocols)` → `m.createTable(protocols)` (v2 CHECK) → `kSeededV1Flag` 클리어 → 다음 부팅이 `SeedImporter` 재실행. user 테이블 (Habits, Phases, TrackerEntries 등) 무변화.
|
||||
|
||||
## 근거 (Rationale)
|
||||
|
||||
- **enum 분리 (1)** 가 source-of-truth 명확화. Protocol DB 의 `category` 는 ETL 단계의 분류축이고, UI 의 `displayCategory` 는 사용자 멘탈 모델 축이다. 두 개념을 한 컬럼에 욱여넣으면 BreakProtocol (이미 자체 `category` 보유) 과 DietPattern (`category` 없음) 통합 시 깨진다.
|
||||
- **schema 마이그레이션 (2)** 이 가상 매핑 (in-code dict) 대비 명시적. CHECK 위배가 컴파일/런타임 즉시 노출되므로 시드 작성 실수를 더 일찍 잡는다. 또한 향후 신규 Protocol 추가 시 정합성 자동 보장.
|
||||
- **DROP + reseed (3)** 가 read-only 카탈로그에 대해 안전한 최단 경로. user 데이터를 건드리지 않아 위험도 낮음. ALTER TABLE 로 CHECK 만 갱신하는 방식은 SQLite 에서 직접 지원하지 않아 어차피 임시 테이블 + 복사 + rename 패턴이 필요한데, read-only catalog 라면 reseed 가 더 단순.
|
||||
|
||||
### 트레이드오프
|
||||
- ✅ user 데이터 0 영향이라 사용자 위험 없음.
|
||||
- ✅ 향후 catalog 갱신 (#FF1+) 시 동일 패턴 재사용 가능 — 마이그레이션 N→N+1 = "DROP catalog 테이블 + flag 클리어" 라는 일관 규칙.
|
||||
- ❌ 시드 파일이 손상되면 부팅 실패 (CHECK 위배 throw). 그러나 본 함수는 unit test 가 명시적 검증.
|
||||
- ❌ `BreakProtocol` 의 카테고리는 `displayCategory.breakHabit` 단일 매핑 → 사용자가 "8 카테고리" 라고 들었지만 그 중 1개는 source 1개에서만 채워짐. 의도된 단순화 (Planner AC-4 가 8개 명시).
|
||||
|
||||
## 결과 (Consequences)
|
||||
|
||||
- **긍정**:
|
||||
- UI 코드가 source 구분 없이 `displayCategory` 만으로 필터링/그룹핑 가능 → 갤러리/카드 구현 단순.
|
||||
- schema 진화 패턴 (v1→v2) 의 첫 reference 코드가 repo 에 등재. v3+ 시점에 재활용.
|
||||
- 시드 갱신 워크플로 = "JSON 수정 → version bump → reseed" 한 줄 정리.
|
||||
- **부정 / 비용**:
|
||||
- 사용자가 v1 DB 를 가진 채 앱 업데이트 시 첫 부팅에서 `protocols` 테이블이 DROP 됨 (user 테이블은 무사). dev 단말 한정 — 베타 외부 배포 전이라 영향 0.
|
||||
- `schemaVersion` 2 도입으로 `MigrationStrategy.onUpgrade` 의 dispatch 로직 진입. `if (from == 1 && to >= 2)` 분기 + 알 수 없는 경로 assert. 향후 v3 도입 시 같은 dispatch 에 한 줄 추가.
|
||||
- **후속 작업**:
|
||||
- `migration_v1_to_v2_test.dart` 가 user 테이블 무변화 + CHECK 갱신 + flag 클리어를 명시 검증 (3 케이스 필수).
|
||||
- 향후 `DisplayCategory` 값 추가 시 본 ADR 갱신 (값 enum 확장은 Accepted 유지, 의미 변경 시 Superseded).
|
||||
|
||||
## 검토한 대안 (Alternatives Considered)
|
||||
|
||||
- **A. 가상 매핑 (in-code dict, schema 무변화)** — Protocol.category 6 값을 그대로 두고, `DisplayCategory.fromProtocolCategory()` 가 코드 내 dict 로 8 값에 매핑.
|
||||
- 기각 사유: CHECK 가 6 값 그대로라 seed JSON 의 새 분류를 못 받음. Planner AC-4 의 "8 카테고리 재분류" 를 문자 그대로 충족하지 못함. 신규 Protocol 추가 시 CHECK 위배가 runtime 까지 안 잡힘.
|
||||
|
||||
- **B. `display_category` 신규 컬럼 추가 + 기존 `category` 유지** — protocols 에 새 컬럼 ADD COLUMN 으로 8 값 저장, 기존 6 값은 유산 컬럼.
|
||||
- 기각 사유: 컬럼 2개의 의미가 겹쳐 SoT 분기. ETL 단계에서 매번 두 컬럼 채워야 함. 6 값 컬럼이 영원히 dead weight.
|
||||
|
||||
- **C. catalog 테이블 전체를 in-memory 로 전환 (JSON 직독)** — DB 에서 catalog 제거, 부팅 시 JSON 만 메모리 로드.
|
||||
- 기각 사유: Phase 1 (#204) 의 ADR-0002 결정 정규화 = "어떻게" 를 뒤집는 큰 변경. read-only 라도 reference / habit 와의 FK 연결 손실. 본 이슈 scope 초과.
|
||||
|
||||
- **D. 마이그레이션 우회 (사용자 앱 재설치 안내)** — 첫 마이그레이션 회피용으로 사용자에게 재설치 권장.
|
||||
- 기각 사유: dev 단말 한정 단계라 기술적으로 가능하나, 첫 마이그레이션 reference 코드를 미루는 것 자체가 부채. 어차피 v3+ 에서 같은 결정 다시 해야 함 — 일찍 답하는 게 싸다.
|
||||
Reference in New Issue
Block a user