Skip to content

Conversation

@david-parkk
Copy link
Contributor

@david-parkk david-parkk commented Dec 23, 2025

#️⃣ 연관된 이슈

closes #367

📝작업 내용

  • 알림 활성화/비활성화 API
  • 알림 비활성화 조회 API
  • 알림 생성시 비활성의 경우 알림 발송 중지

작업 상세 내용

상세 내용을 입력해주세요.

💬리뷰 요구사항

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요.

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능
    • 사용자가 개별 알람 유형을 활성화 또는 비활성화할 수 있는 기능 추가
    • 비활성화된 알람 유형은 알림을 받지 않도록 설정 가능
    • 현재 비활성화된 알람 목록 확인 기능 추가

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 23, 2025

워크스루

사용자별 알림 유형 비활성화 기능을 추가합니다. UserDisabledAlarm 엔티티와 저장소를 도입하고, 알림 비활성화/활성화 API를 노출하며, notifyAlarm에서 사용자가 비활성화한 알림 유형을 확인하여 조기 반환합니다.

변경사항

집합 / 파일 요약
알림 도메인 및 저장소
src/main/java/ku_rum/backend/domain/alarm/domain/UserDisabledAlarm.java, src/main/java/ku_rum/backend/domain/alarm/domain/repository/UserDisabledAlarmRepository.java
사용자가 비활성화한 알림을 추적하는 새로운 JPA 엔티티 및 저장소 인터페이스 추가 (findByUserAndAlarmType, findByUser 쿼리 메서드 포함)
요청/응답 DTO
src/main/java/ku_rum/backend/domain/alarm/dto/request/PatchDisableAlarmRequest.java, src/main/java/ku_rum/backend/domain/alarm/dto/response/PatchDisableAlarmResponse.java, src/main/java/ku_rum/backend/domain/alarm/dto/response/GetAlarmDisableResponse.java
알림 비활성화 요청 및 응답을 위한 새로운 DTO 레코드 추가 (factory 메서드 포함)
알림 서비스
src/main/java/ku_rum/backend/domain/alarm/application/AlarmService.java
disableAlarm (알림 유형 토글), findDisableAlarm (비활성화된 알림 조회), isDisableAlarm (헬퍼) 메서드 추가; notifyAlarm에 비활성화 체크 로직 추가
알림 컨트롤러
src/main/java/ku_rum/backend/domain/alarm/presentation/AlarmController.java
PATCH /disable (알림 비활성화 토글), GET /disable (비활성화된 알림 목록 조회) 엔드포인트 추가
테스트
src/test/java/ku_rum/backend/domain/alarm/presentation/AlarmControllerTest.java
새로운 엔드포인트에 대한 테스트 및 REST Docs 문서화 추가

시퀀스 다이어그램

sequenceDiagram
    actor Client
    participant Controller as AlarmController
    participant Service as AlarmService
    participant Repo as UserDisabledAlarmRepository
    participant DB as Database
    
    rect rgb(220, 240, 255)
    Note over Client,DB: 알림 비활성화/활성화 토글
    Client->>Controller: PATCH /disable<br/>{alarmType}
    Controller->>Service: disableAlarm(userDetails, request)
    Service->>Repo: findByUserAndAlarmType(user, alarmType)
    Repo->>DB: SELECT UserDisabledAlarm
    DB-->>Repo: Optional<UserDisabledAlarm>
    
    alt 이미 비활성화됨
        Repo->>DB: DELETE UserDisabledAlarm
        DB-->>Repo: ✓
        Service-->>Controller: PatchDisableAlarmResponse<br/>(isDisabled=false)
    else 활성화됨
        Service->>Repo: save(new UserDisabledAlarm)
        Repo->>DB: INSERT UserDisabledAlarm
        DB-->>Repo: UserDisabledAlarm
        Service-->>Controller: PatchDisableAlarmResponse<br/>(isDisabled=true)
    end
    
    Controller-->>Client: BaseResponse{data}
    end
    
    rect rgb(240, 255, 240)
    Note over Client,DB: 사용자의 비활성화된 알림 목록 조회
    Client->>Controller: GET /disable
    Controller->>Service: findDisableAlarm(userDetails)
    Service->>Repo: findByUser(user)
    Repo->>DB: SELECT UserDisabledAlarm WHERE user_id=?
    DB-->>Repo: List<UserDisabledAlarm>
    Service->>Service: convert to GetAlarmDisableResponse
    Service-->>Controller: GetAlarmDisableResponse
    Controller-->>Client: BaseResponse{disabledAlarmType}
    end
    
    rect rgb(255, 240, 240)
    Note over Service,DB: 알림 발송 시 비활성화 여부 확인
    Service->>Service: notifyAlarm(user, alarmType, ...)
    Service->>Service: isDisableAlarm(user, alarmType)
    Service->>Repo: findByUserAndAlarmType(user, alarmType)
    Repo->>DB: SELECT UserDisabledAlarm
    DB-->>Repo: Optional<UserDisabledAlarm>
    
    alt 비활성화됨
        Service->>Service: return early
    else 활성화됨
        Service->>Service: send FCM message
    end
    end
Loading

예상 코드 리뷰 난이도

🎯 3 (중간) | ⏱️ ~25분

관련 가능성 있는 PR

제안 리뷰어

  • kmw10693

🐰 알림을 끌 수 있게 되니,
조용한 밤이 찾아오고,
사용자는 웃음 지으며,
우리 코드는 춤을 추네! ✨🔔
설정의 자유, 이제 당신의 손에!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 2 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Description check ❓ Inconclusive PR 설명이 필수 섹션(연관된 이슈, 작업 내용)은 포함하고 있으나 작업 상세 내용 섹션이 실제 내용 없이 placeholder로 남아있습니다. 작업 상세 내용 섹션에 구현된 기능의 구체적인 설명(UserDisabledAlarm 엔티티, API 엔드포인트, 로직 등)을 추가해주세요.
Linked Issues check ❓ Inconclusive PR이 이슈 #367의 알림 설정 API 요구사항을 부분적으로만 구현했습니다. 4가지 알림 타입(친구 위치 공유, 공지 업로드, 키워드 알림, 순위 변동)에 대한 지원이 필요하나 PR에서는 일반적인 알림 비활성화 API만 제공합니다. 이슈 #367의 4가지 특정 알림 타입에 대한 설정이 AlarmType 열거형에 모두 포함되어 있고 지원되는지 확인 및 문서화해주세요.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed 제목이 PR의 주요 변경사항을 명확하게 요약하고 있으며, 알림 비활성화 API 추가라는 핵심 기능 변화를 정확히 나타냅니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 알림 비활성화 기능 구현과 직접 관련되어 있으며, 이슈 #367 범위 내의 변경입니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#367

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/java/ku_rum/backend/domain/alarm/application/AlarmService.java (1)

229-239: 버그: 조건문이 항상 false로 평가됨

LAST_KNOWN_LENGTH는 상수 2로 정의되어 있어, LAST_KNOWN_LENGTH != 2 조건은 항상 false입니다. 의도한 검증은 parts.length != LAST_KNOWN_LENGTH인 것으로 보입니다.

🔎 제안된 수정
         if (lastKnown != null) {
             String[] parts = lastKnown.split("_");
-            if (LAST_KNOWN_LENGTH != 2) {
+            if (parts.length != LAST_KNOWN_LENGTH) {
                 throw new GlobalException(BaseExceptionResponseStatus.INVALID_CURSOR_FORMAT);
             }
🧹 Nitpick comments (9)
src/test/java/ku_rum/backend/domain/alarm/presentation/AlarmControllerTest.java (3)

203-204: 불필요한 Boolean.valueOf(true) 사용

Boolean.valueOf(true) 대신 true를 직접 사용하면 됩니다. 자동 박싱이 처리됩니다.

🔎 제안된 수정
-        PatchDisableAlarmResponse response = new PatchDisableAlarmResponse(1L, AlarmType.NEW_NOTICE,
-                Boolean.valueOf(true));
+        PatchDisableAlarmResponse response = new PatchDisableAlarmResponse(1L, AlarmType.NEW_NOTICE, true);

252-254: 사용되지 않는 변수 request

request 변수가 선언되었지만 테스트에서 사용되지 않습니다. 불필요한 코드는 제거하는 것이 좋습니다.

🔎 제안된 수정
-        PatchDisableAlarmRequest request = new PatchDisableAlarmRequest(AlarmType.NEW_NOTICE);
         given(alarmService.findDisableAlarm(any()))
                 .willReturn(response);

225-241: REST Docs에 request body 필드 문서화 누락

patchDisableAlarm 테스트에서 request body에 alarmType 필드를 전송하지만, requestFields 문서화가 누락되었습니다. API 문서 완성도를 위해 추가하는 것이 좋습니다.

🔎 제안된 수정
                         ResourceSnippetParameters.builder()
                                 .tag("알림 조회 API")
                                 .description("특정 알림 활성화 또는 비활성화한다")
                                 .requestHeaders(
                                         headerWithName("Authorization").description("발급 받은 액세스 토큰입니다.")
                                 )
+                                .requestFields(
+                                        fieldWithPath("alarmType").description("알림 타입")
+                                )
                                 .responseFields(
src/main/java/ku_rum/backend/domain/alarm/dto/request/PatchDisableAlarmRequest.java (1)

5-6: alarmType 필드에 유효성 검사 누락

alarmType이 null로 전달될 경우 서비스 레이어에서 예기치 않은 동작이 발생할 수 있습니다. @NotNull 어노테이션을 추가하고, 컨트롤러에서 @Valid를 사용하여 요청을 검증하는 것이 좋습니다.

🔎 제안된 수정
 package ku_rum.backend.domain.alarm.dto.request;

+import jakarta.validation.constraints.NotNull;
 import ku_rum.backend.domain.alarm.domain.AlarmType;

-public record PatchDisableAlarmRequest(AlarmType alarmType) {
+public record PatchDisableAlarmRequest(@NotNull AlarmType alarmType) {
 }
src/main/java/ku_rum/backend/domain/alarm/presentation/AlarmController.java (2)

83-88: GET 엔드포인트 메서드명 변경 권장

PATCH와 GET 엔드포인트 모두 disableAlarm이라는 동일한 메서드명을 사용하고 있습니다. 컴파일은 되지만, 가독성과 유지보수성을 위해 GET 메서드는 getDisabledAlarms 또는 findDisabledAlarms로 명명하는 것이 좋습니다.

🔎 제안된 수정
     @GetMapping("/disable")
-    public BaseResponse<GetAlarmDisableResponse> disableAlarm(
+    public BaseResponse<GetAlarmDisableResponse> getDisabledAlarms(
             @AuthenticationPrincipal final CustomUserDetails userDetails) {
         GetAlarmDisableResponse response = alarmService.findDisableAlarm(userDetails);
         return BaseResponse.ok(response);
     }

75-81: @Valid 어노테이션 누락

PatchDisableAlarmRequest에 유효성 검사 어노테이션이 추가되면, 컨트롤러에서 @Valid를 사용하여 요청 본문을 검증해야 합니다.

🔎 제안된 수정
     @PatchMapping("/disable")
     public BaseResponse<PatchDisableAlarmResponse> disableAlarm(
             @AuthenticationPrincipal final CustomUserDetails userDetails,
-            @RequestBody PatchDisableAlarmRequest request) {
+            @Valid @RequestBody PatchDisableAlarmRequest request) {
         PatchDisableAlarmResponse response = alarmService.disableAlarm(userDetails, request);
         return BaseResponse.ok(response);
     }
src/main/java/ku_rum/backend/domain/alarm/application/AlarmService.java (2)

295-301: 메서드 간소화 가능

isDisableAlarm 메서드를 한 줄로 간소화할 수 있습니다.

🔎 제안된 수정
     private boolean isDisableAlarm(AlarmType alarmType, User user) {
-        Optional<UserDisabledAlarm> optional = userDisabledAlarmRepository.findByUserAndAlarmType(user, alarmType);
-        if (optional.isPresent()) {
-            return true;
-        }
-        return false;
+        return userDisabledAlarmRepository.findByUserAndAlarmType(user, alarmType).isPresent();
     }

143-168: @Transactional(readOnly = true) 적용 권장

findDisableAlarm 메서드는 읽기 전용 작업입니다. @Transactional(readOnly = true)를 추가하면 JPA가 더티 체킹을 건너뛰어 성능이 향상됩니다.

🔎 제안된 수정
+    @Transactional(readOnly = true)
     public GetAlarmDisableResponse findDisableAlarm(CustomUserDetails userDetails) {
         User user = userService.getUser();
         List<UserDisabledAlarm> userDisabledAlarms = userDisabledAlarmRepository.findByUser(user);
         return GetAlarmDisableResponse.from(userDisabledAlarms);
     }
src/main/java/ku_rum/backend/domain/alarm/domain/UserDisabledAlarm.java (1)

19-35: 데이터베이스 레벨에서 중복 방지를 위한 유니크 제약조건 추가 권장

현재 (user, alarmType) 조합의 고유성은 서비스 레이어에서만 보장됩니다. 데이터 무결성을 위해 데이터베이스 레벨에서 유니크 제약조건을 추가하는 것이 좋습니다.

🔎 제안된 수정
 @Entity
+@Table(uniqueConstraints = @UniqueConstraint(columnNames = {"user_id", "alarm_type"}))
 @NoArgsConstructor(access = AccessLevel.PROTECTED)
 @AllArgsConstructor(access = AccessLevel.PRIVATE)
 @Builder
 @Getter
 public class UserDisabledAlarm extends BaseEntity {

     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;

     @ManyToOne(fetch = FetchType.LAZY)
+    @JoinColumn(name = "user_id")
     private User user;

     @Enumerated(EnumType.STRING)
+    @Column(name = "alarm_type")
     private AlarmType alarmType;
 }

필요한 import 추가:

import jakarta.persistence.Column;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fdf7efe and deb4044.

📒 Files selected for processing (8)
  • src/main/java/ku_rum/backend/domain/alarm/application/AlarmService.java
  • src/main/java/ku_rum/backend/domain/alarm/domain/UserDisabledAlarm.java
  • src/main/java/ku_rum/backend/domain/alarm/domain/repository/UserDisabledAlarmRepository.java
  • src/main/java/ku_rum/backend/domain/alarm/dto/request/PatchDisableAlarmRequest.java
  • src/main/java/ku_rum/backend/domain/alarm/dto/response/GetAlarmDisableResponse.java
  • src/main/java/ku_rum/backend/domain/alarm/dto/response/PatchDisableAlarmResponse.java
  • src/main/java/ku_rum/backend/domain/alarm/presentation/AlarmController.java
  • src/test/java/ku_rum/backend/domain/alarm/presentation/AlarmControllerTest.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (3)
src/main/java/ku_rum/backend/domain/alarm/dto/response/GetAlarmDisableResponse.java (1)

7-13: LGTM!

팩토리 메서드 패턴을 사용하여 엔티티에서 DTO로의 변환을 깔끔하게 처리하고 있습니다.

src/main/java/ku_rum/backend/domain/alarm/domain/repository/UserDisabledAlarmRepository.java (1)

11-17: LGTM!

Spring Data JPA 네이밍 규칙을 따르는 적절한 리포지토리 인터페이스입니다. @Repository 어노테이션은 JpaRepository를 확장할 때 선택 사항이지만, 명시적으로 표시해도 문제없습니다.

src/main/java/ku_rum/backend/domain/alarm/dto/response/PatchDisableAlarmResponse.java (1)

6-11: LGTM!

엔티티에서 응답 DTO로의 변환을 위한 팩토리 메서드가 적절하게 구현되어 있습니다.

@github-actions
Copy link

Test Results

 37 files   37 suites   10s ⏱️
152 tests 152 ✅ 0 💤 0 ❌
153 runs  153 ✅ 0 💤 0 ❌

Results for commit deb4044.

Copy link
Contributor

@kmw10693 kmw10693 left a comment

Choose a reason for hiding this comment

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

고생하셨습니다~

@kmw10693 kmw10693 merged commit f416a9c into develop Dec 23, 2025
4 checks passed
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.

알림 설정 API

3 participants