Skip to content

Conversation

@seongjunnoh
Copy link
Collaborator

@seongjunnoh seongjunnoh commented Sep 26, 2025

#️⃣ 연관된 이슈

📝 작업 내용

v1.3.0 업데이트 내용

  1. [feat] 알림 읽음 처리 api 개발
  2. [feat] 알림 관련 페이지 리다이렉트를 위한 데이터 반환하는 기능 추가
  3. [chore] 매트릭 수집을 위해 프로메테우스 의존성 추가

📸 스크린샷

💬 리뷰 요구사항

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

📌 PR 진행 시 이러한 점들을 참고해 주세요

* P1 : 꼭 반영해 주세요 (Request Changes) - 이슈가 발생하거나 취약점이 발견되는 케이스 등
* P2 : 반영을 적극적으로 고려해 주시면 좋을 것 같아요 (Comment)
* P3 : 이런 방법도 있을 것 같아요~ 등의 사소한 의견입니다 (Chore)

Summary by CodeRabbit

  • New Features

    • 알림 읽음 처리 API(/notifications/check) 추가: 처리 결과로 화면 이동 정보(route, params) 반환.
    • 푸시 알림에 redirect 정보 및 notificationId 포함으로 앱 내 이동 경험 개선.
    • 방 게시글 검색 기본 페이지 크기 10→20으로 확대.
    • FCM 토큰 관련 요청 필드 검증 메시지 한글화.
  • Documentation

    • Swagger에 알림 읽음 처리 응답 및 오류 코드 설명 추가.
  • Chores

    • Prometheus 메트릭스 지원 추가 및 /actuator/prometheus 접근 허용(모니터링 강화).

seongjunnoh and others added 30 commits September 22, 2025 17:17
… 데이터 삭제 (#308)

- 리다이렉트를 위한 데이터들은 fcm 푸시알림에 포함시키는게 아니라, 다른 api 를 통해 응답하도록 수정
- 알림 DB 저장 시에 RedirectSpec 또한 전달받아 저장하도록 수정
- 저장한 알림의 notificationId 값을 이벤트 퍼블리시 과정에 포함해야하므로, EventCommandInvoker 에게 전달하도록 수정
- 알림에 함께 저장해야할 redirectSpec 구성
- 이 값을 포함해서 NotificationSyncExecutor 호출하도록 수정
- 마찬가지로 redirectSpec 구성 & 이 값을 포함해서 NotificationSyncExecutor 를 호출하도록 수정
- 퍼블리시하는 이벤트의 구성 수정에 따른 기존 코드 수정
- 리스닝하는 이벤트의 구성 수정에 따른 기존 코드 수정
- PlatformType 의 정적 팩토리 메서드를 활용하여 지원하는 FE 플랫폼인지 확인하도록 코드 수정
- 필요없는 String code value 삭제
- message/domain 패키지 -> notification/domain/value 패키지로 이동
- FolloweeNewPost -> FolloweeNewFeed 로 네이밍 수정
- notification 패키지 하위로 이동
- 프로젝트 전체의 util 적인 성질보다는 notification 패키지 하위에서 사용되는 value 로 생각하는 것으로 결정됨
…rect-data-storage

[feat] 알림센터 알림 or 푸시알림 클릭 시 BE 로직 구현
seongjunnoh and others added 10 commits September 24, 2025 18:01
- 이미 읽음 처리된 알림에 대한 요청을 받더라도 정상 응답 하도록 수정
…eck-api

[hotfix] 알림 읽음 처리 api 수정
- postType 인자의 타입을 String -> PostType enum 으로 수정
- 모임방 게시글 상세화면에 해당하는 MessageRoute 통일
- FE 요청으로 댓글 모달창 오픈 여부를 나타내는 "openComments" 키 값을 RedirectSpec에 추가
…redirect-data-structure

[refactor] 알림 리다이렉트를 위한 데이터 구조 수정
@coderabbitai
Copy link

coderabbitai bot commented Sep 26, 2025

Walkthrough

프로메테우스 메트릭과 액추에이터 화이트리스트를 추가하고, 알림 시스템 전반을 리다이렉트 스펙·notificationId 중심으로 재설계했습니다. 이벤트 DTO/포트/어댑터 시그니처를 통일했고, 디스패치/오케스트레이터/익스큐터 흐름을 notificationId 기반으로 변경했습니다. 알림 읽음 처리 API와 도메인/영속/마이그레이션을 추가했으며, PostType enum 적용 및 테스트를 갱신했습니다.

Changes

Cohort / File(s) Summary
빌드·보안(프로메테우스)
build.gradle, src/main/java/konkuk/thip/common/security/constant/SecurityWhitelist.java
프로메테우스 레지스트리 의존성 추가 및 /actuator/prometheus 화이트리스트 등록
댓글 서비스 PostType 적용
.../comment/application/service/CommentCreateService.java, .../comment/application/service/CommentLikeService.java
문자열 비교 제거, PostType 스위치로 분기; 룸 알림 오케스트레이터에 enum 전달
피드 생성/리스너 네이밍 정합화
.../feed/application/service/FeedCreateService.java, .../message/adapter/in/event/MessageFeedEventListener.java, .../message/application/port/in/FeedNotificationDispatchUseCase.java
FolloweeNewPost → FolloweeNewFeed 명칭 변경 및 호출/핸들러 시그니처 반영
메시지 이벤트 DTO/퍼블리셔/포트 통합
.../message/adapter/out/event/FeedEventPublisherAdapter.java, .../message/adapter/out/event/RoomEventPublisherAdapter.java, .../message/adapter/out/event/dto/FeedEvents.java, .../message/adapter/out/event/dto/RoomEvents.java, .../message/application/port/out/FeedEventCommandPort.java, .../message/application/port/out/RoomEventCommandPort.java, .../message/domain/MessageRoute.java
이벤트 페이로드를 (title, content, notificationId, targetUserId)로 단순화; actor/room/post 컨텍스트 제거; MessageRoute(enum, message 도메인) 삭제
알림 디스패치 리팩토링
.../message/application/service/FeedNotificationDispatchService.java, .../message/application/service/RoomNotificationDispatchService.java
배치 전송 경로를 통일된 push(userId, notification, notificationId)로 변경; 라우트 기반 메시지 생성 제거, 데이터에 notificationId 포함
알림 API 및 검증 메시지
.../notification/adapter/in/web/NotificationCommandController.java, .../notification/adapter/in/web/request/NotificationMarkToCheckedRequest.java, .../notification/adapter/in/web/response/NotificationMarkToCheckedResponse.java, .../notification/adapter/in/web/request/FcmTokenRegisterRequest.java, .../notification/adapter/in/web/request/FcmTokenDeleteRequest.java, .../notification/adapter/in/web/request/FcmTokenEnableStateChangeRequest.java, .../common/swagger/SwaggerResponseDescription.java
알림 읽음 처리 엔드포인트/DTO 추가; FCM 요청 필드 검증 메시지 명시 및 platformType을 String 입력→도메인 변환
에러코드 추가
.../common/exception/code/ErrorCode.java
알림 리다이렉트 직렬화/역직렬화 실패, 미존재, 권한 없음, 이미 읽음 에러 코드 추가
알림 도메인/값 객체
.../notification/domain/Notification.java, .../notification/domain/value/MessageRoute.java, .../notification/domain/value/NotificationRedirectSpec.java, .../notification/domain/value/NotificationRedirectSpecConverter.java, .../notification/domain/value/PlatformType.java
Notification에 redirectSpec 필드와 소유자 검증/읽음 처리 메서드 추가; 라우트/리다이렉트 스펙/컨버터/플랫폼 타입(from 변환) 추가
알림 영속/매퍼/마이그레이션
.../notification/adapter/out/jpa/NotificationJpaEntity.java, .../notification/adapter/out/mapper/NotificationMapper.java, .../notification/adapter/out/persistence/NotificationCommandPersistenceAdapter.java, src/main/resources/db/migration/V250921__Add_notification_redirect_spec.sql
redirect_spec 컬럼 추가 및 JPA 변환/매핑; save가 ID 반환; findById/update 추가
알림 오케스트레이터/익스큐터 계약 변경
.../notification/application/service/NotificationSyncExecutor.java, .../notification/application/service/EventCommandInvoker.java, .../notification/application/service/FeedNotificationOrchestratorSyncImpl.java, .../notification/application/service/RoomNotificationOrchestratorSyncImpl.java, .../notification/application/port/in/FeedNotificationOrchestrator.java, .../notification/application/port/in/RoomNotificationOrchestrator.java, .../notification/application/port/in/NotificationMarkUseCase.java, .../notification/application/service/NotificationMarkService.java, .../notification/application/service/template/feed/FolloweeNewFeedTemplate.java, .../notification/application/port/out/NotificationCommandPort.java
Executor가 redirectSpec 포함 저장 후 notificationId로 publish 호출; Invoker 시그니처에 notificationId 추가; Room 오케스트레이터 postType을 enum으로; 읽음 처리 UseCase/Service 추가; 템플릿/포트/인터페이스 갱신
피드/포스트 좋아요 분기 정리
.../post/application/service/PostLikeService.java
postType 스위치로 알림 전송 분기 단순화
검색 페이지 사이즈 변경
.../roompost/application/service/RoomPostSearchService.java
기본 페이지 사이즈 10 → 20
테스트 갱신
src/test/java/... 전체 다수
이벤트 시그니처/notificationId/redirectSpec 반영, PostType enum 적용, API 통합 테스트 추가

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Service as Orchestrator/Service
  participant Exec as NotificationSyncExecutor
  participant Repo as NotificationCommandPort
  participant Pub as EventCommandInvoker (Publisher)
  participant Msg as Message Dispatch

  Service->>Exec: execute(template, args, targetUserId, redirectSpec, invoker)
  Exec->>Repo: save(title, content, category, targetUserId, redirectSpec)
  Repo-->>Exec: notificationId
  Exec->>Pub: publish(title, content, notificationId)
  Pub->>Msg: send(targetUserId, notificationId, data{category,action})
  Msg-->>Service: (async) delivery results
Loading
sequenceDiagram
  autonumber
  actor User
  participant API as NotificationCommandController
  participant UseCase as NotificationMarkUseCase
  participant Port as NotificationCommandPort
  participant Dom as Notification

  User->>API: POST /notifications/check {notificationId}
  API->>UseCase: markToChecked(notificationId, userId)
  UseCase->>Port: getByIdOrThrow(notificationId)
  Port-->>UseCase: Notification
  UseCase->>Dom: validateOwner(userId)
  UseCase->>Dom: markToChecked()
  UseCase->>Port: update(Notification)
  UseCase-->>API: NotificationMarkToCheckedResponse(route, params)
  API-->>User: BaseResponse{data: {route, params}}
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested labels

🚀 deploy

Suggested reviewers

  • hd0rable

Poem

새 알림 톡, 토끼는 깡총 뛰네 🥕
리다이렉트 지도를 품고, ID를 손에!
읽음 체크 찰칵—길도 함께 전해요.
피드든 룸이든, 한 번에 척척.
메트릭 노래하며, 오늘도 배포 쿨럭!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed 해당 PR 제목은 주요 변경 사항인 v1.3.0 운영 서버 배포를 명확하고 간결하게 전달하며 불필요한 파일 목록이나 모호한 용어 없이 읽기 쉽습니다.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch develop

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


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.

@github-actions
Copy link

Test Results

479 tests   479 ✅  44s ⏱️
142 suites    0 💤
142 files      0 ❌

Results for commit 7b50425.

@seongjunnoh seongjunnoh merged commit 7619564 into main Sep 26, 2025
4 of 6 checks passed
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: 5

🧹 Nitpick comments (22)
src/main/java/konkuk/thip/post/application/service/PostLikeService.java (1)

71-84: 새로운 PostType 추가 시 바로 실패하도록 방어 코드를 넣어주세요.

현재 switch 문이 FEED/RECORD/VOTE 외의 값에 대해서는 조용히 빠져나가므로, 향후 PostType이 확장될 때 알림이 누락된 채 디버깅이 어려워질 수 있습니다. default 분기를 추가해 예상치 못한 값이 들어오면 즉시 예외를 던지도록 수정하는 편이 안전합니다.

         switch (command.postType()) {
             case FEED ->
                     feedNotificationOrchestrator.notifyFeedLiked(
                             postQueryDto.creatorId(), actorUser.getId(), actorUser.getNickname(), postQueryDto.postId()
                     );
             case RECORD, VOTE ->
                     roomNotificationOrchestrator.notifyRoomPostLiked(
                             postQueryDto.creatorId(), actorUser.getId(), actorUser.getNickname(), postQueryDto.roomId(), postQueryDto.page(), postQueryDto.postId(), command.postType()
                     );
+            default ->
+                    throw new IllegalStateException("Unsupported postType for like notification: " + command.postType());
 
         }
src/main/java/konkuk/thip/notification/domain/value/MessageRoute.java (1)

3-8: 불필요한 Lombok 어노테이션 제거 제안

필드가 없는 enum이라 @Getter, @RequiredArgsConstructor가 실질적으로 아무 코드도 생성하지 않습니다. 어노테이션을 제거하면 Lombok 의존성을 줄이고 선언부를 더 단순하게 유지할 수 있습니다.

적용 예시는 다음과 같습니다:

-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-
-@Getter
-@RequiredArgsConstructor
 public enum MessageRoute {
src/main/java/konkuk/thip/notification/domain/value/NotificationRedirectSpec.java (1)

5-11: 리다이렉트 파라미터 Map 방어 복사 고려

NotificationRedirectSpec 생성 시 외부에서 전달된 Map을 그대로 보관하면 호출 측이 객체를 수정해도 도메인 상태가 변하는 부작용이 생길 수 있습니다. 앞으로 JSON 직렬화/역직렬화 과정 또는 테스트에서 HashMap을 직접 넘길 일이 많을 텐데, 불변 보장을 위해 방어적 복사나 Map.copyOf 적용을 검토해 주세요.

src/test/java/konkuk/thip/notification/application/service/FeedNotificationOrchestratorSyncImplUnitTest.java (2)

40-44: 리다이렉트 스펙까지 검증하도록 단언 보강 제안

새롭게 추가된 redirectSpec을 any()로 넘기면 동작을 보장하는 테스트가 빠집니다. 실제로는 어떤 MessageRoute와 파라미터가 전달되어야 하는지가 핵심이므로, 캡처 등을 활용해 기대값을 확인하는 단언을 추가하면 회귀를 막을 수 있습니다.


49-52: Publish 시그니처 변경사항 반영 여부 다시 확인

invoker.publish("title", "content", 123L) 호출이 실제 시그니처(redirectSpec이나 targetUserId 등의 추가 인자)를 모두 반영하는지 확인이 필요합니다. 만약 프로덕션 코드가 더 많은 인자를 요구한다면 테스트는 컴파일은 되지만 런타임 흐름과 어긋나 오류를 놓칠 수 있습니다.

src/test/java/konkuk/thip/notification/application/service/RoomNotificationOrchestratorSyncImplUnitTest.java (2)

41-45: RedirectSpec 기대값 단언 추가 제안

Feed와 동일하게 Room 시나리오에서도 redirectSpec이 핵심인데, 현재 any()로 흘려보내고 있어 회귀 탐지가 어렵습니다. 실제 기대하는 라우트/파라미터를 검증하도록 보강해 주세요.


50-53: Invoker publish 인자 검증 재확인

invoker.publish 호출이 프로덕션 시그니처(예: redirectSpec, targetUserId 등)를 충족하는지 Cross-check가 필요합니다. 테스트와 본 코드가 어긋나면 런타임에서 누락된 인자로 실패할 수 있습니다.

src/test/java/konkuk/thip/notification/application/service/RoomNotificationOrchestratorSyncImplTest.java (1)

114-138: 커밋 후 저장 엔티티 조회 시 동시성 고려 필요

notificationJpaRepository.findAll().get(0)은 다수 알림이 존재할 경우 순서에 의존해 오작동할 수 있습니다. findAll() 대신 방금 저장된 엔티티의 ID를 활용하거나 정렬/필터를 명시적으로 지정하는 편이 안전합니다.

src/test/java/konkuk/thip/notification/application/service/NotificationSyncExecutorTest.java (1)

38-40: publish 호출 시 notificationId(42)까지 전달되었는지 검증 추가 제안

현재는 예외 미전파만 검증합니다. 회귀 방지를 위해 invoker.publish가 템플릿 값과 save가 리턴한 42L로 호출되었는지도 검증해 주세요.

다음 변경을 권장합니다:

-        // publish 호출 시 강제로 예외를 던지는 invoker
-        EventCommandInvoker invoker = (title, content, notificationId) -> {
-            throw new RuntimeException("강제 퍼블리시 실패");
-        };
+        // publish 호출 시 강제로 예외를 던지는 invoker(mock)
+        EventCommandInvoker invoker = mock(EventCommandInvoker.class);
+        doThrow(new RuntimeException("강제 퍼블리시 실패"))
+                .when(invoker).publish(anyString(), anyString(), anyLong());

그리고 실행 후 호출 인자 검증을 추가합니다(아래 위치에 삽입):

         assertThatCode(() ->
                 executor.execute(template, "dummyArgs", 123L, invoker)
         ).doesNotThrowAnyException();
+
+        // invoker.publish가 템플릿 값과 저장된 notificationId(42L)로 호출되었는지 검증
+        verify(invoker, times(1)).publish(eq("테스트제목"), eq("테스트내용"), eq(42L));
src/main/java/konkuk/thip/notification/adapter/in/web/NotificationCommandController.java (1)

68-79: 엔드포인트 동작은 적절. 메트릭(프로메테우스/마이크로미터) 계수기 추가를 권장

읽음 처리의 성공/이미-읽음/권한오류 비율을 관측하면 UX와 품질 지표에 도움이 됩니다.

예시(참고용): MeterRegistry 주입 후 라우트 태그로 카운팅

// 필드 주입
private final io.micrometer.core.instrument.MeterRegistry meterRegistry;

// 메서드 내부
var res = notificationMarkUseCase.markToChecked(request.notificationId(), userId);
meterRegistry.counter("notifications.mark.checked", "route", res.route().name()).increment();
return BaseResponse.ok(res);
src/main/java/konkuk/thip/notification/adapter/in/web/request/NotificationMarkToCheckedRequest.java (1)

9-12: ID 유효성 제약 강화(@positive) 권장

도메인에 도달하기 전에 Bean Validation으로 음수/0을 차단하면 불필요한 처리 비용을 줄이고 에러 메시지를 일관화할 수 있습니다.

-        @NotNull
+        @NotNull
+        @jakarta.validation.constraints.Positive
         @Schema(description = "읽음 처리할 알림 ID", example = "1")
         Long notificationId
src/main/java/konkuk/thip/notification/application/service/NotificationMarkService.java (1)

26-31: 예외 범위 축소 제안(이미-읽음만 무시)

InvalidStateException을 포괄적으로 무시하면 다른 상태 오류도 숨겨질 수 있습니다. NOTIFICATION_ALREADY_CHECKED만 무시하고, 그 외는 재던지기를 권장합니다.

src/main/java/konkuk/thip/notification/adapter/in/web/request/FcmTokenRegisterRequest.java (1)

18-21: 플랫폼 타입 입력값을 Bean Validation으로 선제 제한

서비스 계층까지 진입하기 전에 허용값을 검증하면 예외 경로를 줄이고 오류 메시지를 일관화할 수 있습니다. 대소문자 무시 패턴 예시:

-        @NotBlank(message = "플랫폼 타입은 필수입니다.")
+        @NotBlank(message = "플랫폼 타입은 필수입니다.")
+        @jakarta.validation.constraints.Pattern(
+                regexp = "^(?i)(ANDROID|WEB)$",
+                message = "플랫폼 타입은 ANDROID 또는 WEB 이어야 합니다."
+        )
         @Schema(description = "플랫폼 타입 (ANDROID 또는 WEB)", example = "ANDROID")
         String platformType

참고: PlatformType.from에서 추가 케이스/에러 매핑이 있다면, 검증 메시지와의 정합성을 유지해 주세요. Based on learnings

src/main/java/konkuk/thip/notification/domain/Notification.java (1)

43-47: NPE 방지용 사전 조건 체크(선택)

userId 또는 targetUserId가 null일 경우를 방어하면 예외 원인 파악이 쉬워집니다.

-    public void validateOwner(Long userId) {
-        if (!targetUserId.equals(userId)) {
+    public void validateOwner(Long userId) {
+        if (userId == null || targetUserId == null || !targetUserId.equals(userId)) {
             throw new InvalidStateException(NOTIFICATION_ACCESS_FORBIDDEN);
         }
     }
src/main/java/konkuk/thip/notification/application/service/FeedNotificationOrchestratorSyncImpl.java (1)

56-123: FEED_DETAIL 리다이렉트 스펙 생성 중복 정리 제안

FEED_DETAIL 리다이렉트 스펙을 만드는 블록이 여러 메서드에 반복되고 있습니다. 헬퍼 메서드로 추출하면 파라미터 추가/변경 시 한 곳만 수정하면 되어 가독성과 유지보수성이 좋아집니다. 아래와 같이 정리해 보는 것 제안드립니다.

-        NotificationRedirectSpec redirectSpec = new NotificationRedirectSpec(
-                MessageRoute.FEED_DETAIL,
-                Map.of("feedId", feedId)
-        );
+        NotificationRedirectSpec redirectSpec = redirectToFeedDetail(feedId);
+    private NotificationRedirectSpec redirectToFeedDetail(Long feedId) {
+        return new NotificationRedirectSpec(
+                MessageRoute.FEED_DETAIL,
+                Map.of("feedId", feedId)
+        );
+    }

Based on learnings

src/main/java/konkuk/thip/notification/domain/value/NotificationRedirectSpecConverter.java (1)

17-37: ObjectMapper 모듈 자동 등록 제안

현재 ObjectMapper가 기본 설정이라 NotificationRedirectSpecparamsLocalDateTime 같은 JSR-310 타입이 들어오면 직렬화/역직렬화가 실패할 수 있습니다. .findAndRegisterModules()를 호출해 프로젝트 전역 설정과 동일하게 모듈을 붙여두면 안전합니다.

-    private static final ObjectMapper objectMapper = new ObjectMapper();
+    private static final ObjectMapper objectMapper = new ObjectMapper().findAndRegisterModules();
src/main/java/konkuk/thip/notification/application/service/NotificationSyncExecutor.java (1)

47-53: 푸시 이벤트 발행 실패 시 원인 분석 정보 부족

푸시 발행 실패 로그에 notificationRedirectSpec 정보가 빠져 있어 실제 리다이렉션 스펙 문제가 원인일 때 진단이 힘듭니다. 최소한 notificationRedirectSpec도 함께 남겨 주세요.

문제 재현 여부 확인 후 아래 패치를 적용해 주세요:

-            log.error("푸시 알림 이벤트 퍼블리시 실패 targetUserId = {}, title = {}, notificationId = {}", targetUserId, title, notificationId, e);
+            log.error("푸시 알림 이벤트 퍼블리시 실패 targetUserId = {}, title = {}, notificationId = {}, redirectSpec = {}",
+                    targetUserId, title, notificationId, notificationRedirectSpec, e);
src/test/java/konkuk/thip/common/util/TestEntityFactory.java (1)

400-402: redirectSpec 파라미터 순서를 보존하도록 복사 제안

테스트에서 JSON 직렬화 결과를 검증할 때, 전달된 paramsHashMap이나 JDK 구현에 따라 키 순서가 달라지면 직렬화 문자열이 비결정적이어서 테스트가 플래키해질 수 있습니다. 한 번 LinkedHashMap으로 감싸서 입력 순서를 고정하면 이런 변동을 피할 수 있습니다.

-    public static NotificationRedirectSpec createNotificationRedirectSpec(MessageRoute route, Map<String, Object> params) {
-        return new NotificationRedirectSpec(route, params);
-    }
+    public static NotificationRedirectSpec createNotificationRedirectSpec(MessageRoute route, Map<String, Object> params) {
+        Map<String, Object> orderedParams = (params == null) ? null : new LinkedHashMap<>(params);
+        return new NotificationRedirectSpec(route, orderedParams);
+    }

Based on learnings

src/main/java/konkuk/thip/message/adapter/out/event/RoomEventPublisherAdapter.java (1)

75-86: 용어 불일치 정리 제안: Host ↔ Owner

메서드명 publishRoomJoinEventToHost vs DTO명 RoomJoinRequestedToOwnerEvent 간 용어 불일치가 있습니다. 도메인 의도(방장=Owner)로 통일을 권장합니다.

다음과 같이 리네임을 제안합니다(포트 인터페이스·호출부 동시 변경 필요):

-    public void publishRoomJoinEventToHost(
+    public void publishRoomJoinRequestedToOwnerEvent(
             String title, String content, Long notificationId,
             Long targetUserId) {
-        publisher.publishEvent(RoomEvents.RoomJoinRequestedToOwnerEvent.builder()
+        publisher.publishEvent(RoomEvents.RoomJoinRequestedToOwnerEvent.builder()
             .title(title)
             .content(content)
             .notificationId(notificationId)
             .targetUserId(targetUserId)
             .build());
     }
src/main/java/konkuk/thip/feed/application/service/FeedCreateService.java (1)

70-76: 팔로워 중복 제거 및 메모리 사용 개선 제안

팔로워 목록에 중복이 존재할 가능성을 고려해 LinkedHashSet으로 ID 중복 제거 후 순회하면 안전합니다. 팔로워 수가 많은 경우에도 불필요한 중복 작업을 줄일 수 있습니다. Based on learnings

다음과 같이 수정 제안:

-    private void sendNotifications(FeedCreateCommand command, Long savedFeedId) {
-        List<User> targetUsers = userQueryPort.getAllFollowersByUserId(command.userId());
-        User actorUser = userCommandPort.findById(command.userId());
-        for (User targetUser : targetUsers) {
-            feedNotificationOrchestrator.notifyFolloweeNewFeed(targetUser.getId(), actorUser.getId(), actorUser.getNickname(), savedFeedId);
-        }
-    }
+    private void sendNotifications(FeedCreateCommand command, Long savedFeedId) {
+        List<User> targetUsers = userQueryPort.getAllFollowersByUserId(command.userId());
+        if (targetUsers.isEmpty()) return;
+        User actorUser = userCommandPort.findById(command.userId());
+        var uniqueTargetUserIds = targetUsers.stream()
+                .map(User::getId)
+                .collect(java.util.stream.Collectors.toCollection(java.util.LinkedHashSet::new));
+        for (Long targetUserId : uniqueTargetUserIds) {
+            feedNotificationOrchestrator.notifyFolloweeNewFeed(targetUserId, actorUser.getId(), actorUser.getNickname(), savedFeedId);
+        }
+    }

추가 import 필요:

import java.util.LinkedHashSet;
import java.util.stream.Collectors;
src/main/java/konkuk/thip/notification/adapter/out/jpa/NotificationJpaEntity.java (2)

5-5: 엔티티의 도메인 의존 제거 권고

엔티티가 도메인(Notification)을 import하면 계층 간 의존 화살표가 복잡해집니다. 업데이트 메서드는 원시 값만 받도록 단순화하세요. 실용적으로는 동작하나 장기 유지보수 관점에서 분리 권장.

다음과 같이 변경 제안:

-import konkuk.thip.notification.domain.Notification;

45-47: updateFrom(Notification) → updateChecked(boolean)로 단순화 제안

도메인 타입 의존을 제거하고 의도(체크 상태 변경)만 노출하도록 메서드 축소를 권장합니다.

-    public void updateFrom(Notification notification) {
-        this.isChecked = notification.isChecked();  // 현재는 isChecked만 업데이트 가능
-    }
+    public void updateChecked(boolean isChecked) {
+        this.isChecked = isChecked;
+    }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 7f6b333 and 7b50425.

📒 Files selected for processing (56)
  • build.gradle (1 hunks)
  • src/main/java/konkuk/thip/comment/application/service/CommentCreateService.java (1 hunks)
  • src/main/java/konkuk/thip/comment/application/service/CommentLikeService.java (1 hunks)
  • src/main/java/konkuk/thip/common/exception/code/ErrorCode.java (1 hunks)
  • src/main/java/konkuk/thip/common/security/constant/SecurityWhitelist.java (1 hunks)
  • src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java (2 hunks)
  • src/main/java/konkuk/thip/feed/application/service/FeedCreateService.java (1 hunks)
  • src/main/java/konkuk/thip/message/adapter/in/event/MessageFeedEventListener.java (1 hunks)
  • src/main/java/konkuk/thip/message/adapter/out/event/FeedEventPublisherAdapter.java (1 hunks)
  • src/main/java/konkuk/thip/message/adapter/out/event/RoomEventPublisherAdapter.java (1 hunks)
  • src/main/java/konkuk/thip/message/adapter/out/event/dto/FeedEvents.java (1 hunks)
  • src/main/java/konkuk/thip/message/adapter/out/event/dto/RoomEvents.java (1 hunks)
  • src/main/java/konkuk/thip/message/application/port/in/FeedNotificationDispatchUseCase.java (1 hunks)
  • src/main/java/konkuk/thip/message/application/port/out/FeedEventCommandPort.java (1 hunks)
  • src/main/java/konkuk/thip/message/application/port/out/RoomEventCommandPort.java (1 hunks)
  • src/main/java/konkuk/thip/message/application/service/FeedNotificationDispatchService.java (1 hunks)
  • src/main/java/konkuk/thip/message/application/service/RoomNotificationDispatchService.java (1 hunks)
  • src/main/java/konkuk/thip/message/domain/MessageRoute.java (0 hunks)
  • src/main/java/konkuk/thip/notification/adapter/in/web/NotificationCommandController.java (3 hunks)
  • src/main/java/konkuk/thip/notification/adapter/in/web/request/FcmTokenDeleteRequest.java (1 hunks)
  • src/main/java/konkuk/thip/notification/adapter/in/web/request/FcmTokenEnableStateChangeRequest.java (1 hunks)
  • src/main/java/konkuk/thip/notification/adapter/in/web/request/FcmTokenRegisterRequest.java (1 hunks)
  • src/main/java/konkuk/thip/notification/adapter/in/web/request/NotificationMarkToCheckedRequest.java (1 hunks)
  • src/main/java/konkuk/thip/notification/adapter/in/web/response/NotificationMarkToCheckedResponse.java (1 hunks)
  • src/main/java/konkuk/thip/notification/adapter/out/jpa/NotificationJpaEntity.java (2 hunks)
  • src/main/java/konkuk/thip/notification/adapter/out/mapper/NotificationMapper.java (2 hunks)
  • src/main/java/konkuk/thip/notification/adapter/out/persistence/NotificationCommandPersistenceAdapter.java (3 hunks)
  • src/main/java/konkuk/thip/notification/application/port/in/FeedNotificationOrchestrator.java (1 hunks)
  • src/main/java/konkuk/thip/notification/application/port/in/NotificationMarkUseCase.java (1 hunks)
  • src/main/java/konkuk/thip/notification/application/port/in/RoomNotificationOrchestrator.java (3 hunks)
  • src/main/java/konkuk/thip/notification/application/port/out/NotificationCommandPort.java (1 hunks)
  • src/main/java/konkuk/thip/notification/application/service/EventCommandInvoker.java (1 hunks)
  • src/main/java/konkuk/thip/notification/application/service/FeedNotificationOrchestratorSyncImpl.java (6 hunks)
  • src/main/java/konkuk/thip/notification/application/service/NotificationMarkService.java (1 hunks)
  • src/main/java/konkuk/thip/notification/application/service/NotificationSyncExecutor.java (2 hunks)
  • src/main/java/konkuk/thip/notification/application/service/RoomNotificationOrchestratorSyncImpl.java (7 hunks)
  • src/main/java/konkuk/thip/notification/application/service/template/feed/FolloweeNewFeedTemplate.java (1 hunks)
  • src/main/java/konkuk/thip/notification/domain/Notification.java (2 hunks)
  • src/main/java/konkuk/thip/notification/domain/value/MessageRoute.java (1 hunks)
  • src/main/java/konkuk/thip/notification/domain/value/NotificationRedirectSpec.java (1 hunks)
  • src/main/java/konkuk/thip/notification/domain/value/NotificationRedirectSpecConverter.java (1 hunks)
  • src/main/java/konkuk/thip/notification/domain/value/PlatformType.java (1 hunks)
  • src/main/java/konkuk/thip/post/adapter/out/persistence/PostLikeCommandPersistenceAdapter.java (0 hunks)
  • src/main/java/konkuk/thip/post/application/service/PostLikeService.java (1 hunks)
  • src/main/java/konkuk/thip/roompost/application/service/RoomPostSearchService.java (1 hunks)
  • src/main/resources/db/migration/V250921__Add_notification_redirect_spec.sql (1 hunks)
  • src/test/java/konkuk/thip/common/util/TestEntityFactory.java (3 hunks)
  • src/test/java/konkuk/thip/message/adapter/in/event/MessageFeedEventListenerTest.java (2 hunks)
  • src/test/java/konkuk/thip/message/adapter/in/event/MessageRoomEventListenerTest.java (2 hunks)
  • src/test/java/konkuk/thip/notification/adapter/in/web/NotificationMarkToCheckedApiTest.java (1 hunks)
  • src/test/java/konkuk/thip/notification/application/service/FeedNotificationOrchestratorSyncImplTest.java (1 hunks)
  • src/test/java/konkuk/thip/notification/application/service/FeedNotificationOrchestratorSyncImplUnitTest.java (2 hunks)
  • src/test/java/konkuk/thip/notification/application/service/NotificationSyncExecutorTest.java (3 hunks)
  • src/test/java/konkuk/thip/notification/application/service/RoomNotificationOrchestratorSyncImplTest.java (6 hunks)
  • src/test/java/konkuk/thip/notification/application/service/RoomNotificationOrchestratorSyncImplUnitTest.java (2 hunks)
  • src/test/java/konkuk/thip/notification/domain/NotificationTest.java (1 hunks)
💤 Files with no reviewable changes (2)
  • src/main/java/konkuk/thip/message/domain/MessageRoute.java
  • src/main/java/konkuk/thip/post/adapter/out/persistence/PostLikeCommandPersistenceAdapter.java
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#113
File: src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/RecentSearchCommandPersistenceAdapter.java:38-44
Timestamp: 2025-07-30T14:05:04.945Z
Learning: seongjunnoh는 코드 최적화 제안에 대해 구체적인 기술적 근거와 효율성 차이를 이해하고 싶어하며, 성능 개선 방식에 대한 상세한 설명을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#93
File: src/main/java/konkuk/thip/room/adapter/out/persistence/RoomQueryPersistenceAdapter.java:49-114
Timestamp: 2025-07-28T16:44:31.224Z
Learning: seongjunnoh는 코드 중복 문제에 대한 리팩토링 제안을 적극적으로 수용하고 함수형 인터페이스를 활용한 해결책을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#285
File: src/main/java/konkuk/thip/room/adapter/out/jpa/RoomStatus.java:1-7
Timestamp: 2025-08-31T05:25:14.845Z
Learning: seongjunnoh는 enum 의존성에 대해 유연한 접근을 선호하며, 도메인→어댑터 레이어 참조와 같은 아키텍처 layering 원칙보다 실용적인 구현을 우선시한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#195
File: src/main/java/konkuk/thip/feed/application/mapper/FeedQueryMapper.java:0-0
Timestamp: 2025-08-13T05:22:32.287Z
Learning: seongjunnoh는 데이터 무결성과 중복 방지에 대한 고민이 깊으며, LinkedHashSet을 활용한 중복 제거와 순서 보장을 동시에 달성하는 솔루션을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#305
File: src/main/java/konkuk/thip/notification/adapter/out/persistence/repository/NotificationQueryRepository.java:9-13
Timestamp: 2025-09-17T06:40:49.863Z
Learning: seongjunnoh는 메서드 네이밍 시 구현 세부사항보다 비즈니스 의도를 반영하는 것을 선호하며, 미래 확장성과 아키텍처 변화를 고려한 설계 철학을 가지고 있다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#278
File: src/main/java/konkuk/thip/TestTokenController.java:0-0
Timestamp: 2025-08-24T09:33:52.982Z
Learning: seongjunnoh는 Spring의 ConditionalOnProperty 동작 원리를 정확히 이해하고 있으며, 보안 이슈에 대해서도 실질적인 위험성을 기준으로 판단하는 실용적 접근을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#180
File: src/main/java/konkuk/thip/room/application/service/AttendanceCheckCreateService.java:22-40
Timestamp: 2025-08-14T09:15:31.371Z
Learning: seongjunnoh는 기술적 완벽성보다 실제 비즈니스 시나리오와 사용자 행동 패턴을 우선 고려하며, 발생 가능성이 낮은 엣지 케이스보다는 실용적인 구현을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#278
File: src/main/java/konkuk/thip/common/exception/code/ErrorCode.java:210-211
Timestamp: 2025-08-24T09:40:48.595Z
Learning: seongjunnoh는 HTTP 상태 코드 선택에 대해 기술적 근거와 코드베이스 내 일관성을 중요하게 생각하며, 구체적인 사례 분석을 통한 설명을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#112
File: src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedQueryRepositoryImpl.java:272-272
Timestamp: 2025-07-30T10:44:34.115Z
Learning: seongjunnoh는 피드 커서 페이지네이션에서 LocalDateTime 단일 커서 방식을 선호하며, 복합 키 기반 커서보다 구현 단순성과 성능을 우선시한다.
📚 Learning: 2025-07-03T03:05:05.031Z
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#43
File: src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java:0-0
Timestamp: 2025-07-03T03:05:05.031Z
Learning: THIP 프로젝트에서는 CQRS Port 분리 시 다음 컨벤션을 따름: CommandPort에는 findByXXX를 통해 도메인 엔티티를 찾아오는 메서드를 추가하고, QueryPort에는 조회 API의 response에 해당하는 데이터들을 DB로부터 조회하는 메서드를 추가함.

Applied to files:

  • src/main/java/konkuk/thip/notification/application/port/out/NotificationCommandPort.java
📚 Learning: 2025-07-26T06:09:00.850Z
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#101
File: src/main/java/konkuk/thip/comment/application/service/CommentCreateService.java:36-39
Timestamp: 2025-07-26T06:09:00.850Z
Learning: THIP 프로젝트에서 Record와 Vote는 Room에 속하지만 Feed는 Room에 속하지 않는 구조이며, 댓글 작성 시 Record/Vote에 대해서만 사용자가 해당 Room의 참가자인지 검증이 필요하다.

Applied to files:

  • src/main/java/konkuk/thip/message/adapter/out/event/dto/RoomEvents.java
📚 Learning: 2025-09-05T05:26:24.628Z
Learnt from: buzz0331
PR: THIP-TextHip/THIP-Server#293
File: src/main/java/konkuk/thip/notification/adapter/out/jpa/FcmTokenJpaEntity.java:44-51
Timestamp: 2025-09-05T05:26:24.628Z
Learning: FcmTokenJpaEntity에서 deviceId는 디바이스를 식별하는 불변 값으로, updateFrom 메서드에서 동기화할 필요가 없음

Applied to files:

  • src/main/java/konkuk/thip/notification/adapter/in/web/request/FcmTokenDeleteRequest.java
  • src/main/java/konkuk/thip/notification/adapter/in/web/request/FcmTokenEnableStateChangeRequest.java
📚 Learning: 2025-09-05T04:38:38.325Z
Learnt from: buzz0331
PR: THIP-TextHip/THIP-Server#293
File: src/main/java/konkuk/thip/notification/application/service/FcmRegisterService.java:21-0
Timestamp: 2025-09-05T04:38:38.325Z
Learning: FcmTokenJpaEntity의 deviceId 필드는 Column(unique = true) 제약과 데이터베이스 레벨 UNIQUE 제약이 모두 설정되어 있음

Applied to files:

  • src/main/java/konkuk/thip/notification/adapter/in/web/request/FcmTokenDeleteRequest.java
  • src/main/java/konkuk/thip/notification/adapter/in/web/request/FcmTokenRegisterRequest.java
  • src/main/java/konkuk/thip/notification/adapter/in/web/request/FcmTokenEnableStateChangeRequest.java
📚 Learning: 2025-09-05T04:38:38.325Z
Learnt from: buzz0331
PR: THIP-TextHip/THIP-Server#293
File: src/main/java/konkuk/thip/notification/application/service/FcmRegisterService.java:21-0
Timestamp: 2025-09-05T04:38:38.325Z
Learning: FcmTokenJpaEntity의 deviceId 필드는 JPA Column(unique = true)와 데이터베이스 레벨 UNIQUE 제약이 모두 설정되어 있어 중복 방지가 보장됨

Applied to files:

  • src/main/java/konkuk/thip/notification/adapter/in/web/request/FcmTokenDeleteRequest.java
  • src/main/java/konkuk/thip/notification/adapter/in/web/request/FcmTokenEnableStateChangeRequest.java
🧬 Code graph analysis (4)
src/test/java/konkuk/thip/notification/adapter/in/web/NotificationMarkToCheckedApiTest.java (1)
src/test/java/konkuk/thip/common/util/TestEntityFactory.java (1)
  • TestEntityFactory (35-403)
src/main/java/konkuk/thip/notification/domain/value/PlatformType.java (1)
src/main/java/konkuk/thip/notification/domain/FcmToken.java (1)
  • Getter (12-74)
src/main/java/konkuk/thip/message/adapter/out/event/FeedEventPublisherAdapter.java (1)
src/main/java/konkuk/thip/message/adapter/out/event/dto/FeedEvents.java (1)
  • FeedEvents (5-42)
src/main/java/konkuk/thip/message/adapter/out/event/RoomEventPublisherAdapter.java (1)
src/main/java/konkuk/thip/message/adapter/out/event/dto/RoomEvents.java (1)
  • RoomEvents (5-61)
⏰ 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 (41)
src/main/java/konkuk/thip/notification/adapter/in/web/request/FcmTokenDeleteRequest.java (1)

9-9: 검증 메시지 추가 👍

구체적인 오류 메시지로 사용자 응답 품질이 향상되었습니다. 잘 반영되었습니다.

build.gradle (1)

82-85: 프로메테우스 레지스트리 추가 👍

Spring Boot BOM이 io.micrometer:micrometer-registry-prometheus 버전을 함께 관리해 주기 때문에 별도 버전 지정 없이도 액추에이터 지표 노출 구성이 잘 맞습니다. 운영 배포에 적합해 보여요.

src/main/java/konkuk/thip/notification/adapter/in/web/request/FcmTokenEnableStateChangeRequest.java (1)

10-15: 검증 메시지 한글화 반영 👍

두 필드 모두 사용자 친화적인 검증 메시지가 추가되어 기존 로직에 부작용 없이 UX가 개선되었습니다.

src/main/java/konkuk/thip/common/security/constant/SecurityWhitelist.java (1)

20-20: Prometheus 엔드포인트 화이트리스트 추가 확인했습니다.

기존 패턴 수집 로직과 자연스럽게 연동되어 /actuator/prometheus 접근이 허용됩니다.

src/main/java/konkuk/thip/roompost/application/service/RoomPostSearchService.java (1)

51-51: 페이지당 기본 개수 상향 조정 확인

커서 생성 시 기본 페이지 사이즈를 20으로 늘리면서 서버 단 기본 배치가 맞춰졌습니다. 다른 로직에는 영향이 없고, 변경 의도대로 동작해 보입니다. 프론트엔드나 호출 측에서 10개 고정 가정이 있는지만 한번 더 점검하면 좋겠습니다.

src/main/java/konkuk/thip/notification/application/port/in/FeedNotificationOrchestrator.java (1)

17-17: 메서드명 변경으로 인한 일관성 개선이 우수합니다.

notifyFolloweeNewPost에서 notifyFolloweeNewFeed로의 변경은 Feed 도메인 전반의 명명 일관성을 높이고, 다른 Feed 관련 메서드들과의 용어 통일을 잘 달성했습니다.

src/main/java/konkuk/thip/notification/application/port/out/NotificationCommandPort.java (2)

12-12: 반환 타입 변경으로 알림 ID 활용이 개선되었습니다.

save 메서드가 void에서 Long을 반환하도록 변경된 점이 우수합니다. notificationId 기반의 이벤트 아키텍처에서 events can be identifiers (a notification that an order was shipped)처럼, 저장된 알림 ID를 즉시 후속 이벤트 처리에서 활용할 수 있게 해줍니다.


14-19: Helper 메서드 패턴으로 개발자 경험이 향상되었습니다.

findByIdgetByIdOrThrow 메서드의 추가로 예외 처리 로직의 재사용성이 크게 개선되었습니다. 특히 default 메서드를 통한 helper 제공은 코드 중복을 방지하고 일관된 예외 처리를 보장합니다. Based on learnings

src/main/java/konkuk/thip/message/application/port/out/FeedEventCommandPort.java (2)

6-8: Event-driven 아키텍처의 정보 은닉화가 우수합니다.

모든 메서드 시그니처에서 actorUserId, actorUsername, feedId 등의 비즈니스 컨텍스트를 제거하고 notificationId로 통합한 점이 탁월합니다. A producer publishes an event to the router, which filters and pushes the events to consumers. Producer services and consumer services are decoupled, which allows them to be scaled, updated, and deployed independently.처럼 이벤트 발행자와 소비자 간의 결합도를 크게 낮췄습니다.

Also applies to: 12-13, 17-18, 21-23, 27-28, 32-33


21-21: 메서드명 변경으로 도메인 일관성이 완성되었습니다.

publishFolloweeNewPostEvent에서 publishFolloweeNewFeedEvent로 변경하여 Feed 도메인 전반의 용어 통일이 완료되었습니다.

src/main/java/konkuk/thip/notification/application/port/in/RoomNotificationOrchestrator.java (2)

3-3: PostType enum 도입으로 타입 안전성이 강화되었습니다.

PostType import 추가는 타입 안전성 향상의 시작점입니다.


13-14: String에서 enum으로 전환하여 컴파일 타임 검증이 강화되었습니다.

String postType 파라미터를 PostType postType으로 변경한 점이 매우 우수합니다. 이는 런타임 오류 가능성을 제거하고 컴파일 타임에 유효하지 않은 postType 값을 검출할 수 있게 합니다.

Also applies to: 28-29, 31-32, 34-35

src/main/java/konkuk/thip/comment/application/service/CommentLikeService.java (2)

74-87: Switch 표현식으로 코드 가독성과 유지보수성이 향상되었습니다.

PostType enum을 활용한 switch 표현식으로 변경한 점이 탁월합니다. 특히 case RECORD, VOTE -> {}를 통해 유사한 로직을 효율적으로 그룹핑했으며, 새로운 PostType이 추가될 경우 컴파일러가 누락된 케이스를 검출할 수 있습니다. Based on learnings


84-84: 메서드 파라미터에 enum 전달로 타입 일관성이 완성되었습니다.

roomNotificationOrchestrator.notifyRoomCommentLikedpostType enum을 전달하여 String 기반 처리에서 벗어나 타입 안전성을 확보한 점이 우수합니다.

src/main/java/konkuk/thip/comment/application/service/CommentCreateService.java (1)

96-107: 중복 코드 제거와 타입 안전성 확보가 동시에 달성되었습니다.

두 메서드(sendNotificationsToPostWriter, sendNotificationsToParentCommentWriter) 모두에서 동일한 패턴으로 PostType.from() 변환과 switch 표현식을 적용한 점이 우수합니다. case RECORD, VOTE 그룹핑으로 코드 중복을 방지했으며, enum 활용으로 타입 안전성을 확보했습니다. Based on learnings

Also applies to: 113-124

src/main/java/konkuk/thip/message/adapter/out/event/FeedEventPublisherAdapter.java (1)

17-24: Event payload 단순화로 확장성과 유지보수성이 크게 개선되었습니다.

모든 이벤트 발행 메서드에서 복잡한 비즈니스 컨텍스트 파라미터(actorUserId, actorUsername, feedId)를 제거하고 단순한 notificationIdtargetUserId로 통합한 점이 탁월합니다. Event-driven architecture (EDA) is a software design pattern that enables the construction of scalable and loosely coupled systems. They are generated by various sources, published to an event bus or message broker, and consumed by interested components asynchronously.처럼 진정한 비동기 이벤트 아키텍처로 발전했습니다.

관련 FeedEvents.java 파일의 DTO 구조도 동일한 단순화를 따르고 있어 전체 시스템 일관성이 우수합니다.

Also applies to: 29-36, 41-48, 53-60, 65-72, 77-84

src/main/java/konkuk/thip/message/application/port/out/RoomEventCommandPort.java (1)

6-8: 통합된 이벤트 시그니처로 아키텍처 일관성이 완성되었습니다.

모든 Room 이벤트 메서드가 (title, content, notificationId, targetUserId) 시그니처로 통합된 점이 매우 우수합니다. 이전의 복잡한 파라미터들(actorUserId, actorUsername, roomId, page, postId, postType 등)을 제거하여 An event-driven architecture uses events to trigger and communicate between decoupled services and is common in modern applications built with microservices. A producer publishes an event to the router, which filters and pushes the events to consumers. 패턴을 완벽하게 구현했습니다.

Feed와 Room 이벤트 포트 간의 시그니처 일관성도 뛰어나 시스템 전체의 아키텍처 통일성이 달성되었습니다.

Also applies to: 12-13, 17-18, 22-23, 27-28, 32-33, 37-38, 42-43, 47-48

src/main/java/konkuk/thip/notification/adapter/out/mapper/NotificationMapper.java (1)

18-31: redirectSpec 매핑 추가 잘 보았습니다.

도메인과 JPA 엔티티 간에 redirectSpec을 빠짐없이 주고받도록 연결해 준 덕분에 새 리다이렉트 스펙 흐름이 안정적으로 작동하겠습니다.

src/main/java/konkuk/thip/message/application/port/in/FeedNotificationDispatchUseCase.java (1)

12-12: 메서드 시그니처 변경과 호출부 정합성 재확인 필요

포트 시그니처를 handleFolloweeNewFeed로 변경했으니, 이 인터페이스를 구현하거나 호출하는 모든 컴포넌트가 동일하게 리네임되었는지 확인이 필요합니다. 특히, 비동기 이벤트 리스너나 오케스트레이터 계층에서 구버전 메서드를 여전히 참조하면 런타임 실패가 발생할 수 있습니다. PR 전반을 다시 한 번 스캔해 주세요.

src/main/java/konkuk/thip/common/exception/code/ErrorCode.java (1)

234-238: 에러 코드 번호 중복 여부 확인 권장

205001~205005 구간이 기존 코드와 충돌하지 않는지, 또한 응답 매핑이나 문서화(스웨거, 프론트 상수)에서 새 코드를 모두 반영했는지 한 번 더 확인해 주세요. 번호 충돌 시 전파가 까다롭습니다.

src/main/java/konkuk/thip/message/adapter/in/event/MessageFeedEventListener.java (1)

37-38: 이벤트 타입 전환 후 기존 퍼블리셔 호출부 점검

리스너 시그니처를 FolloweeNewFeedEvent로 바꿨으니, 이벤트 퍼블리셔 측에서 동일한 타입으로 발행하고 있는지 한번 더 확인 부탁드립니다. 구버전 이벤트가 남아 있으면 리스너가 작동하지 않습니다.

src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java (1)

359-383: 스웨거 문서 새 에러 코드 매핑 누락 여부 확인

NOTIFICATION_MARK_TO_CHECKED에 추가한 에러 코드들이 실제 컨트롤러 응답 매핑과 일치하는지, 또 프론트 문서/응답 예시가 업데이트됐는지 확인해 주세요. 스웨거만 갱신되고 실제 컨트롤러가 다른 코드를 던지면 혼선이 생깁니다.

src/test/java/konkuk/thip/notification/application/service/NotificationSyncExecutorTest.java (1)

38-40: LGTM: 시그니처 변경 반영 및 카테고리 단언 보강

notificationId를 인자로 받는 퍼블리시 시그니처 반영과 FEED 카테고리 단언이 적절합니다.

Also applies to: 56-57

src/main/java/konkuk/thip/notification/adapter/in/web/response/NotificationMarkToCheckedResponse.java (1)

9-16: LGTM

단순·명확한 응답 DTO 설계로 직관적입니다.

src/main/java/konkuk/thip/notification/adapter/in/web/request/FcmTokenRegisterRequest.java (1)

26-27: toCommand 매핑은 적절

문자열 → enum 변환을 포트 커맨드에 위임하는 현재 방식은 깔끔합니다.

src/main/java/konkuk/thip/notification/domain/value/PlatformType.java (1)

17-24: 플랫폼 문자열 매핑 중앙화 👍

문자열 입력을 enum으로 모으는 from 도입 덕분에 잘못된 플랫폼 값은 공통 에러코드(INVALID_FE_PLATFORM)로 처리되어 프론트-백 정합성이 유지되는 점 확인했습니다.

src/main/java/konkuk/thip/notification/adapter/out/persistence/NotificationCommandPersistenceAdapter.java (1)

27-48: 생성된 notificationId 반환 구조 좋아요

save에서 즉시 생성된 notificationId를 돌려줘서 상위 오케스트레이터가 같은 트랜잭션 안에서 ID를 전달·활용할 수 있게 된 점, 그리고 findById/update 도입으로 마크 기능을 바로 구현할 수 있게 된 점 모두 확인했습니다.

src/main/java/konkuk/thip/message/application/service/RoomNotificationDispatchService.java (1)

72-94: 푸시 데이터 최소화 검증 완료

push 헬퍼로 메시지 생성 경로를 통일하고 notificationId만 전달하도록 한 변경이 새 redirect-spec 기반 설계와 잘 맞아떨어집니다. 카테고리/액션 값도 일관되게 세팅되어 있어 문제 없어 보입니다.

src/main/java/konkuk/thip/notification/application/service/RoomNotificationOrchestratorSyncImpl.java (1)

207-231: Jackson 기반 Converter가 enum을 안전하게 처리합니다
NotificationRedirectSpecConverter가 ObjectMapper를 사용해 enum을 name() 형태의 문자열로 직렬화/역직렬화하므로, postType을 enum 객체 그대로 전달해도 문제 없습니다.

src/test/java/konkuk/thip/notification/domain/NotificationTest.java (1)

15-41: 소유자/읽음 예외 흐름 커버 확인했습니다.
두 경계 상황에 대해 InvalidStateException이 기대대로 발생하는지 명확히 검증하고 있어 신뢰도가 높아졌습니다.

src/main/java/konkuk/thip/notification/application/port/in/NotificationMarkUseCase.java (1)

5-8: 포트 인터페이스 정리 깔끔합니다.
식별자 2개로 읽음 처리 유스케이스를 명확히 표현해 주셔서 구현부 연결이 더 분명해졌습니다.

src/main/java/konkuk/thip/notification/application/service/EventCommandInvoker.java (1)

6-6: notificationId 전달 확장은 타당해 보입니다.
퍼블리시 경로에 식별자를 실어 downstream 리다이렉트 컨텍스트와 맞출 수 있게 된 점 좋습니다.

src/main/java/konkuk/thip/notification/application/service/template/feed/FolloweeNewFeedTemplate.java (1)

6-25: 템플릿 명/타입 재정렬 👍
Feed 용어로 통일하면서 Args 타입도 깔끔하게 유지되어, 카테고리/콘텐츠 생성이 직관적입니다.

src/test/java/konkuk/thip/message/adapter/in/event/MessageFeedEventListenerTest.java (1)

35-51: Follower 이벤트 검증이 최신 페이로드와 일치합니다.
커밋 트리거 플로우까지 함께 체크해 주셔서 AFTER_COMMIT 리스너 동작을 안정적으로 보장하네요.

src/main/java/konkuk/thip/message/adapter/out/event/RoomEventPublisherAdapter.java (1)

15-25: 퍼블리시 어댑터 파라미터 단순화 LGTM

빌더 필드와 메서드 시그니처가 DTO와 정확히 정렬되어 있습니다.

src/main/java/konkuk/thip/feed/application/service/FeedCreateService.java (1)

74-75: 알림 오케스트레이터 API 변경 반영 LGTM

notifyFolloweeNewFeed로의 전환이 자연스럽고 시그니처 일치합니다.

src/test/java/konkuk/thip/notification/application/service/FeedNotificationOrchestratorSyncImplTest.java (1)

109-117: notificationId 전파 검증 LGTM

커밋 이후 이벤트의 notificationId와 영속된 엔티티의 PK 일치 검증이 명확합니다.

src/test/java/konkuk/thip/message/adapter/in/event/MessageRoomEventListenerTest.java (1)

34-38: 신규 스키마 적용 테스트 케이스 LGTM

타이틀/콘텐츠/notificationId/targetUserId 기반 이벤트 구성과 AFTER_COMMIT 흐름 검증이 일관됩니다.

src/main/java/konkuk/thip/notification/adapter/out/jpa/NotificationJpaEntity.java (1)

41-43: redirectSpec 매핑 추가 LGTM

@convert를 통한 TEXT 컬럼 매핑이 도메인 VO와 일치합니다. 마이그레이션과도 정합성 있습니다.

src/main/resources/db/migration/V250921__Add_notification_redirect_spec.sql (1)

1-3: 컬럼 COMMENT 문법 수정
ALTER TABLE notifications
ADD COLUMN redirect_spec TEXT NULL COMMENT 'NotificationRedirectSpec을 json 형식의 TEXT 로 저장';

테스트/로컬 DB가 H2인 경우 MySQL 호환 모드 적용 여부를 확인하세요. 필요 시 테스트 전용 마이그레이션 분기 또는 Dialect별 스크립트를 도입하세요.

src/main/java/konkuk/thip/message/adapter/out/event/dto/RoomEvents.java (1)

11-60: 레거시 필드(actorUserId 등) 수동 점검 필요
자동 탐지에서 결과가 없으니 actorUserId, actorUsername, roomId, roomTitle, page, postId, postType, ownerUserId, applicantUserId 빌더 사용 흔적을 직접 확인해 주세요.

Comment on lines 15 to 24
public record FeedCommentedEvent(
String title, String content,
Long targetUserId, Long actorUserId, String actorUsername,
Long feedId) {}
String title, String content, Long notificationId,
Long targetUserId) {}

// 누군가 내 댓글에 대댓글을 다는 경우
@Builder
public record FeedCommentRepliedEvent(
String title, String content,
Long targetUserId, Long actorUserId, String actorUsername,
Long feedId) {}
String title, String content, Long notificationId,
Long targetUserId) {}

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

이벤트 페이로드에서 액터 정보 제거에 대한 근거 필요

기존 이벤트는 actorUserId, actorUsername 등 행위자 정보를 포함하고 있었습니다. 변경 후 FE/수신 측에서 해당 정보를 얻을 수 있는 경로가 없다면, UI에 필요한 프로필 데이터가 사라져 회귀가 발생할 수 있습니다. 알림 상세 API를 통해 후속 조회가 가능하더라도, 푸시 알림 본문이나 목록에서 행위자명을 즉시 표시해야 한다면 현재 구조로는 충족되지 않습니다. 프런트/소비자들이 notificationId만으로 필요한 정보를 재구성 가능한지 확인이 필요합니다. 불가능하다면 이벤트에 최소한 행위자 식별자를 유지하거나, 상세 조회 API에서 동일 데이터를 제공해야 합니다.

행위자 정보 조회 경로가 확보되어 있는지 확인해 주시고, 없다면 이벤트 레코드에 필요한 필드를 유지해 주세요.


🏁 Script executed:

#!/bin/bash
# 기존 이벤트 내 actor 정보 참조 검색
rg -n 'actorUserId|actorUsername' -C3

# 이벤트 생성 위치 확인
rg -nP 'FeedCommentedEvent\.(builder|\()' -C3
rg -nP 'FeedCommentRepliedEvent\.(builder|\()' -C3

# 이벤트 소비자 측 매핑 로직 확인 (예: JSON 파싱)
rg -n 'FeedCommentedEvent' -C3
rg -n 'FeedCommentRepliedEvent' -C3

Length of output: 77090


FeedEvents.FeedCommentedEvent/FeedCommentRepliedEvent record에 actorUserId/actorUsername 복원 (dto/FeedEvents.java:15-24)
FE에서 프로필 조회를 위해 actorUserId가 필요합니다. 상세 조회 API로 대체 가능한지 검증하고, 불가능하면 이벤트 페이로드에 해당 필드를 추가하세요.

🤖 Prompt for AI Agents
In src/main/java/konkuk/thip/message/adapter/out/event/dto/FeedEvents.java
around lines 15 to 24, the FeedCommentedEvent and FeedCommentRepliedEvent
records were stripped of actorUserId/actorUsername but the frontend requires
actorUserId for profile lookup; restore the actorUserId (Long) and actorUsername
(String) fields to both records' parameter lists and update any
builders/constructors/usages so they supply these values (or validate they exist
from the detailed-lookup alternative if you confirm that instead); ensure
serialization/deserialization and any mapping code is updated to include these
fields.

Comment on lines +62 to 85
private void push(Long userId, Notification n, Long notificationId) {
List<FcmToken> tokens = fcmTokenPersistencePort.findEnabledByUserId(userId);

if (tokens.isEmpty()) return;

List<Message> msgs = new ArrayList<>(tokens.size());
List<String> tk = new ArrayList<>(tokens.size());
List<String> tk = new ArrayList<>(tokens.size());
List<String> dev = new ArrayList<>(tokens.size());

for (FcmToken t : tokens) {
Message m = buildMessage(t.getFcmToken(), notification,
MessageRoute.FEED_DETAIL,
"feedId", String.valueOf(feedId));
Message m = Message.builder()
.setToken(t.getFcmToken())
.setNotification(n)
.putData("category", NotificationCategory.FEED.getDisplay())
.putData("action", "OPEN_NOTIFICATION") // FE는 이 액션으로 알림 상세/라우팅을 BE api 요청으로 처리
.putData("notificationId", String.valueOf(notificationId))
.build();

msgs.add(m);
tk.add(t.getFcmToken());
dev.add(t.getDeviceId());
}

firebasePort.sendBatch(msgs, tk, dev);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Firebase 메시지 데이터 크기 초과 위험

push 루프에서 notificationIdString.valueOf(notificationId)로 넣고 있습니다. notificationIdnull일 경우 "null" 문자열이 전송되며, 이후 FE에서 Long 파싱 시 NPE/NumberFormatException이 발생할 수 있습니다. 실제로 saveNotification 실패/롤백 상황에서 publish가 호출되면 이런 null 가능성이 존재하므로 방어 로직이 필요합니다.

아래처럼 null을 사전에 필터링하거나 예외를 던져 주세요:

-        push(event.targetUserId(), n, event.notificationId());
+        push(event.targetUserId(), n, requireNonNull(event.notificationId(), "notificationId must not be null"));

그리고 push 내부에서도 String 변환 전에 null 검사를 수행하세요.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
src/main/java/konkuk/thip/message/application/service/FeedNotificationDispatchService.java
around lines 62-85, the code converts notificationId with
String.valueOf(notificationId) which will send the literal "null" if
notificationId is null and cause FE parsing errors; fix by validating
notificationId before sending: either (A) at the caller/publish site filter out
or throw when notificationId is null (ensure saveNotification failures don't
trigger publish), and (B) inside push add a null check at the start that either
throws an IllegalArgumentException or logs and returns if notificationId == null
to avoid building messages with "null"; ensure the code never calls
String.valueOf on a null id and that any validation error is clearly surfaced
(exception or log) before attempting to send.

Comment on lines +33 to +37
// 3. 읽음 처리된 알림의 redirectSpec 반환 (for FE 알림 리다이렉트 동작)
return new NotificationMarkToCheckedResponse(
notification.getRedirectSpec().route(),
notification.getRedirectSpec().params()
);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

redirectSpec null 시 NPE 위험 — 안전한 폴백 필요

기존 알림에 redirect_spec이 비어있을 수 있어 NPE로 500이 발생할 가능성이 큽니다(배포 즉시 사용자 영향).

아래처럼 폴백을 적용하세요(추가 import 없이 FQCN/Objects 사용):

-        return new NotificationMarkToCheckedResponse(
-                notification.getRedirectSpec().route(),
-                notification.getRedirectSpec().params()
-        );
+        final var spec = java.util.Objects.requireNonNullElseGet(
+                notification.getRedirectSpec(),
+                () -> konkuk.thip.notification.domain.value.NotificationRedirectSpec.none()
+        );
+        return new NotificationMarkToCheckedResponse(spec.route(), spec.params());

검증 권장:

  • 마이그레이션에서 기존 로우의 redirect_spec을 backfill했는지, NOT NULL/DEFAULT가 설정됐는지 확인
  • 새로 생성되는 알림이 항상 redirectSpec을 세팅하는지 확인(엔티티/매퍼/어댑터 경로)

다음 스크립트로 스키마/마이그레이션 흔적을 점검해 주세요:


🏁 Script executed:

#!/bin/bash
# redirect_spec 컬럼 정의 및 backfill 여부 탐색
fd -t f -e sql | xargs -I{} rg -n -C2 'redirect[_ ]?spec' '{}'
# 매퍼/어댑터에서 redirectSpec 매핑 경로 확인
rg -n -C2 --type=java 'redirectSpec' src | sed -n '1,200p'

Length of output: 24353


redirectSpec null 시 NPE 위험 – Fallback 적용 필수
NotificationMarkService.markChecked에서 notification.getRedirectSpec()가 null인 경우 500 에러 발생. 다음과 같이 수정하세요:

- return new NotificationMarkToCheckedResponse(
-     notification.getRedirectSpec().route(),
-     notification.getRedirectSpec().params()
- );
+ final var spec = java.util.Objects.requireNonNullElseGet(
+     notification.getRedirectSpec(),
+     () -> konkuk.thip.notification.domain.value.NotificationRedirectSpec.none()
+ );
+ return new NotificationMarkToCheckedResponse(spec.route(), spec.params());

검증:

  • DB 마이그레이션에서 기존 redirect_spec 백필(backfill) 또는 NOT NULL/DEFAULT 설정 여부
  • 알림 생성(엔티티·매퍼·어댑터) 로직이 항상 redirectSpec을 세팅하는지
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// 3. 읽음 처리된 알림의 redirectSpec 반환 (for FE 알림 리다이렉트 동작)
return new NotificationMarkToCheckedResponse(
notification.getRedirectSpec().route(),
notification.getRedirectSpec().params()
);
// 3. 읽음 처리된 알림의 redirectSpec 반환 (for FE 알림 리다이렉트 동작)
final var spec = java.util.Objects.requireNonNullElseGet(
notification.getRedirectSpec(),
() -> konkuk.thip.notification.domain.value.NotificationRedirectSpec.none()
);
return new NotificationMarkToCheckedResponse(
spec.route(),
spec.params()
);

Comment on lines +31 to 41
public static Notification withoutId(String title, String content, NotificationCategory notificationCategory, Long targetUserId,
NotificationRedirectSpec redirectSpec) {
return Notification.builder()
.title(title)
.content(content)
.isChecked(false)
.notificationCategory(notificationCategory)
.targetUserId(targetUserId)
.redirectSpec(redirectSpec)
.build();
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

redirectSpec 기본값 보장으로 도메인 불변식 강화

도메인에서 null을 허용하면 서비스/컨트롤러 전반에 NPE 위험이 전파됩니다. none() 폴백을 적용해 도메인 레벨에서 불변식을 보장하세요.

-    public static Notification withoutId(String title, String content, NotificationCategory notificationCategory, Long targetUserId,
-                                          NotificationRedirectSpec redirectSpec) {
+    public static Notification withoutId(String title, String content, NotificationCategory notificationCategory, Long targetUserId,
+                                          NotificationRedirectSpec redirectSpec) {
         return Notification.builder()
                 .title(title)
                 .content(content)
                 .isChecked(false)
                 .notificationCategory(notificationCategory)
                 .targetUserId(targetUserId)
-                .redirectSpec(redirectSpec)
+                .redirectSpec(redirectSpec != null ? redirectSpec : NotificationRedirectSpec.none())
                 .build();
     }

추가(선택): 안전 접근자 제공

// 클래스 내부에 추가
public NotificationRedirectSpec redirectSpecOrDefault() {
    return redirectSpec != null ? redirectSpec : NotificationRedirectSpec.none();
}
🤖 Prompt for AI Agents
In src/main/java/konkuk/thip/notification/domain/Notification.java around lines
31 to 41, the factory method allows redirectSpec to be null which propagates NPE
risk; update the withoutId method to apply a non-null default
(NotificationRedirectSpec.none()) when redirectSpec is null so the built
Notification always has a valid redirectSpec, and add a safe accessor method
redirectSpecOrDefault() that returns redirectSpec if non-null or
NotificationRedirectSpec.none() otherwise.

Comment on lines +115 to +124
// when & then: 다시 읽음 처리 시도 → 400
String body = objectMapper.writeValueAsString(Map.of("notificationId", notification.getNotificationId()));
mockMvc.perform(post("/notifications/check")
.contentType(APPLICATION_JSON)
.content(body)
.requestAttr("userId", owner.getUserId()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.route").value(MessageRoute.FEED_USER.name()))
.andExpect(jsonPath("$.data.params.userId").value(123));
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

주석 불일치(400 → 200 OK) 정정 필요

이미 읽음 알림도 OK를 기대하는데, 주석엔 400으로 표기되어 있습니다. 혼동을 유발하므로 주석을 200으로 수정하세요.

-        // when & then: 다시 읽음 처리 시도 → 400
+        // when & then: 다시 읽음 처리 시도 → 200(OK) + 리다이렉트 데이터 반환
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// when & then: 다시 읽음 처리 시도 → 400
String body = objectMapper.writeValueAsString(Map.of("notificationId", notification.getNotificationId()));
mockMvc.perform(post("/notifications/check")
.contentType(APPLICATION_JSON)
.content(body)
.requestAttr("userId", owner.getUserId()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.route").value(MessageRoute.FEED_USER.name()))
.andExpect(jsonPath("$.data.params.userId").value(123));
}
// when & then: 다시 읽음 처리 시도 → 200(OK) + 리다이렉트 데이터 반환
String body = objectMapper.writeValueAsString(Map.of("notificationId", notification.getNotificationId()));
mockMvc.perform(post("/notifications/check")
.contentType(APPLICATION_JSON)
.content(body)
.requestAttr("userId", owner.getUserId()))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.route").value(MessageRoute.FEED_USER.name()))
.andExpect(jsonPath("$.data.params.userId").value(123));
}
🤖 Prompt for AI Agents
In
src/test/java/konkuk/thip/notification/adapter/in/web/NotificationMarkToCheckedApiTest.java
around lines 115 to 124, the inline comment says the expected response is 400
but the test assertions expect 200 OK; update the comment to reflect 200 OK to
match the assertions and avoid confusion (change "→ 400" to "→ 200 OK" or
similar).

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.

2 participants