Repository navigation
feat: 차단/신고 API, 암호화 모델 결정, 스케줄러 정리 #30
Description
Describe
API 명세서(34개 엔드포인트)는 #28 로 모두 구현되었다. 이 이슈는 명세서에 없지만 CLAUDE.md 와 암호화 설계 문서에 정의되어 있는, MVP 마감 전 남은 세 가지를 함께 처리한다.
1. 차단 / 신고 API
Block, Report 는 엔티티와 레포지토리만 있고 API가 없다.
문제는 차단 로직이 이미 서버 전반에서 동작 중이라는 점이다. CapsuleAccessPolicy, SendFriendRequestService, RespondFriendRequestService, CreateCapsuleService, SearchFriendService 가 모두 BlockRepository 를 호출한다. 즉 차단을 전제로 동작하는데 정작 차단할 수단이 없는 상태다.
POST /api/v1/blocks차단,DELETE /api/v1/blocks/{memberId}차단 해제,GET /api/v1/blocks차단 목록POST /api/v1/reports신고 (uq_report_reporter_target으로 중복 신고 차단)- 차단 시 기존 친구 관계와 대기 중인 친구 요청을 어떻게 처리할지 결정 필요
Report.targetType과Report.reason이 rawString이다. CLAUDE.md 의 표현적 도메인 모델링 원칙에 따라 enum 으로 교체한다(마이그레이션 불필요, 컬럼은 이미 VARCHAR).- 두 API 모두 명세서에 없으므로 Notion 반영이 필요하다.
2. 암호화 모델 결정 — Shamir's Secret Sharing
이건 구현 작업 전에 결정이 필요한 항목이다.
「🔐 암호화 설계」 문서와 현재 구현이 서로 다른 모델이다.
설계 문서 (클라이언트 사이드 E2E)
- CEK 를 클라이언트에서 생성하고, 서버에 평문으로 전달하지 않는다
- CEK 를 Shamir's Secret Sharing 으로 N개 조각 분할 →
tbl_key_share에 저장 (2-of-3 기본, 3-of-5 강화) - 열람 시 서버는 위치·비밀번호 검증 후 키 조각만 반환하고, 복호화는 클라이언트가 수행한다
- 서버는 캡슐 내용을 절대 볼 수 없다
현재 구현 (#23, 서버 사이드 봉투 암호화)
- 서버가 보유한
MUDDA_MASTER_KEY를 KEK 로 사용한다 EncryptedStringConverter(JPA AttributeConverter) 가TimeCapsule.content를 투명하게 암·복호화한다OpenCapsuleService가 평문 내용을 응답에 담아 반환한다 → 서버가 평문을 볼 수 있다tbl_key_share는 존재하지 않는다
현재 구현은 저장소 유출에 대한 at-rest 암호화로는 유효하지만, CLAUDE.md 의 "서버는 캡슐 내용을 절대 평문으로 볼 수 없다" 는 만족하지 못한다.
결정할 것
- (A) 설계 문서대로 클라이언트 사이드 E2E 로 전환한다.
tbl_key_share추가, Shamir GF(256) 직접 구현, 캡슐 생성/열람 API 의 요청·응답 계약 변경, 클라이언트 작업 동반. 범위가 크므로 별도 이슈로 분리하는 편이 낫다. - (B) 현재 서버 사이드 모델을 유지하기로 확정하고, CLAUDE.md 와 암호화 설계 문서를 실제 구현에 맞게 수정한다. Shamir 는 스택에서 제외한다.
어느 쪽이든 문서와 코드 중 하나는 고쳐야 한다. 지금은 둘이 어긋난 상태다. 결정 전까지 이 항목은 구현하지 않는다.
3. 스케줄러 (배치)
@Scheduled 가 프로젝트에 하나도 없다.
- 미첨부 미디어 정리 —
tbl_media.time_capsule_id IS NULL인 채로 일정 시간이 지난 pending 업로드는 S3 객체와 행이 모두 고아로 남는다. V4 에서 pending 업로드를 허용하면서 생긴 실제 누수다. - 만료 캡슐 키 조각 삭제 — 암호화 설계 문서에 "만료된 캡슐의 키 조각은 스케줄러로 삭제 처리" 로 명시되어 있다. 위 2번 결정이 (A) 일 때만 해당한다.
- 참고: 캡슐 만료 상태 전이 배치는 불필요하다.
CapsuleAccessPolicy.requireAccessible이 읽기 시점에expiredAt을 검사해 차단하고 있다. 배치가 필요한 이유는 정합성이 아니라 저장공간 회수다.
Additional
세 항목의 성격이 서로 달라 진행 중 분리가 필요하다고 판단되면 이슈를 쪼개도 된다.
2번은 결정 전까지 착수 불가다. 1번과 3번(미디어 정리)은 독립적으로 바로 진행할 수 있다.
Activity
test: #30 :: hide the security test controller from the openapi document
cfcromn commentedon Aug 30, 2026
진행 상황 정리합니다.
findReceivedRequests/findSentRequests에 차단 필터가 누락되어 있던 기존 결함도 함께 수정했습니다.#31 머지 후에는 이 이슈에 남는 작업이 없으므로 닫고, 암호화 논의는 #32 에서 이어가면 됩니다.
fix: #30 :: close concurrency races in block, report, and media cleanup
cfcromn commentedon Aug 30, 2026
PR #31 머지로 1번(차단/신고 API)과 3번(미디어 정리 스케줄러)이 완료되었습니다. 2번(암호화 모델 결정)은 #32 로 분리되어 이어집니다.