From 6cb40fe65c9beb9c538899815e3d5c0f3c5fac3b Mon Sep 17 00:00:00 2001 From: seongjunnoh Date: Wed, 24 Sep 2025 18:01:01 +0900 Subject: [PATCH] =?UTF-8?q?[fix]=20=EC=95=8C=EB=A6=BC=20=EC=9D=BD=EC=9D=8C?= =?UTF-8?q?=20=EC=B2=98=EB=A6=AC=20api=20=EB=B9=84=EC=A6=88=EB=8B=88?= =?UTF-8?q?=EC=8A=A4=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95=20(#312)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 이미 읽음 처리된 알림에 대한 요청을 받더라도 정상 응답 하도록 수정 --- .../swagger/SwaggerResponseDescription.java | 3 +-- .../in/web/NotificationCommandController.java | 3 ++- .../service/NotificationMarkService.java | 11 ++++++++--- .../in/web/NotificationMarkToCheckedApiTest.java | 16 +++++++++------- 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java b/src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java index 37ac64015..648c7d004 100644 --- a/src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java +++ b/src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java @@ -379,8 +379,7 @@ public enum SwaggerResponseDescription { ))), NOTIFICATION_MARK_TO_CHECKED(new LinkedHashSet<>(Set.of( NOTIFICATION_NOT_FOUND, - NOTIFICATION_ACCESS_FORBIDDEN, - NOTIFICATION_ALREADY_CHECKED + NOTIFICATION_ACCESS_FORBIDDEN ))), ; diff --git a/src/main/java/konkuk/thip/notification/adapter/in/web/NotificationCommandController.java b/src/main/java/konkuk/thip/notification/adapter/in/web/NotificationCommandController.java index a2a7ae231..145706b58 100644 --- a/src/main/java/konkuk/thip/notification/adapter/in/web/NotificationCommandController.java +++ b/src/main/java/konkuk/thip/notification/adapter/in/web/NotificationCommandController.java @@ -67,7 +67,8 @@ public BaseResponse deleteFcmToken( @Operation( summary = "유저의 특정 알림 읽음 처리", - description = "유저가 특정 알림을 읽음 처리합니다 (푸시알림, 알림센터의 알림 모두 포함). 읽음 처리 후, 해당 알림의 페이지로 리다이렉트를 위한 데이터를 응답합니다." + description = "유저가 특정 알림을 읽음 처리합니다 (푸시알림, 알림센터의 알림 모두 포함). 읽음 처리 후, 해당 알림의 페이지로 리다이렉트를 위한 데이터를 응답합니다. " + + "이미 읽음처리가 된 알림에 대해서는 리다이렉트를 위한 데이터만 응답합니다." ) @ExceptionDescription(NOTIFICATION_MARK_TO_CHECKED) @PostMapping("/notifications/check") diff --git a/src/main/java/konkuk/thip/notification/application/service/NotificationMarkService.java b/src/main/java/konkuk/thip/notification/application/service/NotificationMarkService.java index e4d523861..ddff7d7b4 100644 --- a/src/main/java/konkuk/thip/notification/application/service/NotificationMarkService.java +++ b/src/main/java/konkuk/thip/notification/application/service/NotificationMarkService.java @@ -1,5 +1,6 @@ package konkuk.thip.notification.application.service; +import konkuk.thip.common.exception.InvalidStateException; import konkuk.thip.notification.adapter.in.web.response.NotificationMarkToCheckedResponse; import konkuk.thip.notification.application.port.in.NotificationMarkUseCase; import konkuk.thip.notification.application.port.out.NotificationCommandPort; @@ -22,9 +23,13 @@ public NotificationMarkToCheckedResponse markToChecked(Long notificationId, Long notification.validateOwner(userId); // 2. 알림 읽음 처리 - notification.markToChecked(); - notificationCommandPort.update(notification); - + try { + notification.markToChecked(); + notificationCommandPort.update(notification); + } catch (InvalidStateException e) { + // 이미 알림 읽음 처리된 경우 -> 무시 + } + // 3. 읽음 처리된 알림의 redirectSpec 반환 (for FE 알림 리다이렉트 동작) return new NotificationMarkToCheckedResponse( notification.getRedirectSpec().route(), diff --git a/src/test/java/konkuk/thip/notification/adapter/in/web/NotificationMarkToCheckedApiTest.java b/src/test/java/konkuk/thip/notification/adapter/in/web/NotificationMarkToCheckedApiTest.java index 65ddfcb83..c09ddcf6d 100644 --- a/src/test/java/konkuk/thip/notification/adapter/in/web/NotificationMarkToCheckedApiTest.java +++ b/src/test/java/konkuk/thip/notification/adapter/in/web/NotificationMarkToCheckedApiTest.java @@ -23,9 +23,7 @@ import java.util.Map; -import static konkuk.thip.common.exception.code.ErrorCode.NOTIFICATION_ALREADY_CHECKED; import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.Matchers.containsString; import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -94,13 +92,17 @@ void mark_notification_to_checked_forbidden_when_not_owner() throws Exception { } @Test - @DisplayName("이미 읽음 처리된 알림을 다시 읽음 처리하면, 400 에러를 반환한다.") + @DisplayName("이미 읽음 처리된 알림을 다시 읽음 처리하더라도, 에러를 반환하지 않고 알림의 리다이렉트를 위한 데이터를 반환한다.") void mark_notification_to_checked_already_checked() throws Exception { // given: owner의 알림을 미리 is_checked=true 상태로 만들어 둔다 UserJpaEntity owner = userJpaRepository.save(TestEntityFactory.createUser(Alias.WRITER)); + NotificationRedirectSpec redirectSpec = TestEntityFactory.createNotificationRedirectSpec( + MessageRoute.FEED_USER, Map.of("userId", 123L) // 특정 유저의 피드 페이지로 이동 + ); + NotificationJpaEntity notification = notificationJpaRepository.save( - TestEntityFactory.createNotification(owner, "이미 읽은 알림", NotificationCategory.FEED)); + TestEntityFactory.createNotification(owner, "이미 읽은 알림", NotificationCategory.FEED, redirectSpec)); // is_checked=true 로 강제 세팅 jdbcTemplate.update( @@ -116,8 +118,8 @@ void mark_notification_to_checked_already_checked() throws Exception { .contentType(APPLICATION_JSON) .content(body) .requestAttr("userId", owner.getUserId())) - .andExpect(status().isBadRequest()) - .andExpect(jsonPath("$.code").value(NOTIFICATION_ALREADY_CHECKED.getCode())) - .andExpect(jsonPath("$.message", containsString(NOTIFICATION_ALREADY_CHECKED.getMessage()))); + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.route").value(MessageRoute.FEED_USER.name())) + .andExpect(jsonPath("$.data.params.userId").value(123)); } }