데이터 · 확장 후 — 저장소 다섯의 소유권
갱신일 2026-08-12 · 기준: 확장 개요 · AA · 현재 판 데이터 이 문서가 갖는 것: 누가 원본을 갖고, 무엇을 지워도 되고, 지운 뒤 무엇으로 되살리는가. 여기 없는 것: 테이블·칼럼·FK(erd.md) · 그래프 온톨로지(ontology.md) · 파이프라인 단계(AA §3) · 노트 삭제 화면(IA §9).
⚠︎ 이건 계획이지 현황이 아니다. 지금 서 있는 것은 현재 판 데이터 아키텍처가 갖는다.
1. 원본은 셋뿐이다
Postgres heymoa · S3 오디오(7일) · voice print. 나머지는 전부 파생이거나 휘발이다. 아래 모든 규칙이 이 한 줄에서 나온다.
| 저장소 | 무엇이 들어 있나 | 원본/파생 | 주인 | 재생성 |
|---|---|---|---|---|
Postgres heymoa | 유저·워크스페이스·프로젝트·노트 · 전사 세그먼트(+화자) · 결정·액션 아이템·근거 · 채팅·승인 · 에이전트 카드 · 연동 토큰 · 감사 이벤트 | 원본 | server | 불가. 백업 복원뿐 |
Postgres heymoa_ai | analysis_results(멱등성 캐시) · LangGraph checkpoint* | 파생 | ai | 재분석 |
| 벡터 | 전사 청크 임베딩 1536 · 확정 Decision 임베딩(C3 씨앗 · 실시간 §4) · HNSW | 파생 | ai | 재색인 |
| Neo4j | Meeting·Person·Decision·ActionItem·ExternalItem과 관계 | 파생 | server가 쓴다 · ai는 읽기 전용 | 전량 재투영 |
| S3 오디오 | 회의 녹음 Int16 PCM · 멀티파트 잔여 | 원본 · 7일 | server transcription | 불가. 지나간 소리는 못 만든다 |
| 브로커 · 큐 | STOMP 팬아웃 구독 · 파이프라인 잡 메시지 | 휘발 | server | 해당 없음 |
voice print는 여섯 번째 줄이 아니라 첫 줄 안의 별도 구역이다. heymoa 와 같은 인스턴스, biometric 스키마, 다른 키, 다른 조회 경로 — §3.
브로커·큐는 저장소가 아닌데 표에 있다. 인스턴스 밖에 상태가 사는 순간부터 백업·장애·용량을 저장소처럼 봐야 하고, SA가 소유권을 이 문서로 넘겼다(SA §2). 재생성 칸이 「해당 없음」인 이유는 되살릴 것이 없어서다 — 팬아웃으로 흐른 전사는 이미 heymoa 에 들어갔고, 잃어버린 잡은 pipeline_steps 를 훑는 워치독이 다시 낸다.
벡터는 1만 구간까지 heymoa_ai 안의 pgvector 다. 10만에서 전용 스토어로 나간다(README §7의 18번). 그 이사가 가능한 이유가 파생이기 때문이다 — 옮기는 게 아니라 새 스토어에 다시 만들고 옛 것을 버린다.
재생성 경로와 소요
| 무엇 | 입력 | 외부 API | 소요 |
|---|---|---|---|
analysis_results | transcript_segments | LLM 1회/노트 | 노트 단위. 전량은 안 한다 |
| 벡터 | transcript_segments | 임베딩 API | 노트 단위는 분. 전량 55,000건 원가는 미확인 |
| Neo4j | notes·note_participants·decisions·action_items·evidence·decision_relations | 없음 | SQL 읽기 + Cypher 쓰기. 전량 소요 미확인 — §10 리허설이 잰다 |
| voice print | 사용자가 다시 녹음 | 없음 | 자동 재생성 경로가 없다. 사람이 다시 해야 한다 |
Neo4j 재생성에 LLM이 없다는 것이 "파생"의 진짜 조건이다. 모델 호출이 끼면 결과가 결정적이지 않고, 결정적이지 않으면 재생성이 아니라 재추론이다. 그래서 LLM이 낸 후보는 Postgres에 먼저 쓰고(온톨로지 §6) 그래프는 투영으로만 만든다. 사람이 확인한 SUPERSEDES 판정과 그 부정 기록도 Postgres에 있어야 한다 — 그래프에만 두면 재투영에서 사라지고 확인 작업을 사용자가 다시 한다. 칼럼은 erd.md.
버린 대안
| 대안 | 왜 버렸나 |
|---|---|
| Neo4j를 결정·액션 아이템의 정본으로 | 백업 대상이 둘이 되고, 어긋났을 때 판정할 방법이 없다. 그래프 DB를 쓰는 시스템은 대개 여기서 무너진다 |
| 전사 본문을 벡터 스토어에만 | 임베딩 모델을 바꾸는 순간 전사가 사라진다 |
| S3 오디오를 영구 보관 | 보관 자체가 사고의 크기다(D15). 화자 분리 재실행 여유만 준다 |
2. 흐름 — 화살표마다 push 냐 pull 이냐
규칙 한 줄: 확정된 것은 push, 계속 변하는 것과 큰 바이너리는 pull.
| 화살표 | 방식 | 왜 |
|---|---|---|
| 오디오 청크 → server → Soniox → S3 | push | 스트림이다. uploadId 는 DB 에(CA §8) |
| server → speech | 잡만 push | 오디오를 페이로드에 싣지 않는다. 45분 11MB를 큐에 넣으면 재시도마다 다시 흐른다 |
| speech → S3 | pull | 큰 바이너리는 받는 쪽이 당긴다. 재시도가 공짜가 된다 |
| server → voice print | pull · 내부 1회 | 매칭 서비스만 읽는다(§3) |
| server → ai 분석 | push | 회의가 끝난 확정 전사. 한 번에 넘긴다 |
| ai → server 채팅 컨텍스트 | pull | 진행 중 회의라 매 턴 최신이 필요하다 |
| server → Neo4j | push · 비동기 | 몇 분 늦어도 아무도 모른다(온톨로지 §6) |
| ai → Neo4j | pull · 읽기 전용 | 턴당 1회 상한(온톨로지 §7) |
S1에서 10:45에 회의가 끝나면 위 화살표가 순서대로 한 번씩 돈다. 화자 분리가 실패해도 server → ai 는 돈다 — 화자 없는 요약이 요약 없는 것보다 낫다.
3. voice print — 원본이지만 옆에 두지 않는다
목소리는 생체정보다. 나머지 원본과 같은 스키마에 두면 조회·백업·로그·삭제가 전부 같이 딸려 다닌다. 그게 위험의 전부다.
| 같이 두면 벌어지는 일 | |
|---|---|
| 조회 | 노트 상세 API가 참석자를 조인하다가 임베딩까지 실어 나른다. 한 번 새면 되돌릴 수 없다 |
| 백업 | 일반 스냅숏에 생체정보가 들어간다. 스냅숏에는 삭제 요청이 안 닿는다 |
| 로그 | 슬로우 쿼리 로그에 임베딩 값이 찍힌다 |
| 삭제 | S7의 "즉시 삭제"가 CASCADE 사슬 어딘가에 걸려 소프트 삭제로 타협된다 |
나중에 분리할 수 없다. 위 넷은 전부 "이미 흘러간 곳"의 문제라서, 분리하는 시점에는 이미 늦었다.
| 규칙 | 무엇으로 강제하나 |
|---|---|
별도 스키마 biometric | 일반 커넥션 풀의 role에 USAGE 를 주지 않는다. 조인이 문법적으로 불가능해진다. 스키마·칼럼은 ERD §3가 갖는다 |
| 별도 KMS 키 | 키를 지우면 데이터가 죽는다 — 조직 단위 삭제의 최후 수단 |
| 단일 조회 경로 | 매칭 서비스 하나만 읽는다. 일반 조회 API·관리자 화면·데이터 내보내기에 노출 경로 없음 |
| organization 스코프 | 조직을 넘어 대조하지 않는다. 넘는 순간 다른 제품이 된다 |
| 임베딩 바이트는 즉시 물리 삭제 | S7에서 embedding·key_id 를 NULL로 만든다. 남는 것은 동의·철회 이력 껍데기 행뿐 — 재등록 때 "전에 지웠다"를 알아야 하고 그 이력이 감사 대상이다(ERD §3) |
| 원본 음성 미보관 | 임베딩만 남긴다. 등록용 녹음은 추출 직후 버리고, speech가 낸 회의별 화자 임베딩도 매칭 직후 폐기 — 저장하면 그것도 생체정보다 |
pyannote를 자체 GPU로 돌리는 결정(D9)이 여기서 회수된다. 화자 분리를 관리형 API로 했으면 목소리가 매 회의 국외로 나가고, 위 규칙 전부가 성립하지 않는다 — §11.
S1에서 최유진은 voice print를 등록하지 않았다. 매칭은 시도조차 하지 않는다. 최유진은 화자 4 로 남고, 김민수가 화자 4 → 최유진 으로 교정한다. 그 교정은 transcript_segments 의 화자 칼럼을 채우고, voice print를 만들지 않는다. S2의 외부 게스트 이정훈(외부) 도 같다 — 계정 없는 이름표는 그 노트 안에서만 사는 문자열이다.
4. S6 — 노트를 지우면 다섯에서 무슨 일이
김민수가 노트 하나를 지운다. 다섯 저장소를 한 트랜잭션에 묶을 방법은 없다.
원본 삭제와 outbox 기록이 같은 트랜잭션이어야 한다. 이 한 줄이 설계 전부다 — 원본을 먼저 지우고 outbox를 나중에 쓰면 그 사이 크래시한 노트가 영원한 고아가 된다. 지금 판의 구멍이 정확히 그것이다(현재 판 ERD §11 ③).
대상값은 넷이다 — S3·NEO4J·VECTOR·AI_CACHE(ERD §9). heymoa_ai 는 heymoa 와 다른 database라 notes CASCADE가 애초에 닿지 않는다. VECTOR 가 전사 청크 임베딩을, AI_CACHE 가 analysis_results 와 그 노트의 LangGraph 체크포인트를 진다. 둘을 한 대상값으로 묶지 않는 이유는 재시도 단위가 다르기 때문이다 — 임베딩 삭제는 인덱스를 건드리고 캐시 삭제는 안 건드린다. 한쪽이 실패했을 때 다른 쪽까지 다시 도는 것을 막는다.
| 버린 대안 | 왜 |
|---|---|
| 2PC / 분산 트랜잭션 | S3와 Neo4j가 참여하지 못한다. 참여하는 둘만 묶어도 반쪽이다 |
| 파생을 먼저 지우고 원본을 나중에 | 원본 삭제가 실패하면 사용자가 요약 없는 반쪽 노트를 본다. 더 나쁘다 |
| 삭제 API를 저장소마다 동기 호출 | 셋 중 하나가 느리면 삭제 버튼이 30초 돈다. Neo4j가 죽어 있으면 삭제 자체가 막힌다 |
| 아무것도 안 지우고 "파생이라 괜찮다" | 지금 하고 있는 것. 검색과 브리핑에 지운 회의가 계속 나온다 |
실패하면 무엇이 남나
| 어디서 죽나 | 남는 것 | 사용자에게 보이나 | 청소 |
|---|---|---|---|
| 트랜잭션 1 | 아무것도 안 지워진다 | 노트 그대로 | 다시 누른다 |
| S3 | 오디오 (최대 7일) | 아니오 | lifecycle이 알아서 지운다 — 자동 청소가 있는 유일한 저장소 |
heymoa_ai | 임베딩 · 캐시 행 · checkpoint | 예 — 검색에 지운 회의가 뜬다 | outbox 재시도 → 주간 스윕 |
| Neo4j | Decision·ActionItem 노드 | 예 — 브리핑·MCP가 지운 회의를 근거로 든다 | outbox 재시도 → 스윕 → 전량 재투영 |
제일 나쁜 둘이 벡터와 그래프다. 그래서 방어선을 하나 더 둔다 — 조회 결과는 언제나 noteId 를 달고 나오므로, server가 Postgres에서 살아 있는 노트만 통과시킨다. 고아가 남아 있어도 화면에는 안 나온다. 시나리오 S3에서 박준호가 heymoa.search_decisions 를 부를 때도 같은 필터를 지난다.
고아 청소는 주간 스윕이다. 벡터·Neo4j의 noteId 집합에서 notes.id 를 뺀 나머지를 지운다. 이 스윕이 0이 아니면 outbox가 새고 있다는 신호다 — 청소가 아니라 계측이 목적이다. 주기가 야간이 아니라 주간인 이유가 그것이다.
남는 것: voice print. 노트 소유가 아니라 사용자 소유다. 삭제 확인 화면이 이 구분을 말해야 한다(IA §9).
5. S7 — voice print를 지우면
최유진이 계정 설정에서 voice print를 지운다.
| 무엇 | 어떻게 되나 | 왜 |
|---|---|---|
biometric 스키마의 임베딩 바이트 | 즉시 물리 삭제 (embedding·key_id NULL) | §3 |
| 동의·철회 이력 껍데기 행 | 남는다 | 재등록 때 "전에 지웠다"를 알아야 한다. 감사 대상이다(§7) |
| 매칭 옵트인 플래그 | false | 앞으로 대조하지 않는다 |
| 과거 노트의 화자 라벨 | 남는다 | 아래 |
Neo4j ATTENDED · ASSIGNED_TO | 남는다 | 참석과 담당은 회의 시점의 사실이다 |
| S3 오디오 | 관계없다 (이미 7일에 사라진다) | voice print 삭제는 녹음 삭제가 아니다 |
| 회의별 화자 임베딩 | 애초에 없다 | §3 |
왜 과거 라벨을 안 지우나 — 셋.
- 라벨은 회의 기록의 일부다. 그 회의록은 최유진 혼자의 것이 아니라 참석자 4명의 기록이다.
- 상당수는 사람이 만든 것이다. S1의
화자 4 → 최유진은 voice print가 아니라 김민수가 붙였다. 목소리를 지운다고 김민수가 한 판단이 취소되지 않는다. - 지우기 시작하면 이력이 부서진다.
ASSIGNED_TO가 끊기고, 액션 아이템의 담당자가 사라지고, 브리핑이 "누가 맡았는지 모르는 일"을 쌓는다. 지운 것은 목소리인데 조직의 기록이 손상된다.
개별 라벨을 지우고 싶으면 그 노트에서 교정한다. 삭제 화면이 이 경로를 말해야 한다(IA §8).
계정 삭제는 다른 문제다 — 유저를 가리키는 FK에 CASCADE도 SET NULL도 없어서 지금은 삭제가 실패한다(현재 판 ERD §11 ⑤). §13으로 내린다.
6. 보존 기간
| 데이터 | 어디 | 산다 | 정하는 것 |
|---|---|---|---|
| 오디오 원본 | S3 | 7일 (D15) · 미완료 멀티파트는 1일 | lifecycle. 조직이 늘릴 수 있다 |
| 전사 세그먼트·화자 · 결정·액션·근거 · 에이전트 카드 | heymoa | 노트와 같이 | 삭제만이 끝. 카드는 §8 |
| 회의별 화자 임베딩 | 없음 | 매칭 직후 폐기 | §3 |
| voice print | biometric | 사용자가 지울 때까지 | §5 |
| 임베딩 | 벡터 | 노트와 같이 (outbox) | §4 |
analysis_results | heymoa_ai | 90일 (추정) | 멱등성 캐시일 뿐이다. 오래된 것을 들고 있을 이유가 없다 |
LangGraph checkpoint* | heymoa_ai | 마지막 턴 +30일 (추정) | 지우면 멀티턴 맥락만 사라진다. 표시용 히스토리는 server에 있다 |
| Neo4j 노드 | Neo4j | 노트와 같이 | 파생이라 독립 수명이 없다 |
| 감사 이벤트 | heymoa | 노트를 지워도 남는다 | §7 |
7. 감사 로그
삭제 기록이 삭제와 함께 사라지면 감사가 아니다. §12가 이 문장을 설계 전제로 들고 있으니 실물이 어디에 사는지를 여기서 정한다. organization 계층(D16)과 함께 들어온다.
Loki에 안 둔다. 관측 로그는 Grafana Cloud Free의 50GB/월 상한에 걸리면 조용히 잘린다(CA §10). 상한에 잘리는 것을 감사라고 부를 수 없다. 감사 로그는 Postgres heymoa 의 테이블이고, 그래서 §1 표의 첫 줄 안에 있다. 테이블 정의는 ERD가 갖는다.
| 사건 | 언제 | 주체 |
|---|---|---|
VOICE_PRINT_CONSENTED | 목소리 등록 동의 | user |
VOICE_PRINT_DELETED | S7 · 임베딩 바이트 물리 삭제(§5) | user |
SPEAKER_CONFIRMED | S1의 화자 4 → 최유진 교정 | user |
NOTE_DELETED | S6 · outbox INSERT와 같은 트랜잭션(§4) | user |
TOKEN_ISSUED | S3의 90일 PAT 발급 | user |
TOKEN_USED | MCP 호출. 집계 행이다 | token |
POLICY_CHANGED | 조직이 기능을 끄고 켠다 | user |
OPS_ACTION | 재처리·재투영 같은 운영 개입 | user |
주체는 (user_id, token_id) 쌍이다. PAT이 두 번째 인증 주체가 되면서 "누가"가 사람 하나로 안 끝난다(SA §1). 쿠키 경로는 token_id 가 비고, S3의 MCP 호출은 둘 다 찬다.
| 규칙 | 왜 |
|---|---|
| append-only | UPDATE·DELETE를 role로 막는다. 앱 코드가 지키는 게 아니라 권한이 지킨다 |
notes FK를 안 건다 | deletion_outbox 와 같은 판단(§4) — 지운 노트의 삭제 기록이 그 삭제에 딸려 가면 안 된다 |
TOKEN_USED 만 집계 행 | 기계 속도에 행이 폭발한다. 원시 호출 로그는 Loki로 가고, 결제·감사 근거는 버킷 집계다 |
| 화면은 조직 하나 | /o/{orgId}/audit(IA §3). 워크스페이스 화면에 안 붙인다 — 감사를 보는 사람과 회의를 보는 사람이 다르다 |
보존 기간은 정하지 않았다 — §13.
8. AI 산출과 사람 발화는 다른 테이블에 둔다
S1의 10:23:14에 C3 카드가 뜬다. 카드도 시각을 갖고 타임라인에 섞여 보이니 transcript_segments 에 kind 칼럼 하나 붙이면 정렬·페이징이 공짜다. 그렇게 하면 안 된다. 소유권 규칙은 하나뿐이다 — transcript_segments 에는 사람 목소리에서 나온 것만 들어간다. 카드는 agent_cards 별도 테이블, 카드에서 나온 항목은 origin = AI, 요약 payload 에도 카드는 세그먼트와 섞지 않고 별도 필드로 AI 출력 표시를 달아 넘긴다(실시간 §6). 칼럼과 "왜 한 테이블이 아닌가"의 논거 셋은 ERD §5가 갖는다.
여기 남기는 것은 그림 하나다. 분리를 안 했을 때 데이터가 어떻게 도는가.
두 홉이면 추측이 사실이 된다. 아무도 안 본 카드가 요약의 입력이 되고, 요약이 만든 결정이 그래프에 들어가고, 다음 회의의 충돌 감지가 그것을 근거로 든다. 그리고 근거를 눌러 들어가면 AI가 한 말이 나온다 — evidence → transcript_segments 사슬이 사람 발화로 착지하지 않으면 온톨로지 §4의 "근거 없는 노드는 만들지 않는다"가 무력화된다. 루프라서 한 번 섞이면 다음 회의가 오염을 키운다.
버린 대안 — 같은 테이블 + source 칼럼으로 필터. 필터를 빠뜨린 쿼리 하나가 오염을 만들고, 그 오염은 그래프까지 가서야 보인다. 읽는 쪽이 실수할 수 있는 구조를 두지 않는다.
9. cross-database 금지가 다섯으로 늘면
쌍이 열이다. 쌍마다 규칙을 만들면 무너진다. 규칙 수를 늘리지 않고 접는다.
| 규칙 | |
|---|---|
| 어느 저장소도 남의 저장소를 조인하지 않는다 | Postgres는 database를 넘는 FK를 못 만들고, Neo4j·S3는 애초에 조인 대상이 아니다 |
| 저장소를 넘는 참조는 전부 맨 문자열 ID | 13자 TSID. DB가 무결성을 안 지킨다 — 지키는 것은 outbox(§4)와 스윕이다 |
| 조인은 애플리케이션에서 한다 | 벡터가 noteId 10건을 주면 server가 Postgres에서 그 10건을 읽는다. 반대 방향은 안 된다 — 벡터는 권한 조건을 모른다 |
| 쓰기는 주인만 | §1의 주인 열. ai는 Neo4j 읽기 전용 계정, server는 벡터에 쓰지 않는다 |
| mcp는 저장소에 직접 붙지 않는다 | server API만 본다. public edge가 데이터에 직접 닿으면 상한·감사·권한이 두 벌이 된다 |
진짜 문제는 조인이 아니라 스코프다. 워크스페이스 권한은 Postgres 에만 있다. 벡터와 Neo4j는 자기 안에 workspaceId 를 복제해서 들고 순회를 자른다. 복제한 스코프가 원본과 어긋나면 그건 성능 문제가 아니라 권한 사고다.
| 상황 | 어떻게 |
|---|---|
| 노트가 다른 프로젝트로 이동 | 워크스페이스는 안 바뀐다. 해당 노트만 재투영 |
| 노트가 다른 워크스페이스로 이동 | 기능을 만들지 않는다. 만드는 순간 파생 전부의 스코프를 다시 써야 하고, 그 사이 창에서 잘못된 조직이 조회한다 |
| 워크스페이스 삭제 | 노트 삭제와 같은 outbox 경로를 노트 수만큼 |
10. 재생성 리허설
안 돌려본 복구 절차는 없는 절차다. 그리고 리허설의 목적은 복구가 아니라 수치를 얻는 것이다 — README §10의 미확인 7번(Neo4j 재생성 소요)을 채우는 게 이 절이다.
| 리허설 | 주기 | 무엇을 재나 | 합격 |
|---|---|---|---|
| Neo4j 전량 재투영 | 분기 1회 · staging | 소요 시간 · 노드/관계 수가 Postgres 집계와 일치하는가 · 확인된 SUPERSEDES 가 살아 돌아오는가 | 수치 일치 |
| 벡터 전량 재색인 | 반기 1회 · 표본 1% | 소요 · 임베딩 API 원가 · HNSW 빌드 시간 | 원가 추정치 갱신 |
| 초기 백필 예행 | 1회 · staging | 기능이 켜지는 날 한 번만 도는 것 — 워크스페이스 단위로 오래된 것부터 투영할 때 전량 소요 · 도입 전 노트(오디오가 없어 화자 분리가 영영 불가)가 무엇을 만드는가 | 소요가 예상 창 안 · 반쯤 투영된 워크스페이스가 안 생긴다 |
| outbox 스윕 | 주 1회 · prod | 고아 수 | 0 |
벡터 전량은 임베딩 모델을 바꿀 때만 실제로 돈다. 55,000건 재색인은 API 원가가 들어서 리허설로 쓸 수 없다 — 표본으로 단가를 재고 곱한다. 그리고 도는 동안 검색이 살아 있어야 한다. 새 인덱스를 옆에 만들고 스위치한다. 현재 판의 노트 단위 재적재(note_id DELETE 후 INSERT + advisory lock)는 한 건짜리라 괜찮지만 전량에는 못 쓴다.
초기 백필 예행은 딱 한 번 쓰고 버리는 리허설이다. 그래도 하는 이유는 그날이 전량 투영을 처음이자 마지막으로 prod에서 도는 날이고, 실패하면 되돌릴 대상이 사용자 화면이기 때문이다.
11. 국외 이전 — 무엇이 나가나
| 어디로 | 나가는 것 | 안 나가는 것 |
|---|---|---|
| Soniox | 오디오 그 자체 (server 릴레이). 전사 원문을 돌려받는다 | 사용자 신원 · 노트 제목 · 워크스페이스 |
| OpenAI · 요약 | 전사 원문 전량 + 화자 라벨(실명) | 오디오 · voice print |
| OpenAI · 임베딩 | 전사 청크 원문 | 〃 |
| OpenAI · 실시간 게이트 | 30~60초 창 + 기존 결정 문구(실시간 §5) | 〃 |
| pyannote | 아무것도 안 나간다 — 자체 GPU · 서울 | |
| Linear · GitHub | 사용자가 승인한 항목의 제목·본문 |
세 줄로 접으면: 오디오는 Soniox로, 텍스트는 OpenAI로, 목소리의 지문은 아무 데도 안 간다.
마지막 줄이 §3의 대가다. GPU를 직접 굴리는 비용(CA §6)이 인프라 최대 항목인데, 관리형 화자 분리 API로 바꾸면 매 회의 모든 참석자의 음성이 국외로 나가고 voice print 규칙 전체가 무의미해진다. 값을 치르고 사는 것이 그것이다. 다만 요약에는 실명이 나간다 — 김민수 를 화자 A 로 바꿔 보내고 결과에서 되돌릴 수 있지만 누가 누구에게 무엇을 부탁했는지가 흐려진다. 재본 적이 없다(§13).
12. 설계 전제
- 원본은 셋이다. 넷째를 만들자는 제안은 백업·복구 책임을 하나 더 만들자는 제안이다.
- 재생성 명령이 있을 때만 파생이다. 없으면 "파생"은 규칙이 아니라 주장이다. 그 명령에 LLM이 필요하면 그것도 파생이 아니다 — 결정적이지 않은 복구는 복구가 아니다.
- 저장소를 넘는 삭제는 outbox로 한다. 2PC를 쓰지 않고, 최종 일관성을 인정하고, 그 사이를 조회 필터로 가린다.
- 고아는 생긴다고 전제한다. 안 생기게 하는 대신 보여주지 않고 주기적으로 센다 — 스윕은 청소가 아니라 계측이다.
- 생체정보는 별도 스키마·별도 키·단일 조회 경로. 나중에 분리할 수 없다.
- AI가 만든 것과 사람이 말한 것은 다른 테이블에 둔다. 필터로 가르지 않는다.
- 저장소가 늘어도 규칙 수는 늘지 않는다. 쌍마다 예외를 만들면 열 쌍에서 무너진다.
- 삭제 기록은 삭제되지 않는다. 감사 로그만 사슬 밖이다(§7).
- 휘발하는 것은 원본이 아니다. 브로커·큐가 비어도 되살릴 것이 없어야 한다.
13. 미확인
| 무엇 | 지금 아는 것 | 확인해야 할 것 |
|---|---|---|
| Neo4j 전량 재투영 소요 | 없다 | §10 리허설. 이 수치가 "파생" 규칙의 실현 가능성을 정한다 |
| 벡터 전량 재색인 원가 | 없다 | 표본 1% 로 단가를 재고 55,000건에 곱한다 |
| Soniox·OpenAI의 리전·재하도급·보존 기간 | 계약 전이다 | DPA. §11 표의 "어디로"가 실제로 어디인지 · 실명 익명화가 요약 품질을 얼마나 깎는가 |
| 감사 로그 보존 기간 | 사건 8종과 저장 위치는 정했다(§7) | organization 계층(D16)과 함께. 법정 기간을 모른다 |
analysis_results·checkpoint 만료 기간 | 90일·30일은 추정 | 실제 재분석 요청이 며칠 뒤에 오는지 |
| 계정 삭제 흐름 | 없다. FK가 막아서 지금은 실패한다 | voice print 삭제(§5)와 다른 문제다. 어디까지 지우고 무엇을 익명화하나 |
| 조직 단위 데이터 내보내기 | 없다. 화면도 없다 | 포맷 · 다섯 저장소 중 무엇까지 포함하나 · 감사 이벤트를 넣나 |