Skip to content

[FEAT] 완결 / 휴재 알림 기능 구현 #567

Description

@ljy1348

Description

작업 배경

사용자가 작품 상세에서 작품별로 완결 알림휴재 복귀 알림을 각각 등록하고, 설정 화면에서 등록한 작품과 등록일을 조회·삭제할 수 있도록 합니다.

  • 완결 알림: 작품 상태가 연재작에서 완결작으로 변경될 때 사용
  • 휴재 복귀 알림: 휴재 작품에 새로운 회차가 등록될 때 사용
  • 두 알림은 서로 독립적으로 등록·해제할 수 있습니다.
  • 토글을 켠 시점을 서버에 저장하여 등록 이전의 상태 변화가 알림 대상으로 해석되지 않게 합니다.
  • 실제 작품 상태·회차 변화 감지와 메시지 생성·발송은 어드민 작품 데이터 수집 시스템에서 담당합니다.

현재 상태

  • 작품별·알림 유형별 구독 정보를 저장하는 도메인과 테이블이 없습니다.
  • 전역 푸시 설정인 User.isPushEnabled만 존재하며, 작품별 완결·휴재 복귀 알림 설정은 표현할 수 없습니다.
  • UserNovel은 관심·감상 상태를 관리하고 조건에 따라 삭제되므로 독립적인 알림 구독 저장소로 사용하기 어렵습니다.
  • 기존 Notification은 공지 또는 피드 이동용 feedId만 제공하며 작품 상세로 이동할 novelId가 없습니다.
  • 기존 알림 목록·읽음 처리는 재사용할 수 있지만 작품 알림을 식별하고 이동시키는 응답 계약은 없습니다.

목표 상태

  • 사용자가 한 작품에 완결 알림과 휴재 복귀 알림을 독립적으로 등록·해제할 수 있습니다.
  • 작품 상세 진입 시 두 알림의 현재 설정 상태를 한 번에 조회할 수 있습니다.
  • 설정 화면에서 알림 유형별 등록 작품을 최신 등록순으로 조회할 수 있습니다.
  • 목록에는 작품 ID, 이미지, 제목, 작가, 알림 등록일을 제공합니다.
  • 설정 화면에서 같은 알림 유형의 작품을 한 개 또는 여러 개 선택해 일괄 삭제할 수 있습니다.
  • 어드민 작품 데이터 수집 시스템이 알림 대상자를 중복 없이 식별할 수 있는 저장 구조와 인덱스를 제공합니다.
  • 앱 내 알림 목록이 완결·휴재 복귀 알림의 novelId를 반환하여 작품 상세로 이동할 수 있습니다.
  • 기존 공지·피드 알림과 전역 푸시 설정 API의 계약은 유지합니다.

확정 정책

  • 알림 유형은 COMPLETION, HIATUS_RETURN 두 가지로 관리합니다.
  • 하나의 작품에 연결된 플랫폼별 작품 중 하나라도 완결로 확인되면 해당 작품을 완결로 판정합니다.
  • 연결된 플랫폼 링크 중 하나라도 새 회차가 추가되면 휴재 복귀로 판정하며, 플랫폼에서 공지사항을 회차로 제공하는 경우에도 새 회차로 포함합니다.
  • 구독은 사용자·작품·알림 유형 조합당 하나만 존재하도록 유니크 제약조건을 둡니다.
  • 구독 데이터는 UserNovel과 분리합니다. 관심 등록이나 감상 상태 변경·삭제가 작품 알림 구독을 암묵적으로 변경하지 않습니다.
  • 같은 알림을 여러 번 켜는 요청은 멱등하게 처리하며 최초 등록일을 유지합니다.
  • 알림을 끈 뒤 다시 켜면 새 구독으로 등록하고 새 등록일을 저장합니다.
  • 이미 꺼진 알림의 해제·삭제 요청은 멱등하게 성공 처리합니다.
  • 사용자는 본인의 구독만 조회·변경·삭제할 수 있습니다.
  • 목록은 알림 유형별로 조회하며 등록일과 식별자를 이용한 커서 페이지네이션을 적용합니다.
  • 전역 푸시를 꺼도 작품별 구독은 유지합니다. 실제 발송 여부는 어드민 수집 시스템이 전역 푸시 설정과 디바이스 상태를 함께 확인합니다.
  • 휴재 복귀 알림은 휴재 후 연재가 재개되는 시점에 한 번만 발송합니다.
  • 구독에 발송 여부(is_sent)를 두고 등록 시 false로 시작합니다. 이 서버는 값을 변경하지 않습니다.
  • 어드민 작품 데이터 수집 시스템은 알림 발송에 성공하면 해당 구독의 is_senttrue로 갱신합니다.
  • 발송이 끝난 구독은 설정 목록과 작품 상세 조회 양쪽에서 미등록으로 취급합니다. 구독 자체는 남으므로 발송 이력을 추적할 수 있습니다.
  • 발송이 끝난 알림을 다시 켜면 기존 구독을 지우고 새로 등록해 등록일을 갱신합니다. 해제 후 재등록과 같은 규칙입니다.
  • 발송에 실패한 구독은 is_sentfalse로 남아 재시도 대상이 됩니다.
  • 발송이 완료된 사용자가 다음 알림을 받으려면 해당 작품의 알림을 다시 등록해야 합니다.

API 계약

  • 작품별 설정 조회
    • GET /novels/{novelId}/notification
    • 완결 알림·휴재 복귀 알림 활성화 여부 반환
  • 작품별 설정 갱신
    • PUT /novels/{novelId}/notification
    • 두 알림의 목표 상태를 한 요청으로 반영
  • 알림 등록 작품 목록 조회
    • GET /users/me/notification/novels?notificationType={type}&lastSubscriptionId={id}&size={size}
    • 발송이 끝난 구독은 목록에서 제외
    • 작품 ID·이미지·제목·작가·등록일과 다음 페이지 여부 반환
  • 알림 등록 작품 일괄 삭제
    • DELETE /users/me/notification/novels
    • 알림 유형과 작품 ID 목록을 받아 본인 구독만 삭제
  • 기존 앱 내 알림 목록
    • GET /notifications 응답에 nullable novelId 추가
    • 기존 feedId와 공지 응답의 호환성 유지

작업 범위

  • 작품 알림 유형 enum과 구독 엔티티·Repository 설계
  • 사용자·작품·알림 유형 유니크 제약조건 및 조회 인덱스 정의
  • 어드민이 갱신할 발송 여부 컬럼과 미발송 구독만 조회하는 조건 추가
  • 작품별 완결·휴재 복귀 알림 설정 조회·갱신 API 구현
  • 알림 유형별 등록 작품 커서 목록 조회 구현
  • 소유권을 검증하는 일괄 삭제 구현
  • 등록일 응답 형식 YYYY.MM.DD 제공
  • 작품 삭제·회원 탈퇴 시 구독 데이터 정리
  • Notification의 작품 이동 정보와 작품 알림 유형 지원
  • 기존 알림 목록 응답에 novelId 추가
  • 신규 테이블·제약조건·인덱스·알림 유형 데이터 반영용 DDL 정리
  • Controller·Application·Service·Repository 테스트 작성

제외 범위

  • 작품 상태가 연재에서 완결로 바뀌었는지 감지하는 로직
  • 휴재 작품의 신규 회차 등록을 감지하는 로직
  • 완결·휴재 복귀 알림 레코드 및 메시지 문구 생성
  • FCM 메시지 조립·발송, 재시도, 실패 복구
  • 어드민 작품 데이터 수집 스케줄러·배치 수정
  • 발송 성공 후 구독의 is_sent를 갱신하는 어드민 처리
  • 앱의 바텀시트, 설정 화면, 알림 관리 UI 구현
  • 전역 푸시 설정 API 변경
  • 관심·감상 상태와 작품 알림의 자동 연동
  • 기존 공지·피드 알림 발송 흐름 변경

완료 조건

  • 한 사용자가 같은 작품에 두 알림 유형을 각각 독립적으로 등록·해제할 수 있습니다.
  • 중복 등록 시 구독이 추가 생성되지 않고 기존 등록일이 유지됩니다.
  • 해제 후 재등록 시 새 등록일이 저장됩니다.
  • 작품 상세용 조회에서 두 토글의 현재 상태가 정확히 반환됩니다.
  • 설정 목록이 유형별·최신 등록순으로 중복과 누락 없이 페이지네이션됩니다.
  • 목록에서 작품 표시 정보와 YYYY.MM.DD 형식의 등록일을 확인할 수 있습니다.
  • 일괄 삭제가 본인 구독에만 적용되고 존재하지 않는 대상에도 멱등하게 동작합니다.
  • 회원 탈퇴와 작품 삭제 시 고아 구독 데이터가 남지 않습니다.
  • 발송이 완료(is_sent = true)된 구독은 설정 목록과 작품 상세의 토글 상태 양쪽에서 제외됩니다.
  • 발송이 완료된 알림을 다시 켜면 새 구독으로 등록되어 등록일이 갱신됩니다.
  • 발송에 실패한 구독은 is_sentfalse로 남아 재시도 대상으로 유지됩니다.
  • 앱 내 완결·휴재 복귀 알림 응답에 작품 상세 이동용 novelId가 포함됩니다.
  • 기존 공지·피드 알림 응답과 읽음 처리 동작이 유지됩니다.
  • 메시지 생성·푸시 발송 구현 없이 관련 로컬 테스트가 통과합니다.

To-Do

  • 작품 알림 구독 도메인·테이블·유니크 제약조건 설계
  • 작품별 알림 설정 조회·갱신 API 구현
  • 유형별 등록 작품 커서 목록 조회 구현
  • 일괄 삭제 및 소유권 검증 구현
  • 작품·회원 삭제 시 구독 정리 (DDL의 ON DELETE CASCADE)
  • 발송 여부 컬럼 추가와 미발송 구독만 조회하는 조건
  • 작품 알림 유형과 Notification.novelId 지원
  • 기존 알림 목록 응답의 작품 이동 정보 추가
  • 어드민 수집 시스템용 조회 인덱스·DDL 정리
  • 멱등성·페이지네이션·삭제·기존 알림 회귀 테스트 작성
  • 전체 테스트 및 기존 API 호환성 확인
  • notification_type에 완결·휴재 복귀 유형 데이터 반영 (어드민 발송에 필요, 이 PR에 미포함)
  • dev DB에 DDL 적용 후 is_sent 동작 확인

진행 상황

PR #595 에서 위 To-Do를 구현했습니다. 구현하며 이슈 초안과 달라진 결정은 다음과 같습니다.

  • API 경로 정리: 초안의 /novels/{novelId}/notification-settings, /novel-notification-settings
    각각 /novels/{novelId}/notification, /users/me/notification/novels로 바꿨습니다.
    같은 기능인데 한쪽만 최상위 하이픈 경로였고, 구독 목록은 "내" 리소스인데 소유자가 경로에 드러나지 않아서입니다.
    기존 /users/me/devices 컨벤션과 맞췄습니다.
  • 발송 후 처리 방식 변경: 초안은 발송 성공 시 구독을 삭제하는 방식이었으나,
    발송 여부(is_sent) 컬럼을 두고 어드민이 갱신하는 방식으로 바꿨습니다.
    구독이 남아 발송 이력 추적과 재시도 판단이 가능합니다.
    발송이 끝난 구독은 설정 목록과 작품 상세 양쪽에서 미등록으로 보이며,
    다시 켜면 지우고 새로 등록해 등록일이 갱신됩니다.
  • 단건 삭제 API 미구현: 설정 화면이 일괄 삭제만 사용하므로 DELETE는 목록 기반 하나로 통합했습니다.
    작품 상세의 토글 해제는 PUT이 담당합니다.
  • 부수 수정: @Validated 파라미터 검증 실패가 401 AUTH-001로 둔갑하던 문제를 함께 고쳤습니다.
    이 API의 NOVEL_ID_POSITIVE, SIZE_MIN, SIZE_MAX, LAST_SUBSCRIPTION_ID_POSITIVE_OR_ZERO
    메시지가 클라이언트에 전달되지 않던 원인입니다.

남은 확인 사항은 To-Do 하단 두 항목이며, 특히 notification_type 데이터는
어드민이 작품 알림을 만들 때 필요하므로 별도 반영이 필요합니다.

Reference

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions