왜
알림 히스토리창에 4개 기능이 필요하다 - 모두읽음 · 단건삭제 · 모두삭제 · N일 자동삭제. 이 중 모두읽음은 이미 있고(POST /read {all:true} → markAllRead), 나머지 삭제 계열 3개가 미구현이다.
무엇을 (브레인스토밍 확정 설계)
[결정] 하드삭제 / N일 자동삭제는 읽음 무관 전부 삭제 / read API 미러링한 단일 삭제 엔드포인트.
① 모두 읽음 - 작업 없음
기존 POST /read {all:true} → markAllRead 그대로 사용.
② 단건·모두 삭제 (하드삭제, 단일 엔드포인트)
- 엔드포인트:
DELETE /api/v1/notifications + body {all XOR ids} (read 계약 미러링). 단건=ids[1개], 다건=ids[N], 모두=all:true.
- 요청 DTO
NotificationDeleteRequest(all, ids): read 의 all XOR ids 검증(@AssertTrue)·toCommand() 패턴 그대로 복제.
- 컨트롤러:
NotificationHistoryController 에 @DeleteMapping 추가 (같은 /api/v1/notifications).
- 서비스: 본인(userId) 소유만 삭제.
all→전체, ids→지정(타인·없는 id 무영향, 멱등).
- Repository:
deleteByUserIdAndIds(userId, ids): Int 신규 / 모두는 기존 hardDeleteAllByUserId 재사용 가능(동일 쿼리).
- 뱃지 동기화: 삭제된 것 중 안읽음이 있으면 unread 뱃지 감소 - read flow(
readAndSyncBadge)와 대칭으로 삭제 후 뱃지 재계산 + SSE silent sync.
- 응답: read 미러링이라 카테고리별 unread 뱃지 반환.
③ N일 자동삭제 (하드삭제, 읽음 무관)
- 스케줄러
NotificationCleanupScheduler(@Scheduled, 1일 1회) - AnnouncementScheduler 패턴 참고.
- Repository
deleteByCreatedAtBefore(cutoff): Int - created_at < now-N일 전부 삭제.
- N 설정:
application.yml (notification.retention-days), 하드코딩 금지. 기본값은 구현 시 협의.
- 동시성: age delete 는 멱등이라 blue-green 중복 실행 무해.
마이그레이션
- 하드삭제라
deleted_at 컬럼 불필요.
- 단, 자동삭제 age 쿼리가 풀스캔 안 하게
created_at 인덱스 확인 / 필요시 idx_notifications_created_at 마이그레이션 추가.
반드시 지킬 CLAUDE.md 규약 (구현자 체크리스트)
1. 도메인 예외 정책 (require/check/error vs 커스텀 예외)
- 판단 기준 "멀쩡한 클라이언트가 정상 요청으로 여기 닿을 수 있나?" - 닿으면 커스텀 예외(계약), 못 닿으면
require/check/error(불변식).
- 계약 위반(
all·ids 동시 / 둘 다 없음 등)은 Bean Validation(400) 또는 NotificationException 커스텀. 없는 id·타인 id 는 예외 대신 무영향(멱등) 으로 처리 - 설계상 삭제는 본인 소유만 걸러 0건 반환.
- 커스텀 예외는
NotificationException(도메인 명사)Exception, private 생성자 + companion 정적 팩토리, message·ErrorCategory·HttpStatus 를 한 자리에.
2. 에러 code (#728 code 기반 전환)
3. API 응답 전수 문서화 (절대 규칙)
NotificationHistoryApi(DELETE) 에 멀쩡한 클라가 받을 수 있는 모든 응답을 @ApiResponses 로 전수 선언 - 성공 200(+뱃지) · 400(all XOR ids 위반) · 401(미인증).
*ApiExamples 에 각 응답 example payload 를 ApiResponseBody.ok/fail 로 등록, fail detail 은 single source(Bean Validation 은 DTO const, 도메인 예외는 OperationExamples.add(exception)).
4. 기타
- DTO 자체 매핑(
toCommand, 별도 Mapper 금지) · @Transactional 서비스 경계(삭제는 쓰기) · FK 제약 금지 · 테스트 컨벤션(DTO 분기 단위 / DELETE 계약 통합 / 자동삭제 cutoff 경계, 내부 모킹·셋업 hook 금지, IntegrationTestSupport 공유).
왜
알림 히스토리창에 4개 기능이 필요하다 - 모두읽음 · 단건삭제 · 모두삭제 · N일 자동삭제. 이 중 모두읽음은 이미 있고(
POST /read {all:true}→markAllRead), 나머지 삭제 계열 3개가 미구현이다.무엇을 (브레인스토밍 확정 설계)
[결정] 하드삭제 / N일 자동삭제는 읽음 무관 전부 삭제 / read API 미러링한 단일 삭제 엔드포인트.
① 모두 읽음 - 작업 없음
기존
POST /read {all:true}→markAllRead그대로 사용.② 단건·모두 삭제 (하드삭제, 단일 엔드포인트)
DELETE /api/v1/notifications+ body{all XOR ids}(read 계약 미러링). 단건=ids[1개], 다건=ids[N], 모두=all:true.NotificationDeleteRequest(all, ids): read 의all XOR ids검증(@AssertTrue)·toCommand()패턴 그대로 복제.NotificationHistoryController에@DeleteMapping추가 (같은/api/v1/notifications).all→전체,ids→지정(타인·없는 id 무영향, 멱등).deleteByUserIdAndIds(userId, ids): Int신규 / 모두는 기존hardDeleteAllByUserId재사용 가능(동일 쿼리).readAndSyncBadge)와 대칭으로 삭제 후 뱃지 재계산 + SSE silent sync.③ N일 자동삭제 (하드삭제, 읽음 무관)
NotificationCleanupScheduler(@Scheduled, 1일 1회) -AnnouncementScheduler패턴 참고.deleteByCreatedAtBefore(cutoff): Int-created_at < now-N일전부 삭제.application.yml(notification.retention-days), 하드코딩 금지. 기본값은 구현 시 협의.마이그레이션
deleted_at컬럼 불필요.created_at인덱스 확인 / 필요시idx_notifications_created_at마이그레이션 추가.반드시 지킬 CLAUDE.md 규약 (구현자 체크리스트)
1. 도메인 예외 정책 (require/check/error vs 커스텀 예외)
require/check/error(불변식).all·ids동시 / 둘 다 없음 등)은 Bean Validation(400) 또는NotificationException커스텀. 없는 id·타인 id 는 예외 대신 무영향(멱등) 으로 처리 - 설계상 삭제는 본인 소유만 걸러 0건 반환.NotificationException(도메인 명사)Exception,private생성자 +companion정적 팩토리, message·ErrorCategory·HttpStatus를 한 자리에.2. 에러 code (#728 code 기반 전환)
NotificationErrorCode(도메인 code)로 배정하고fail(errorCode)로 내린다.3. API 응답 전수 문서화 (절대 규칙)
NotificationHistoryApi(DELETE) 에 멀쩡한 클라가 받을 수 있는 모든 응답을@ApiResponses로 전수 선언 - 성공 200(+뱃지) · 400(all XOR ids 위반) · 401(미인증).*ApiExamples에 각 응답 example payload 를ApiResponseBody.ok/fail로 등록, fail detail 은 single source(Bean Validation 은 DTOconst, 도메인 예외는OperationExamples.add(exception)).4. 기타
toCommand, 별도 Mapper 금지) ·@Transactional서비스 경계(삭제는 쓰기) · FK 제약 금지 · 테스트 컨벤션(DTO 분기 단위 / DELETE 계약 통합 / 자동삭제 cutoff 경계, 내부 모킹·셋업 hook 금지,IntegrationTestSupport공유).