From 60612a4e71b4098eed734726a6a2c4fdd3867e2f Mon Sep 17 00:00:00 2001 From: Hong Seokho Date: Mon, 27 Jul 2026 16:28:53 +0900 Subject: [PATCH 1/2] =?UTF-8?q?docs:=20=ED=83=88=ED=87=B4=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=EC=97=90=20AI=20=ED=8C=8C=EC=83=9D=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EB=AC=B4=ED=9A=A8=ED=99=94=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(=C2=A73.6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- "static/08_API_\353\252\205\354\204\270.md" | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git "a/static/08_API_\353\252\205\354\204\270.md" "b/static/08_API_\353\252\205\354\204\270.md" index 27c82dd..ddf8cd5 100644 --- "a/static/08_API_\353\252\205\354\204\270.md" +++ "b/static/08_API_\353\252\205\354\204\270.md" @@ -367,9 +367,25 @@ DELETE /api/core/v1/me | `member`, `social_account` | 소프트 삭제 | | `record`, `context`, `collection`, `collection_record`, 관련 `follow` | 소프트 삭제 | | `social_account`의 `provider_user_id`, `email` | **마스킹**(개인정보 파기 대상) | +| `ai.context_ai_state` | 두 status를 `CANCELLED`로 전이 | +| `ai.context_embedding` | `is_deleted = true` 표시 | | Refresh Token | 해당 회원의 **모든** Refresh를 무효화하고 인증 쿠키와 표시 쿠키(1.8)를 만료시킨다 | | `place` | 공용 데이터이므로 유지한다 | +### AI 파생 데이터 + +Context가 소프트 삭제될 때 함께 처리한다. **물리 삭제가 아니라 무효화 표시다.** + +| 표시 | 효과 | +|---|---| +| `context_ai_state`의 두 status → `CANCELLED` | 진행 중인 AI 작업을 취소해 **늦게 도착한 결과가 저장되는 것을 막는다** | +| `context_embedding.is_deleted = true` | 검색 대상에서 제외하고 물리 삭제 대상으로 식별한다 | + +- 두 컬럼 모두 **백엔드(Spring)가 변경한다.** FastAPI는 건드리지 않는다(05 §12.3). +- `ai.context_keyword`는 별도 처리가 필요 없다. `keyword_status`가 `CANCELLED`가 되면 조회에서 자동으로 제외된다(05 §9). +- 물리 삭제 시점은 이 명세의 범위가 아니며 개인정보 정책을 따른다(07 §5). +- 같은 처리를 Record 삭제(5.6·5.7)에도 적용한다. 탈퇴 전용 동작이 아니다. + 이 경로는 Refresh 쿠키의 `Path` 범위 밖이라 Refresh 쿠키가 전송되지 않는다. 따라서 **Access 쿠키로 회원을 식별하고 그 회원의 Refresh를 전부 무효화한다.** 탈퇴는 모든 기기에서 즉시 로그아웃되어야 하므로 전체 무효화가 의도된 동작이다. Access가 만료된 상태라면 인증 실패(401)이므로, 클라이언트는 재발급(3.3) 후 다시 요청한다. - 탈퇴한 사용자의 Shelf와 Collection은 다른 사용자의 Library·Feed에서 즉시 제외한다. From 6c923fbc188942e1acd3f50245c0122fbb444417 Mon Sep 17 00:00:00 2001 From: Hong Seokho Date: Mon, 27 Jul 2026 16:51:16 +0900 Subject: [PATCH 2/2] =?UTF-8?q?docs:=20AI=20=ED=8C=8C=EC=83=9D=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EB=AC=B4=ED=9A=A8=ED=99=94=EC=97=90=20CAN?= =?UTF-8?q?CELLED=20=EC=A0=84=EC=9D=B4=20=EC=A1=B0=EA=B1=B4=EA=B3=BC=200?= =?UTF-8?q?=ED=96=89=20=ED=97=88=EC=9A=A9=20=EB=AA=85=EC=8B=9C=20(=C2=A73.?= =?UTF-8?q?6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- "static/08_API_\353\252\205\354\204\270.md" | 23 ++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git "a/static/08_API_\353\252\205\354\204\270.md" "b/static/08_API_\353\252\205\354\204\270.md" index ddf8cd5..c9f0dab 100644 --- "a/static/08_API_\353\252\205\354\204\270.md" +++ "b/static/08_API_\353\252\205\354\204\270.md" @@ -378,11 +378,28 @@ Context가 소프트 삭제될 때 함께 처리한다. **물리 삭제가 아 | 표시 | 효과 | |---|---| -| `context_ai_state`의 두 status → `CANCELLED` | 진행 중인 AI 작업을 취소해 **늦게 도착한 결과가 저장되는 것을 막는다** | +| `context_ai_state`의 두 status → `CANCELLED` | 진행 중인 AI 작업을 취소해 **늦게 도착한 결과가 저장되는 것을 막고**, 키워드 조회에서 제외한다 | | `context_embedding.is_deleted = true` | 검색 대상에서 제외하고 물리 삭제 대상으로 식별한다 | -- 두 컬럼 모두 **백엔드(Spring)가 변경한다.** FastAPI는 건드리지 않는다(05 §12.3). -- `ai.context_keyword`는 별도 처리가 필요 없다. `keyword_status`가 `CANCELLED`가 되면 조회에서 자동으로 제외된다(05 §9). +```sql +UPDATE ai.context_ai_state +SET embedding_status = 'CANCELLED', + keyword_status = 'CANCELLED', + updated_at = now() +WHERE context_id = ?; +-- 조건 없음. COMPLETED·FAILED도 덮는다. CANCELLED가 다른 모든 상태보다 우선한다(05 §11.1). + +UPDATE ai.context_embedding +SET is_deleted = true, updated_at = now() +WHERE context_id = ?; +-- 영향 행이 0이어도 정상이다(Embedding 생성 전에 삭제된 경우). +-- 늦게 도착하는 INSERT는 State의 CANCELLED가 차단한다(05 §11.2). +``` + +- **`CANCELLED` 전이에 조건을 걸지 않는다.** `COMPLETED`도 덮어야 한다. `context_keyword`에는 `is_deleted`에 해당하는 컬럼이 없어, 키워드 조회 제외를 오직 `keyword_status = 'CANCELLED'`가 담당하기 때문이다. `COMPLETED`를 남기면 임베딩 검색에서는 걸러지지만 **키워드 조회에서 탈퇴한 사용자의 키워드가 계속 노출된다.** +- `is_deleted` UPDATE의 **영향 행이 0이어도 오류로 처리하지 않는다.** +- 두 컬럼 모두 **백엔드(Spring)가 변경한다.** FastAPI는 건드리지 않는다(05 §6.4·§12.3). 반대로 Finalizer는 `CANCELLED`를 `FAILED`로 덮어쓸 수 없다(05 §12). +- `ai.context_keyword`는 별도 처리가 필요 없다. 조회가 `context_ai_state`를 조인해 `keyword_status = 'COMPLETED'`로 거르므로 자동 제외된다(05 §9·§11.2). 백엔드는 이 테이블에 쓰지 않고 읽기 조인만 한다. - 물리 삭제 시점은 이 명세의 범위가 아니며 개인정보 정책을 따른다(07 §5). - 같은 처리를 Record 삭제(5.6·5.7)에도 적용한다. 탈퇴 전용 동작이 아니다.