S3 · DATA MODEL2026-07-16 — 엔티티·층·관계 시각 정리

데이터 모델 — 한눈에 보는 구조

여정(Path)·수첩(Note)·여비(Budget)와 그 위의 렌즈들이 공유하는 데이터 구조를, 층(L0–L5)·엔티티·필드·관계·렌즈로 한 장에 정리합니다. 핵심은 하나 — 렌즈 수만큼 데이터를 두지 않는다. 중심 객체 하나(Event) + 성분 마스터 + 돈 4역이 전부고, 화면은 같은 데이터를 다른 각도로 보는 필터+투영입니다.

정본은 텍스트다. 필드 단위 최종 진실은 data-model.md(정본 SoT)를 따른다 — 이 HTML은 그 구조의 시각 스냅샷(2026-07-16)이며, 상충 시 md가 이긴다. 개념·서사 층(비전)은 개념 모델이, SwiftData 물리 번역은 @Model 정의서가, 프리즈 전 seam 배경은 프리즈전 리포트가 맡는다. A2 표식 = 네이티브 프리즈 전 예약된 seam(전건 null/미사용).

대 원 칙

단일 소스, 렌즈는 문(門)

모두가 공유하는 중심 객체 하나(Event) + 성분 마스터(Person·Place·Subject) + 그룹핑(Project) + 돈 4역이 전부다. 렌즈는 같은 데이터를 다른 각도에서 보는 필터 + 투영일 뿐 — 어디서 입력하든 같은 객체를 편집하고 즉시 상호 반영된다. 상호 참조용 별도 필드가 필요 없다 — 모든 렌즈가 같은 id를 본다.

세 문장으로 요약.사건이 코어다 — 한 행(row) = 인생의 한 문장(6하원칙). ② 관계는 딱 둘 — 문장 의 성분(Involvement) · 문장 사이의 참조(Link). ③ 파생은 저장하지 않는다 — 시제·롤업·곡선·겹은 계산이다(원칙 7).
이 문서를 읽는 법. 필수 = 값이 항상 있음 · 선택 = null 허용(기능이 요구할 때 채움) · A2 = 프리즈 전 예약 seam(지금은 전건 null/미사용). 층 색: L0 코어 L1 파생 L2 돈 L3 겹 L4 소유 L5 서사.
전체 지형

층 구조 — 사건 코어 + 다섯 층

가드레일이 새 개념을 '어느 층에 드는가'로 라우팅한다. L1(파생)은 저장 층이 아니라 원칙 7의 관철이라 엔티티가 없다.

L0
사건 코어
문장 + 성분 + 마스터. Event(6하의 한 문장) · Involvement(성분) · Person·Place·Subject(마스터) · Link(참조) · Project(그룹). 시간은 리터럴(start/end).
L1
파생
저장하지 않고 계산한다. 시제('지금' 대비) · 롤업(프로젝트 등장인물) · 순자산 곡선 · 미배정 자금 · 빈 구간. 엔티티 없음.
L2
돈 4역
사건에 앵커되는 경제 층. 스톡(NetWorthSnapshot) + 거처(Pocket) · 이동(Flow) · 배정(Allocation) · 목표(Goal).
L3
시나리오 겹
계산·비저장 가능세계 + 결정 연산자. Scenario + 소속 3필드(scenarioId·baseId·removed)를 Event·Flow·Goal·Allocation에 얹는다.
L4
소유·렌즈
자서전의 소유권과 문. User+RLS(모든 데이터는 한 사람) · Publication(기본 비공개) · 렌즈(데이터 안 소유) · ProjectModule.
L5
서사
그릇을 채우고 꺼낸다. Interview·Reflection·Question(엘리시테이션 엔진) · 책자·백업(산출).

엔티티 한눈에

L0 코어
EventInvolvementPersonPlaceSubjectLinkProject
L2 돈 4역
PocketNetWorthSnapshotFlowGoalAllocation
L3 겹
Scenario+ 소속 3필드
L5 서사
InterviewReflectionQuestion
L4 소유
UserPublicationProjectModule

마스터(점선) = 여러 사건에 재등장하는 참조점(Person·Place·Subject). 이들은 시나리오를 타지 않는다(하나뿐).

중심 객체 L0

Event — 인생의 한 문장 (6하원칙)

마일스톤·일정·시대를 쪼개지 않고 하나의 Event로 관리하며 kind로만 구분한다. 6하의 성분이 각자의 자리를 갖는다.

6하개념(별칭)모델
(주어)암묵 — 모든 데이터는 한 User에 귀속(표시에서만 접힘)
언제일정Event.start/end — 시제의 원천(선택값)
어디서무대Place + Involvement
누가등장인물Person + Involvement
무엇대상title + Subject + Involvement
어떻게방법Event.how (텍스트)
이유Event.why (텍스트)

Event 필드

필드뜻 · 값
id필수 식별자
title필수 요약 서술('인생의 한 문장')
kind필수 milestone(순간·점) · span(구간·작업) · era(시대·색인)
projectId선택Project. null=프로젝트 없는 순수 마일스톤·시대
start선택 시작일. 없으면 타임라인 비노출·수첩 전용(backlog). era는 필수
end선택 종료일 — span·era의 끝. milestone(점)·날짜미정=null
startPrecision선택 A2 exact|month|year|circa — 퍼지 과거('1983년쯤')를 '날짜미정'과 구분(리터럴 유지)
endPrecision선택 A2 (동일)
priority'항상 표시' 핀(이진). 축소·요약해도 보임. era는 미지정
category선택 (era 전용) 주제 트랙 — 학교·커리어·정서 등 사용자 정의(빈 값=‘기본’)
statusbacklog|todo|done|aborted — 맥락 중심 상태. aborted A2=자원 투입 후 무산된 과거 계획
why · how · note선택 이유 · 방법 · 자유 메모
scenarioId · baseId · removed시나리오 소속 3필드 → . modal 전용
kind은 4번째 축이 아니라 '모양'이다. milestone(점)·span(구간)·era(시대)는 아래 세 선택 속성과 독립이다.

세 선택 속성 — 어느 1차 렌즈에 뜨나

필드있으면 뜨는 렌즈
언제(일정)start·end여정 — 타임라인에 배치 (없으면 수첩에만)
상태(task)status + 맥락수첩 — 맥락별 '할 것/이룬 것' 롤업
돈(배정)Allocation·Goal·Flow여비 — 배정된 것만
관계 메커니즘 L0

성분과 참조 — 조인은 딱 둘

Involvement(문장 ) · Link(문장 사이). 이 둘 외의 새 조인은 페이로드가 실릴 때만 만든다(Allocation이 유일 전례 — 금액을 나름).

Involvement — 성분뜻 · 값
id필수
eventId필수Event (어느 문장의 성분인가)
targetType필수 person | place | subject
targetId필수 → 해당 마스터
role선택 null 출발 — with/host·at/from/to·acquire/use… (어휘 후행)
order선택 다중 경유 순서
attributedTo선택 A2 이 사실을 누구 증언으로 귀속하나(→Person). null=암묵 '나'. '소유≠귀속'의 씨앗
Link — 느슨한 참조뜻 · 값
id필수
fromType · fromId필수 event|reflection|project|person|place|subject|scenario
toType · toId필수 (동일)
relationType선택 null 출발 — depends-on·blocks·reflects-on·achieves·occurs-during·sameAs A2

성분 마스터 — 여러 사건의 공통 참조점

엔티티필드 (전 필드 선택 · name만 권장)
Person 누가name · relation(관계 호칭) · birthDate · gender A2 · occupation · note. ≠ User(내 기록 속 인물)
Place 어디서name · address · geo(lat/lng) · note. 같은 장소가 '한 장소의 역사'로 쌓임
Subject 무엇name · attrs(자유 키-값) · note. 구체물(자동차)도 범주(비상금)도 — 돈은 안 담고 정체만
Project 그룹title · description · status(planning|active|paused|done)

마스터는 시나리오를 타지 않는다(하나). 시변 정체성(개명·재혼)은 유예 — 지금은 Link("sameAs")로만 상관.

돈 4역 L2

돈의 4역 + 지갑

돈의 역할은 넷으로 딱 떨어지고, '인생 노드'(Project·Event·Goal·Subject)가 공통 참조점이다. 스톡은 내부 구조로 지갑(Pocket)을 갖는다 — 금융상품 = 지갑(잠김) + 계약된 이동(Flow) + 성장률의 조합이다.

엔티티질문
스톡NetWorthSnapshot지금 얼마가 있나
이동Flow돈이 언제 어떻게 드나드나
배정Allocation지금 돈이 무슨 임무를 맡았나
목표Goal얼마까지 모아야 하나
엔티티주요 필드
Pocket 지갑name · label(상품 종류명·자유) · balance(만원 — 음수 허용=부채 A2) · growth{rate,min?,band?} · lock{age|date}? · payout{monthly,years?|life?}? · contribution{monthly}? · est?
NetWorthSnapshot 스톡date · amount(자산−부채) · pocketId?(null=총액 한 줄) · note
Flow 이동label? · direction(in|out) · fromPocketId? · toPocketId? · amount · cadence(monthly|yearly|weekly|null=일회) · dayOfMonth? · startDate/endDate(반복) · eventId(일회성 앵커·필수) · subjectId? · projectId? · +3필드
Goal 목표label · targetAmount · targetDate · targetType(networth|project|event|subject) · targetId(networth면 null) · +3필드
Allocation 배정amount · targetType(project|event|goal|subject) · targetId · +3필드
핵심 규약.일회 Flow는 사건 없이 못 산다(eventId 필수) — 돈이 움직였으면 그날 무슨 일이 있었다. 시점은 앵커 start에서 파생. ② 내부 이동(from·to 둘 다 지갑) = 순자산 불변(유동성만 이동). ③ 계단(반복)/점프(일회)는 데이터가 아니라 렌더 별칭.

예측 곡선·런웨이·미배정 자금·유동성 경고는 저장하지 않고 계산한다(파생). 스냅샷=시작점 · 반복 Flow=계단 · 일회 Flow=점프 · Goal=결승선.

겹(오버레이) L3

시나리오 — 미래는 여러 벌, 결정은 하나

확정 세계 위에 미래 대안을 겹쳐 그리고 → 견주고 → 하나를 골라 승격하고 → 이력으로 남긴다. 시나리오는 이 순환을 담는 이다 — 별도 세계가 아니라 확정 세계에 대한 diff(계산).

Scenario뜻 · 값
id · title필수 "제주 이주", "서울 잔류"
statusexploring(구상) · chosen(승격) · shelved(보류) · discarded(접음·이력 유지) · coexisting A2(비붕괴 병존)
authorLabel선택 A2 '누가 그렸나' 표시용(아버지가 그리는 내 미래 등). 진짜 소유는 user_id에서 파생
asOf · note · decidedAt어느 현실을 전제로 · 저울질 기록 · 결정 시점

소속 3필드 — Event·Flow·Goal·Allocation에 공통으로 얹음

필드
scenarioIdnull = 확정 세계(baseline). 지금까지의 모든 데이터가 자동으로 확정 세계
baseIdnull=시나리오에서 새로 그림 · 값=확정 레코드의 '시나리오판'(원본을 가림)
removedtrue=이 시나리오에선 '없던 일로'(원본 가리는 삭제 표식)
3필드는 modal(양상적 미래) 전용이다 A2. baseId(가림)·removed(뺄셈)는 '기준세계 있음·붕괴 지향'에 용접돼 있어, 관점/저자축(인식적·대등)을 오버로드하면 한 관점이 다른 관점을 지운다. 관점파생 미래의 양상 절반은 status=coexisting으로, 관점/저자 절반은 유예된 L4 View로 간다.

원칙: 시나리오는 미래에만 · 마스터는 안 탐 · 기본 뷰=확정 세계 · 수정판은 문장 단위(성분도 복사) · 참조는 수정판 우선 · 중첩 없음(평평).

서사 L5

인터뷰 / 성찰 — 타임라인을 끌어내는 엔진

인터뷰는 과거에 한정되지 않는다. 회상형 질문 → 과거 Event, 지향형 질문 → 미래 Event를 생성하며 성분(인물·장소)도 함께 채운다.

엔티티주요 필드
Reflection 성찰interviewId? · questionId? · anchorTime?(가리키는 시점) · content · voice A2(누구의 목소리→Person) · media[](멀티모달) · sensitive(기본 true) · createdAt
Interview 세션mode(self|facilitated) · intervieweeId(User) · interviewerId · date · title · context · status(in_progress|done)
Question 질문text · type(random|typology|dialogue) · orientation(retrospective|prospective|open) · theme. 유일한 공용 엔티티(User 비귀속·어드민 관리)

ReflectionLink로 사건·인물·장소·대상·시나리오를 가리킨다("왜 망설이는가"가 데이터로 남음). 가장 사적인 데이터라 오프라인 우선·암호화 1순위.

보는 방식 L4

렌즈 — 같은 데이터, 여러 개의 문

렌즈 = 필터 + 투영. 1차 렌즈는 사건을 축으로, 2차 렌즈는 성분 마스터에서 출발해 1차를 역투영(재사용)한다. 마스터 관리 화면은 따로 없다 — 렌즈가 곧 관리 화면.

렌즈읽는 것
여정 1차·언제start 있는 Event[] + '지금 선' 파생 + 시나리오 가지(비교)
수첩 1차·맥락Project[] + Event[](날짜미정 backlog 포함) + 등장인물 롤업(파생)
여비 1차·돈Pocket[] + 스냅샷 + 배정 + 예측 곡선(Flow) + Goal[] + 시나리오 곡선 비교
주소록 2차·누가Person[] + 성분 역참조(그 사람과의 여정·다가올 만남)
지도 2차·어디서Place[] + 성분 역참조(그 장소의 사건·시대 무대·동선)
사물함 2차·무엇Subject[] + 돈 4역 참조(충전 게이지) + 이벤트 이력
갤러리 2차·미디어Reflection.media의 시간·성분 투영

상태 모델 — 맥락 중심 3(+1)상태

backlog(착상·날짜미정 허용) · todo(지금 이 맥락에서 할 것·승격 시 맥락 필수) · done(맥락에서 바뀐 것) · aborted A2(무산). status ≠ 성취(outcome)done성취Reflection 링크로 여정에 마일스톤 승격(파생). 개입 3지점: 착수 · 성취 확정 · 갈림길 결정.

소유 · 공개 · 파생 L4 L1

모든 데이터는 한 사람의 것

모든 최상위 엔티티는 정확히 한 User에 귀속된다(구현: user_id 컬럼 + RLS 2겹). 유일한 예외는 공용 질문 풀 Question.

엔티티주요 필드
User 계정uid(불변=Supabase Auth id) · displayName? · birthDate? · gender? · createdAt. 프로필 필수 없음
Publication 공개targetType · targetId · channel · publishedAt · revokedAt(null=공개 중). 레코드 없으면 비공개
ProjectModule 모듈projectId · type · scope(common|custom) · storage(lens=데이터 없음·필터 / store=고유 데이터 소유) · order · config
파생 — 저장하지 않고 계산한다 L1(원칙 7). 시제(now 대비 있었다/있다/갈 것이다) · '지금 위치·동행'(now가 걸친 사건의 성분) · 프로젝트 등장인물 롤업 · 순자산 곡선·런웨이·목표도달 · 미배정 자금(스냅샷 − Σ배정) · 시나리오 확정세계 재합성 · 타인 병치·색인(co-narrate)(무소유 CQRS 읽기 투영). 저장 엔티티가 아니라 계산이라 '한 User 귀속' 불변식을 문자 그대로 지킨다.

교환·수명: 백업 = md/csv/미디어 zip(무손실 자기 직렬화, 정본 경로). 표준 export(schema.org/CIDOC)는 좁은 목적의 손실 편의 투영으로 격하 — 상세는 기록의 수명 — 자기 구조로.

모델을 단일하게 유지하는 법

확장 가드레일 — 새 개념이 들어올 때

어떤 개념이 제안되든 이 순서를 통과해야 모델이 단일하게 유지된다. 앞의 표(층·엔티티·관계)는 전부 이 일곱 질문의 산물이다.

  1. 파생 가능한가? — 저장 전에 계산으로 될지 먼저(시제·롤업·노출의 전례).
  2. 어느 층인가? — 문장·성분·마스터·돈4역·겹·서사·계정 중 하나.
  3. 성분인가 참조인가?Involvement 또는 Link. 새 조인은 페이로드 실릴 때만.
  4. 돈이면 4역 중 무엇인가? — 스톡·이동·배정·목표 밖의 돈 개념은 안 만든다.
  5. 어휘를 미리 못 박나?role·relationType처럼 비워 두고 출발할 수 있으면 비운다.
  6. 이력이 남는가? — 도메인 기록은 삭제 대신 보존(discarded·revokedAt), 확장은 nullable 추가.
  7. 의무 3종User 귀속+RLS · 백업(md/csv) 매핑 · 별칭 사전 등재.
프리즈 가드 (Q8 — 마이그레이션 시점) A2. 위 일곱을 통과해도 그 변경이 비-additive(필드 TYPE 변경 · 불변식 완화 · 의미 재해석)면 SwiftData @Model 프리즈에 적대다. 그러면 nullable 곁필드·enum 확장·부호 개방으로 예약하거나 명시적 갈아엎기로 유예한다.

A2 seam — 프리즈 전 예약(전건 null/미사용)

seam여는 미래
Event.startPrecision퍼지 과거(SEM 유연 시간)
Event.status=aborted무산된 과거 계획(가지 않은 길의 과거 축)
Involvement.attributedTo다관점 과거 — 강형 accordingTo·Claim의 record-grain 씨앗
Reflection.voice제3자·고인의 증언을 그의 목소리로
Link "sameAs"교차정체성(개명·재혼) — 훗날 PersonEpithet(validFrom/validTo)
Pocket.balance 음수부채 = 음의 지갑
Scenario.coexisting · authorLabel관점파생 미래(비붕괴) · 훗날 authorityId

조직 원리 = 소유 ≠ 귀속(소유=user_id·귀속=Involvement.attributedTo). 범용 Frame/Context 층은 거부 — 메커니즘은 둘(modal=Scenario, epistemic=유예 View). 상세 = 프리즈전 리포트.