Files
life-helper/data-model.md
joungmin 29befe4d97 [Architect] Refs #204 — apply OQ decisions: diet_pattern (19th), ADR-0002 normalize dose_variants
- 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>
2026-06-11 17:13:04 +09:00

254 lines
10 KiB
Markdown

# life-helper 데이터 모델 (v1)
> **목적**: 본 프로젝트의 3개 마크다운 SoT(`huberman-protocols.md`, `habit-todo-methodologies.md`, `habit-breaking-protocols.md`)와 메모리 원칙(`feedback_sustainable_minimal.md`, `feedback_reward_ladder.md`, `feedback_positive_framing.md`)을 앱에서 쓸 수 있는 데이터 구조로 정형화.
>
> **선택된 형식**: JSON Schema (Draft 2020-12). SQL/TS 변환은 추후. user_id 기반 → 단일 사용자 local-first와 멀티 사용자 클라우드 모두 호환.
>
> **machine-readable schema**: `./schema/*.json` (엔티티별 분리)
> **본 문서**: 엔티티 관계 + 설계 의도 + 운영 규칙 + 사용 예시
---
## §1. 엔티티 분류
### Catalog (read-only seed data, 앱에 번들)
| 엔티티 | SoT 원본 | 설명 |
|--------|---------|------|
| `protocol` | `huberman-protocols.md` + `nutrition/diet-protocols.md` | 원자 프로토콜 (category로 health/meditation/motivation/habit/learning/diet 구분) |
| `break_protocol` | `habit-breaking-protocols.md` §2 | 끊기 카테고리 8개 |
| `common_frame` | `habit-breaking-protocols.md` §1 | 끊기 공통 프레임 5개 (Dopamine Reset 등) |
| `methodology` | `habit-todo-methodologies.md` | 21개 습관/투두 방법론 |
| `frame_pattern` | 동 문서 §"언어 프레이밍" | L0→L1/L2/L3 변환 예시 |
| `reward_menu_item` | 동 문서 §"리워드 시스템" | 추천 보상 메뉴 |
| `reference` | 모든 SoT의 출처 필드 | DOI/URL/책 인용 |
| `diet_pattern` | `nutrition/diet-protocols.md` §2 | 식이 패턴 5개 (지중해/저탄수/IF/Plant-Based/한식) — 의견 분열 영역, 사용자 선택 메뉴 |
### User Data (사용자가 생성·변경)
| 엔티티 | 설명 | 핵심 제약 |
|--------|------|----------|
| `user` | 사용자 (단일/멀티 호환) | `id` 필수 |
| `phase` | 6주 사이클 1개 | user당 동시 active 1개 |
| `habit` | 사용자 습관 (build 또는 break) | active build ≤ 3, active break ≤ 1 |
| `tracker_entry` | 일일 ○/공백 체크 | (habit_id, date) unique |
| `if_then_rule` | Implementation Intentions | habit당 ≤ 3개 권장 |
| `lapse_log` | LEARN 5필드 (재발 일지) | break 습관 위주 |
| `urge_log` | 충동 발생/통과 기록 (선택) | break 습관 위주 |
| `reward_declaration` | 사전 선언 보상 (T0~T4) | phase 시작 시점에만 |
| `reward_claim` | 실제 milestone 달성 기록 | declaration 참조 |
| `reflection` | 주/월 회고 텍스트 | 자유 |
---
## §2. 관계 다이어그램
```
user (1)
├─< phase (n) -- 시점별 6주 사이클
│ └─< reward_declaration (n) -- 이 phase 동안의 T0~T4 사전 선언
└─< habit (n)
├── protocol_id or break_protocol_id (catalog 참조, XOR)
├── frame {level, original_text, framed_text} -- L2 또는 L3 강제
├── anchor {when, after_what, where} -- Tiny Habits
├── stack_position -- after-this-then-that 순서 (선택)
├─< if_then_rule (n)
├─< tracker_entry (n, daily)
├─< lapse_log (n) -- break 위주
├─< urge_log (n, 선택) -- break 위주
└─< reward_claim (n) -- milestone 달성 시
```
**catalog 측**:
```
protocol ──< protocol_reference >── reference (M:N)
break_protocol ──< break_reference >── reference
methodology ──< methodology_reference >── reference
```
---
## §3. 운영 규칙 (DB level constraint로 적용)
본 프로젝트 메모리에서 도출된 가드레일을 schema에 박는다.
| ID | 규칙 | 적용 위치 | 출처 |
|----|------|----------|------|
| R1 | `user`당 동시 active build habit ≤ 3 | `habit.status='active' AND type='build'` 집계 | `feedback_sustainable_minimal.md` |
| R2 | `user`당 동시 active break habit ≤ 1 | `habit.status='active' AND type='break'` 집계 | `project_habit_breaking_module.md` |
| R3 | `habit.frame.level` ∈ {`L2`, `L3`} 강제 (L0 거부) | enum 제약 | `feedback_positive_framing.md` |
| R4 | `reward_declaration``phase.started_at` 후 7일 안에만 생성 | timestamp 검증 | `feedback_reward_ladder.md` (사전성) |
| R5 | `tracker_entry.value` ∈ {`done`, `blank`} 2값만 | enum (×·이모지 X) | 6 가드레일 #6 |
| R6 | `phase.duration_weeks` = 6 (default), 6주 전 anchor 변동 X | 변경 시 warning | 6 가드레일 #5 |
| R7 | `if_then_rule.then_action` 회피 키워드 감지 시 warning | 클라이언트 검증 | 코끼리 회피 UX |
| R8 | `habit` 일일 운영 ≤ 2분 (tracker UI 30~60초) | UX 제약 (DB X) | 6 가드레일 #2 |
| R9 | `habit.dose_variants[]` 개수 제한 없음. 단 체크인 화면은 "장소 1탭 + 컨디션 1탭 → 추천 + override" 흐름으로 R8 유지. `is_minimum=true` variant는 0개 이상 허용 (강제 X). | application + UI | `docs/adr/0001-dose-variants.md` |
| R10 | `reflection.scope='weekly'``minimum_ratio` (해당 주 done 중 is_minimum 비율) 표시. 강제 임계값 없음 — hint 용도. | application 계산 | `docs/adr/0001-dose-variants.md` |
---
## §4. 핵심 enum 정의
```yaml
HabitType: ["build", "break"]
HabitStatus: ["active", "paused", "completed", "abandoned"]
FrameLevel: ["L0", "L1", "L2", "L3"] # L0/L1은 입력 차단 (warning + 변환 제안)
TrackerValue: ["done", "blank"] # ○/공백 2값
RewardTier: ["T0", "T1", "T2", "T3", "T4"]
# T0=매일 / T1=3회 스트릭 / T2=7일 / T3=30일 / T4=42일(6주)
LapseFieldHALT: ["hungry", "angry", "lonely", "tired", "none"]
ProtocolCategory: ["health", "meditation", "motivation", "habit", "learning", "diet"]
# huberman-protocols.md §1~§5 + nutrition/diet-protocols.md (별도 모듈)
BreakCategory: ["alcohol", "nicotine", "porn_masturbation",
"social_media", "sugar", "caffeine", "cannabis", "behavioral"]
CommonFrameId: ["dopamine_reset", "urge_surf",
"environment_design", "relapse_recovery", "recovery_stack"]
```
---
## §5. JSON Schema 파일 목록 (`./schema/`)
### Catalog
- `protocol.schema.json` — Huberman atomic protocol
- `break_protocol.schema.json` — 끊기 카테고리
- `common_frame.schema.json` — 끊기 공통 프레임 5개
- `methodology.schema.json` — 21개 방법론
- `frame_pattern.schema.json` — L0→L2/L3 변환표
- `reward_menu_item.schema.json` — 보상 메뉴
- `reference.schema.json` — DOI/URL/책
### User
- `user.schema.json`
- `phase.schema.json`
- `habit.schema.json`
- `if_then_rule.schema.json`
- `tracker_entry.schema.json`
- `lapse_log.schema.json`
- `urge_log.schema.json`
- `reward_declaration.schema.json`
- `reward_claim.schema.json`
- `reflection.schema.json`
### 메타
- `enums.schema.json` — 위 §4 enum 통합 정의 ($ref로 재사용)
- `_index.json` — 전체 스키마 인덱스 + 운영 규칙 메타
---
## §6. 사용 예시 (단일 사용자, build 습관 1개)
### habit (build, L2 프레임)
```json
{
"id": "hb_01J9...",
"user_id": "u_local_default",
"type": "build",
"status": "active",
"title": "아침 햇빛 10분",
"protocol_id": "huberman_1_1_morning_sunlight",
"break_protocol_id": null,
"frame": {
"level": "L2",
"original_text": "햇빛 안 빼먹기",
"framed_text": "기상 직후 야외에서 햇빛 10분 받기"
},
"anchor": {
"when": "06:30",
"after_what": "기상 직후",
"where": "현관 앞 야외"
},
"if_then_rules": [
{ "if": "비 오는 날이면", "then": "20분으로 늘려서 우산 쓰고 나간다" }
],
"started_at": "2026-06-04",
"phase_id": "ph_2026Q2_1",
"stack_position": 1
}
```
### tracker_entry (일일)
```json
{ "id": "te_...", "habit_id": "hb_01J9...", "date": "2026-06-04", "value": "done", "logged_at": "2026-06-04T06:42:00+09:00" }
```
### reward_declaration (phase 시작 시 사전 선언)
```json
{
"id": "rd_...",
"phase_id": "ph_2026Q2_1",
"habit_id": "hb_01J9...",
"tier": "T3",
"milestone_rule": "30일 중 24일 이상 done",
"reward_text": "주말 1박 단기 여행",
"estimated_cost_krw": 150000,
"declared_at": "2026-06-04T20:00:00+09:00"
}
```
### habit (break, L3 프레임)
```json
{
"id": "hb_brk_01...",
"user_id": "u_local_default",
"type": "break",
"status": "active",
"title": "평일 무알콜",
"protocol_id": null,
"break_protocol_id": "alcohol",
"common_frame_ids": ["dopamine_reset", "environment_design", "recovery_stack"],
"frame": {
"level": "L3",
"original_text": "술 끊기",
"framed_text": "나는 평일 무알콜인 사람이다"
},
"anchor": { "when": "18:00", "after_what": "퇴근", "where": "집 거실" },
"phase_id": "ph_2026Q2_1"
}
```
### lapse_log (LEARN 5)
```json
{
"id": "ll_...",
"habit_id": "hb_brk_01...",
"date": "2026-06-10",
"label_text": "회식에서 맥주 2잔. 데이터로 처리.",
"examine_halt": ["tired", "lonely"],
"antecedent": ["회식 자리", "20시 늦은 식사", "동료 권유"],
"replan": "If 회식 공지 받으면, then 첫잔 무알콜 사전 선언 + 동료에게 알림",
"next_action": "다음 평일은 즉시 정상 진행. Never miss twice."
}
```
---
## §7. 단일 vs 멀티 사용자 호환 전략
- 모든 user data 엔티티에 `user_id` 필수 필드.
- 단일 사용자 local 앱: `user_id = "u_local_default"` 고정.
- 클라우드 멀티 사용자: auth 서비스의 sub/uid를 그대로 `user_id`로 매핑.
- catalog 엔티티에는 `user_id` 없음 (모든 사용자 공유).
- ID 형식: ULID 권장 (`hb_01J9...`). prefix는 엔티티 타입.
---
## §8. 마이그레이션 노트
- v1은 minimal viable. 다음 항목은 v2 이후 고려:
- `habit_stack` (Atomic Habits #5 — 여러 habit 묶음 운영)
- `dashboard_preference` (사용자별 트래커 UI 옵션)
- `notification_rule` (알림/cron 트리거)
- `social_link` (Recovery Stack의 "1-person check-in" 파트너)
- `integration_*` (Apple Health / Google Fit / Notion sync)
- enum 추가는 backward compatible. enum 제거/이름 변경은 마이그레이션 스크립트 필수.
---
## §9. 다음 단계 제안
1. 본 문서 + `./schema/*.json` 리뷰.
2. seed catalog 데이터 채우기 (Huberman 29개 + break 8개 + methodology 21개) — `./seed/*.json`.
3. 검증기(Ajv 등)로 schema 적합성 확인.
4. UI 와이어프레임 (트래커 30초, 대시보드 1화면 등) — 별도 문서.
5. 백엔드 결정 (SQLite local-first 권장 → 추후 Supabase 등으로 확장).