Skip to content

docs: 탈퇴 처리에 AI 파생 데이터 무효화 추가 (§3.6) - #14

Merged
cherry-go-round merged 2 commits into
mainfrom
docs/withdraw-ai-derived-data
Jul 29, 2026
Merged

docs: 탈퇴 처리에 AI 파생 데이터 무효화 추가 (§3.6)#14
cherry-go-round merged 2 commits into
mainfrom
docs/withdraw-ai-derived-data

Conversation

@cherry-go-round

Copy link
Copy Markdown
Contributor

배경

AI 담당자 지적으로 08 §3.6 회원 탈퇴의 처리 대상 표에 AI 파생 데이터 처리가 빠져 있는 것을 확인했습니다. record·context 소프트 삭제만 적혀 있어, 그대로 구현하면 임베딩과 AI 작업 상태가 방치됩니다.

변경

§3.6 처리 표에 두 행을 추가하고, 상세 소절을 신설했습니다.

대상 처리
ai.context_ai_state 두 status를 CANCELLED로 전이
ai.context_embedding is_deleted = true 표시

물리 삭제가 아니라 무효화 표시라는 점을 명시했습니다.

  • CANCELLED — 진행 중인 AI 작업을 취소해 늦게 도착한 결과가 저장되는 것을 차단
  • is_deleted — 검색 제외 + 물리 삭제 대상 식별
  • 물리 삭제 시점은 개인정보 정책 소관이라 이 명세 범위 밖

덧붙인 것:

  • 두 컬럼 모두 Spring이 변경합니다. FastAPI는 건드리지 않습니다 (05 §12.3)
  • ai.context_keyword는 별도 처리 불필요 — keyword_statusCANCELLED가 되면 조회에서 자동 제외됩니다 (05 §9)
  • Record 삭제(5.6·5.7)에도 같은 처리가 적용됩니다. 탈퇴 전용이 아닙니다

근거는 07_ERD §5 삭제 방식 요약입니다.

이 PR 범위 밖 — 06_데이터모델에 남은 문제

AI 담당자가 이미 정정 요청했다고 하셨는데, "즉시 파기"·"HNSW" 문구 외에 하나가 더 있습니다.

06 §1.3 쓰기 매트릭스가 백엔드 권한을 이렇게 규정합니다.

테이블 백엔드 AI 워커
ai.context_embedding 삭제 시 DELETE만 INSERT / UPDATE
ai.context_keyword 삭제 시 DELETE만 INSERT / DELETE

그런데 실제로 백엔드가 해야 하는 것은 UPDATE(is_deleted = true, status → CANCELLED)입니다. 그리고 ai.context_ai_state가 매트릭스에 아예 없습니다.

문구 수정만으로는 안 되고 권한 계약 자체가 바뀌어야 합니다.

  • ai.context_embedding — 백엔드 UPDATE 필요 (is_deleted)
  • ai.context_ai_state — 매트릭스에 행 추가 + 백엔드 UPDATE 필요 (두 status)
  • ai.context_keyword — 백엔드 쓰기 불필요. 읽기 조인만

06은 제 소유가 아니라 손대지 않았습니다. 정정하실 때 이 부분도 함께 봐주세요.

🤖 Generated with Claude Code

AI 파트 지적 반영. §3.6 처리 대상 표에 record·context 소프트 삭제는
있었으나 AI 파생 데이터 처리가 빠져 있었다.

- ai.context_ai_state — 두 status를 CANCELLED로 전이
- ai.context_embedding — is_deleted = true 표시

물리 삭제(파기)가 아니라 무효화 표시다. CANCELLED는 진행 중인 AI 작업을
취소해 늦게 도착한 결과의 저장을 막고, is_deleted는 검색 제외와 물리 삭제
대상 식별을 담당한다. 물리 삭제 시점은 개인정보 정책 소관이라 명세 밖이다.

두 컬럼 모두 Spring이 변경한다(05 §12.3). ai.context_keyword는 keyword_status
CANCELLED로 조회에서 자동 제외되므로 별도 처리가 필요 없다(05 §9).

근거는 07_ERD §5이며, Record 삭제(5.6·5.7)에도 같은 처리가 적용된다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
AI 파트 회신 반영.

- CANCELLED 전이에 가드를 걸지 않는다. COMPLETED·FAILED도 덮는다(05 §11.1).
  context_keyword에는 is_deleted에 해당하는 컬럼이 없어 키워드 조회 제외를
  오직 keyword_status = CANCELLED가 담당한다. COMPLETED를 남기면 임베딩
  검색에서는 걸러지지만 키워드 조회에서 탈퇴 사용자의 키워드가 노출된다
- is_deleted UPDATE의 영향 행이 0이어도 오류로 처리하지 않는다.
  Embedding 생성 전에 삭제된 경우가 정상 경로다
- 05 §7의 embedding_status IN ('PENDING','PROCESSING') 가드는 FastAPI의
  작업 선점용이라 Spring의 CANCELLED 전이와 무관하다. 혼동을 막기 위해
  실행할 SQL을 그대로 명시했다
- Finalizer가 CANCELLED를 FAILED로 덮을 수 없다는 반대 방향 보호도 기재
- context_keyword 자동 제외 근거를 §9에서 §9·§11.2로 보강

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@cherry-go-round
cherry-go-round requested a review from colosair July 27, 2026 07:51
@cherry-go-round

Copy link
Copy Markdown
Contributor Author

@colosair 답변 감사합니다. 두 건 다 반영했습니다.

CANCELLED 전이 — 제가 놓친 부분이었습니다

가드 없이 무조건 전이한다는 것, 그리고 COMPLETED도 덮어야 하는 이유를 본문에 넣었습니다. context_keywordis_deleted가 없어서 키워드 조회 제외를 keyword_status가 단독으로 담당한다는 설명이 핵심이더군요. 이 근거가 없으면 다음 사람이 "완료된 작업까지 취소하는 건 이상한데" 하고 가드를 넣기 딱 좋습니다.

주신 SQL을 주석까지 그대로 명세에 넣었습니다. §7embedding_status IN ('PENDING','PROCESSING')을 보고 제가 혼동했던 것처럼, 실행할 문장을 못 박아두는 편이 안전하다고 판단했습니다.

is_deleted 0행 허용도 명시했습니다. 이건 특히 놓치기 쉬운데, 보통 UPDATE 결과가 0이면 "대상이 없다"고 예외를 던지게 짜기 때문입니다. Embedding 생성 전 삭제가 정상 경로라는 걸 적어뒀습니다.

Finalizer가 CANCELLEDFAILED로 덮을 수 없다는 반대 방향 보호도 함께 기재했습니다.

context_keyword — 근거 보강했습니다

§9 하나만 인용했었는데 §11.2(조회가 context_ai_state를 조인해 keyword_status = 'COMPLETED'로 필터)를 더했습니다. "백엔드는 쓰지 않고 읽기 조인만 한다"도 명시했습니다.

남은 것

06 §1.3 쓰기 매트릭스 — 민용님 전달안에 넣어주신다니 맡기겠습니다. 정리하면 세 줄이 바뀌어야 합니다.

테이블 현재 필요
ai.context_embedding 백엔드 DELETE만 백엔드 UPDATE (is_deleted)
ai.context_ai_state 행 없음 행 추가 + 백엔드 UPDATE (두 status)
ai.context_keyword 백엔드 DELETE만 백엔드 쓰기 없음, 읽기 조인만

06의 "즉시 파기"·"HNSW" 서술(§1.1, §2.x, §6.x, §7.x 네 군데)도 같은 정정 대상입니다.

back#28의 제 서술은 별도로 정정 코멘트를 달았습니다. 근거로 삼은 게 06 §1.3이었어서, 그 문서가 고쳐지면 같은 오해가 반복되지 않을 것 같습니다.

리뷰 요청 드렸습니다. 확인 부탁드립니다.

@colosair colosair left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI 파트입니다. 확인이 늦었습니다 — 승인합니다.

코드와 대조했습니다. 문서가 선언한 *"CANCELLED가 다른 모든 상태보다 우선한다"*가 실제로 강제됩니다.

# app/repository/ai_state_repo.py
async def complete(...):  WHERE context_id = $1 AND {col} = 'PROCESSING'
async def fail(...):      WHERE context_id = $1 AND {col} = 'PROCESSING'
                          """PROCESSING 가드로 CANCELLED를 덮지 않는다."""

백엔드가 CANCELLED로 전이한 뒤에는 워커의 완료·실패 쓰기가 전부 rowcount 0으로 떨어집니다. try_start()status IN ('PENDING','PROCESSING')이라 재선점하지 못합니다. fail()의 docstring에 그 의도가 명시돼 있습니다.

COMPLETED도 덮는다는 판단이 맞습니다. context_keywordis_deleted에 해당하는 컬럼이 없어 키워드 조회 제외를 keyword_status가 단독으로 담당한다는 근거도 정확합니다. COMPLETED를 남기면 임베딩 검색에서는 걸러지지만 키워드 조회에서 노출된다는 지적 그대로입니다.

is_deleted UPDATE의 영향 행이 0이어도 정상이라는 것도 맞습니다. 워커의 context_embedding UPSERT는 ON CONFLICT (context_id) DO UPDATEembedding·embedding_profile만 덮고 is_deleted는 건드리지 않으므로, 늦게 도착한 INSERT가 무효화 표시를 되돌리지 않습니다.

소유 경계도 Team-PinLog/back#61에 답한 내용과 일치합니다. 두 컬럼은 백엔드가 직접 씁니다(06 §1.3 쓰기 매트릭스). AI 파트가 추가로 구현할 것은 없습니다 — 차단 방어선이 이미 코드에 있고, 빠진 것은 플래그를 켜는 쪽뿐이었습니다.

참조 절 번호도 확인했습니다. 05 §9·§11.1·§11.2·§12·§12.3·§6.4가 현재 main에서 전부 유효합니다. 방금 병합된 docs#21§14 위주로 고쳐 절 번호가 밀리지 않았습니다.


관측 한계 하나만 기록해 둡니다. 이 PR의 문제는 아닙니다.

경합으로 rowcount가 0이 될 때 — 즉 취소가 진행 중이던 작업을 실제로 잘랐을 때 — 워커가 아무 로그도 남기지 않습니다. 동작은 안전하지만 사후에 *"취소가 무엇을 잘랐는지"*를 확인할 방법이 없습니다. AI 파트의 관측 공백이고 dev 배포 계약(Team-PinLog/ai#32)에서 인프라에 이미 고지했습니다.

@cherry-go-round
cherry-go-round merged commit b371d2c into main Jul 29, 2026
@cherry-go-round
cherry-go-round deleted the docs/withdraw-ai-derived-data branch July 29, 2026 02:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants