From 61cca0ec8af4856530e20904c63533d1bfb7ca4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=98=81?= Date: Fri, 14 Aug 2026 16:56:38 +0900 Subject: [PATCH 1/8] =?UTF-8?q?:sparkles:=20feat:=20=ED=9A=8C=EC=9B=90=20?= =?UTF-8?q?=ED=83=88=ED=87=B4=20=EC=8B=9C=20=EA=B4=80=EB=A0=A8=EB=90=9C=20?= =?UTF-8?q?=EC=95=8C=EB=A6=BC=20=EA=B0=9D=EC=B2=B4=20=EC=9E=90=EB=8F=99=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=EB=A5=BC=20=EC=9C=84=ED=95=9C=20OnDelete=20?= =?UTF-8?q?=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domains/notification/persistence/entity/Notification.java | 3 +++ .../notification/persistence/entity/NotificationDelivery.java | 1 + .../persistence/entity/OrgNotificationSetting.java | 3 +++ .../notification/persistence/entity/UserNotification.java | 4 ++++ 4 files changed, 11 insertions(+) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/Notification.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/Notification.java index 33afbfe8..17d06226 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/Notification.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/Notification.java @@ -4,6 +4,8 @@ import com.whereyouad.WhereYouAd.domains.organization.persistence.entity.Organization; import jakarta.persistence.*; import lombok.*; +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; @@ -42,6 +44,7 @@ public class Notification { // 연관 관계 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "org_id", nullable = false) + @OnDelete(action = OnDeleteAction.CASCADE) private Organization organization; @Enumerated(EnumType.STRING) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/NotificationDelivery.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/NotificationDelivery.java index dcfbd545..f183622d 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/NotificationDelivery.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/NotificationDelivery.java @@ -48,6 +48,7 @@ public class NotificationDelivery extends BaseEntity { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "notification_id", nullable = false) + @OnDelete(action = OnDeleteAction.CASCADE) private Notification notification; @ManyToOne(fetch = FetchType.LAZY) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/OrgNotificationSetting.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/OrgNotificationSetting.java index 63a7adf2..87ba7e74 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/OrgNotificationSetting.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/OrgNotificationSetting.java @@ -5,6 +5,8 @@ import jakarta.persistence.*; import lombok.*; import org.hibernate.annotations.ColumnDefault; +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; import org.springframework.util.StringUtils; @Entity @@ -22,6 +24,7 @@ public class OrgNotificationSetting extends BaseEntity { @OneToOne(fetch = FetchType.LAZY) @MapsId // PK == FK(식별 관계) @JoinColumn(name = "org_id") + @OnDelete(action = OnDeleteAction.CASCADE) private Organization organization; @Column(name = "slack_webhook_url", length = 512) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/UserNotification.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/UserNotification.java index 96b1701d..63a4cdcd 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/UserNotification.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/UserNotification.java @@ -4,6 +4,8 @@ import jakarta.persistence.*; import lombok.*; import org.hibernate.annotations.ColumnDefault; +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; import java.time.LocalDateTime; @@ -30,10 +32,12 @@ public class UserNotification { // 연관관계 @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "notification_id", nullable = false) + @OnDelete(action = OnDeleteAction.CASCADE) private Notification notification; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id", nullable = false) + @OnDelete(action = OnDeleteAction.CASCADE) private User user; // 알림 읽음 처리 From 316cc37ba616e611d936c05938e38fa6ae255ba5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=98=81?= Date: Fri, 14 Aug 2026 16:57:01 +0900 Subject: [PATCH 2/8] =?UTF-8?q?:sparkles:=20feat:=20=EC=98=A4=EB=9E=98?= =?UTF-8?q?=EB=90=9C=20=EC=95=8C=EB=A6=BC=20=EC=82=AD=EC=A0=9C=20=EC=8A=A4?= =?UTF-8?q?=EC=BC=80=EC=A4=84=EB=9F=AC=EB=A5=BC=20=EC=9C=84=ED=95=9C=20Rep?= =?UTF-8?q?ository=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/NotificationDeliveryRepository.java | 9 +++++++++ .../persistence/repository/NotificationRepository.java | 10 ++++++++++ .../repository/UserNotificationRepository.java | 5 +++++ 3 files changed, 24 insertions(+) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/NotificationDeliveryRepository.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/NotificationDeliveryRepository.java index 0db71843..4a05e798 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/NotificationDeliveryRepository.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/NotificationDeliveryRepository.java @@ -2,8 +2,17 @@ import com.whereyouad.WhereYouAd.domains.notification.persistence.entity.NotificationDelivery; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; public interface NotificationDeliveryRepository extends JpaRepository { + // 만료 알림 정리용 - 발송 이력 벌크 삭제 + @Modifying + @Query("DELETE FROM NotificationDelivery nd WHERE nd.notification.id IN :notificationIds") + int deleteByNotificationIdIn(@Param("notificationIds") List notificationIds); } diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/NotificationRepository.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/NotificationRepository.java index 20aa94fe..638d2300 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/NotificationRepository.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/NotificationRepository.java @@ -1,7 +1,17 @@ package com.whereyouad.WhereYouAd.domains.notification.persistence.repository; import com.whereyouad.WhereYouAd.domains.notification.persistence.entity.Notification; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.time.LocalDateTime; +import java.util.List; public interface NotificationRepository extends JpaRepository { + + // 보관 기간이 지난 알림 id 조회 (청크 단위로 끊어서 조회) + @Query("SELECT n.id FROM Notification n WHERE n.createdAt < :threshold ORDER BY n.id") + List findIdsByCreatedAtBefore(@Param("threshold") LocalDateTime threshold, Pageable pageable); } diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/UserNotificationRepository.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/UserNotificationRepository.java index dfeaa872..2032ff28 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/UserNotificationRepository.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/UserNotificationRepository.java @@ -9,6 +9,7 @@ import org.springframework.data.repository.query.Param; import java.time.LocalDateTime; +import java.util.List; import java.util.Optional; public interface UserNotificationRepository extends JpaRepository { @@ -56,4 +57,8 @@ int markAllAsRead(@Param("userId") Long userId, @Param("orgId") Long orgId, @Param("now") LocalDateTime now); + // 만료 알림 정리용 - 회원별 알림 내역 벌크 삭제 + @Modifying + @Query("DELETE FROM UserNotification un WHERE un.notification.id IN :notificationIds") + int deleteByNotificationIdIn(@Param("notificationIds") List notificationIds); } From 8c20199db41d83e8b866a70a886c21b87933322a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=98=81?= Date: Fri, 14 Aug 2026 16:57:24 +0900 Subject: [PATCH 3/8] =?UTF-8?q?:sparkles:=20feat:=20=EC=98=A4=EB=9E=98?= =?UTF-8?q?=EB=90=9C=20=EC=95=8C=EB=A6=BC=20=EC=82=AD=EC=A0=9C=20=EB=B0=B0?= =?UTF-8?q?=EC=B9=98=20Executor=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NotificationCleanupExecutor.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/service/scheduler/NotificationCleanupExecutor.java diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/service/scheduler/NotificationCleanupExecutor.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/service/scheduler/NotificationCleanupExecutor.java new file mode 100644 index 00000000..3683d321 --- /dev/null +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/service/scheduler/NotificationCleanupExecutor.java @@ -0,0 +1,31 @@ +package com.whereyouad.WhereYouAd.domains.notification.domain.service.scheduler; + +import com.whereyouad.WhereYouAd.domains.notification.persistence.repository.NotificationDeliveryRepository; +import com.whereyouad.WhereYouAd.domains.notification.persistence.repository.NotificationRepository; +import com.whereyouad.WhereYouAd.domains.notification.persistence.repository.UserNotificationRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Slf4j +@Component +@RequiredArgsConstructor +public class NotificationCleanupExecutor { + + private final NotificationRepository notificationRepository; + private final UserNotificationRepository userNotificationRepository; + private final NotificationDeliveryRepository notificationDeliveryRepository; + + @Transactional + public void deleteNotificationChunk(List notificationIds) { + int userNotificationCount = userNotificationRepository.deleteByNotificationIdIn(notificationIds); + int deliveryCount = notificationDeliveryRepository.deleteByNotificationIdIn(notificationIds); + notificationRepository.deleteAllByIdInBatch(notificationIds); + + log.debug("만료 알림 청크 삭제 - 알림(Notification): {}건, 회원 알림 내역(UserNotification): {}건, 발송 이력(NotificationDelivery): {}건", + notificationIds.size(), userNotificationCount, deliveryCount); + } +} From 212c7fa8eedd5ca4c5a254aea0319c9937a3a573 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=98=81?= Date: Fri, 14 Aug 2026 16:57:33 +0900 Subject: [PATCH 4/8] =?UTF-8?q?:sparkles:=20feat:=20=EC=98=A4=EB=9E=98?= =?UTF-8?q?=EB=90=9C=20=EC=95=8C=EB=A6=BC=20=EC=82=AD=EC=A0=9C=20=EC=8A=A4?= =?UTF-8?q?=EC=BC=80=EC=A4=84=EB=9F=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NotificationCleanupScheduler.java | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/service/scheduler/NotificationCleanupScheduler.java diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/service/scheduler/NotificationCleanupScheduler.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/service/scheduler/NotificationCleanupScheduler.java new file mode 100644 index 00000000..78e2bdd7 --- /dev/null +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/service/scheduler/NotificationCleanupScheduler.java @@ -0,0 +1,44 @@ +package com.whereyouad.WhereYouAd.domains.notification.domain.service.scheduler; + +import com.whereyouad.WhereYouAd.domains.notification.persistence.repository.NotificationRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.PageRequest; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.util.List; + +@Slf4j +@Component +@RequiredArgsConstructor +public class NotificationCleanupScheduler { + + private static final int RETENTION_DAYS = 30; + private static final int CHUNK_SIZE = 1000; + + private final NotificationRepository notificationRepository; + private final NotificationCleanupExecutor notificationCleanupExecutor; + + @Scheduled(cron = "0 0 5 * * *", zone = "Asia/Seoul") + public void deleteOldNotifications() { + LocalDateTime threshold = LocalDateTime.now().minusDays(RETENTION_DAYS); + log.info("오래된 알림 내역 삭제 스케줄러 실행 - threshold: {}", threshold); + + int totalDeleted = 0; + while (true) { + List notificationIds = + notificationRepository.findIdsByCreatedAtBefore(threshold, PageRequest.of(0, CHUNK_SIZE)); + + if (notificationIds.isEmpty()) { + break; + } + + notificationCleanupExecutor.deleteNotificationChunk(notificationIds); + totalDeleted += notificationIds.size(); + } + + log.info("오래된 알림 내역 삭제 완료 - 삭제 알림 갯수: {}", totalDeleted); + } +} From 47af00cbff4c03ab4956fa67dd5d9c21f3e2cc73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=98=81?= Date: Tue, 18 Aug 2026 20:48:57 +0900 Subject: [PATCH 5/8] =?UTF-8?q?:art:=20style:=20=EC=98=A4=EB=9E=98?= =?UTF-8?q?=EB=90=9C=20=EC=95=8C=EB=A6=BC=20=EB=82=B4=EC=97=AD=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EC=8A=A4=EC=BC=80=EC=A4=84=EB=9F=AC=20=EC=A3=BC?= =?UTF-8?q?=EC=84=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/scheduler/NotificationCleanupScheduler.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/service/scheduler/NotificationCleanupScheduler.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/service/scheduler/NotificationCleanupScheduler.java index 78e2bdd7..1bd12530 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/service/scheduler/NotificationCleanupScheduler.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/service/scheduler/NotificationCleanupScheduler.java @@ -23,18 +23,22 @@ public class NotificationCleanupScheduler { @Scheduled(cron = "0 0 5 * * *", zone = "Asia/Seoul") public void deleteOldNotifications() { + // 스케줄러 실행 시점으로 부터 만료 기준일 (30일 과거) 설정 LocalDateTime threshold = LocalDateTime.now().minusDays(RETENTION_DAYS); log.info("오래된 알림 내역 삭제 스케줄러 실행 - threshold: {}", threshold); int totalDeleted = 0; while (true) { + // 삭제 대상인 알림 내역 (30일 이상 지난 내역) Id 를 List 조회 List notificationIds = notificationRepository.findIdsByCreatedAtBefore(threshold, PageRequest.of(0, CHUNK_SIZE)); + // 삭제 대상인 알림 내역이 없으면 종료 if (notificationIds.isEmpty()) { break; } + // 청크 단위 알림 내역 삭제 notificationCleanupExecutor.deleteNotificationChunk(notificationIds); totalDeleted += notificationIds.size(); } From 54785699abb4bda61aefe3f30c1a40e5a6a26c28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=98=81?= Date: Wed, 19 Aug 2026 15:21:26 +0900 Subject: [PATCH 6/8] =?UTF-8?q?:recycle:=20refactor:=20=EC=98=A4=EB=9E=98?= =?UTF-8?q?=EB=90=9C=20=EC=95=8C=EB=A6=BC=20=EB=82=B4=EC=97=AD=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EC=8A=A4=EC=BC=80=EC=A4=84=EB=9F=AC=20=EB=82=B4?= =?UTF-8?q?=EB=B6=80=20=EC=82=AD=EC=A0=9C=20=EB=82=A0=EC=A7=9C=20=EA=B8=B0?= =?UTF-8?q?=EC=A4=80=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/service/scheduler/NotificationCleanupScheduler.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/service/scheduler/NotificationCleanupScheduler.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/service/scheduler/NotificationCleanupScheduler.java index 1bd12530..0d62b86e 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/service/scheduler/NotificationCleanupScheduler.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/service/scheduler/NotificationCleanupScheduler.java @@ -7,6 +7,7 @@ import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; +import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; @@ -24,7 +25,7 @@ public class NotificationCleanupScheduler { @Scheduled(cron = "0 0 5 * * *", zone = "Asia/Seoul") public void deleteOldNotifications() { // 스케줄러 실행 시점으로 부터 만료 기준일 (30일 과거) 설정 - LocalDateTime threshold = LocalDateTime.now().minusDays(RETENTION_DAYS); + LocalDateTime threshold = LocalDate.now().minusDays(RETENTION_DAYS).atStartOfDay(); log.info("오래된 알림 내역 삭제 스케줄러 실행 - threshold: {}", threshold); int totalDeleted = 0; From e865a772f013054c70fa591f532a069f472cf982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=98=81?= Date: Fri, 21 Aug 2026 00:35:02 +0900 Subject: [PATCH 7/8] =?UTF-8?q?:recycle:=20refactor:=20=EC=98=A4=EB=9E=98?= =?UTF-8?q?=EB=90=9C=20=EC=95=8C=EB=A6=BC=20=EB=82=B4=EC=97=AD=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EC=8A=A4=EC=BC=80=EC=A4=84=EB=9F=AC=20=EB=B0=A4=20?= =?UTF-8?q?12=EC=8B=9C=20=EC=8B=A4=ED=96=89=EC=9C=BC=EB=A1=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20+=20ZoneId=20=EB=AA=85=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/scheduler/NotificationCleanupScheduler.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/service/scheduler/NotificationCleanupScheduler.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/service/scheduler/NotificationCleanupScheduler.java index 0d62b86e..cb5a870d 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/service/scheduler/NotificationCleanupScheduler.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/domain/service/scheduler/NotificationCleanupScheduler.java @@ -9,6 +9,7 @@ import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.List; @Slf4j @@ -22,10 +23,10 @@ public class NotificationCleanupScheduler { private final NotificationRepository notificationRepository; private final NotificationCleanupExecutor notificationCleanupExecutor; - @Scheduled(cron = "0 0 5 * * *", zone = "Asia/Seoul") + @Scheduled(cron = "0 0 0 * * *", zone = "Asia/Seoul") public void deleteOldNotifications() { // 스케줄러 실행 시점으로 부터 만료 기준일 (30일 과거) 설정 - LocalDateTime threshold = LocalDate.now().minusDays(RETENTION_DAYS).atStartOfDay(); + LocalDateTime threshold = LocalDate.now(ZoneId.of("Asia/Seoul")).minusDays(RETENTION_DAYS).atStartOfDay(); log.info("오래된 알림 내역 삭제 스케줄러 실행 - threshold: {}", threshold); int totalDeleted = 0; From 9b6206dc0c96d8c4e6f954bd6ea3001681231cad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=98=81?= Date: Fri, 21 Aug 2026 17:58:08 +0900 Subject: [PATCH 8/8] =?UTF-8?q?:recycle:=20refactor:=20Notification=20?= =?UTF-8?q?=EC=97=94=ED=8B=B0=ED=8B=B0=EC=97=90=20=EC=9D=B8=EB=8D=B1?= =?UTF-8?q?=EC=8A=A4=20=EC=A0=81=EC=9A=A9=20&=20Repository=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EC=A0=95=EB=A0=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notification/persistence/entity/Notification.java | 5 ++++- .../persistence/repository/NotificationRepository.java | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/Notification.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/Notification.java index 17d06226..6fe0bc34 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/Notification.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/entity/Notification.java @@ -12,7 +12,10 @@ import java.time.LocalDateTime; @Entity -@Table(name = "notification") +@Table(name = "notification", indexes = { + // 오래된 알림 삭제 스케줄러의 청크 조회용 인덱스 + @Index(name = "idx_notification_created_at_id", columnList = "created_at, notification_id") +}) @EntityListeners(AuditingEntityListener.class) @Getter @Builder diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/NotificationRepository.java b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/NotificationRepository.java index 638d2300..ac4ad439 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/NotificationRepository.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/notification/persistence/repository/NotificationRepository.java @@ -12,6 +12,6 @@ public interface NotificationRepository extends JpaRepository { // 보관 기간이 지난 알림 id 조회 (청크 단위로 끊어서 조회) - @Query("SELECT n.id FROM Notification n WHERE n.createdAt < :threshold ORDER BY n.id") + @Query("SELECT n.id FROM Notification n WHERE n.createdAt < :threshold ORDER BY n.createdAt, n.id") List findIdsByCreatedAtBefore(@Param("threshold") LocalDateTime threshold, Pageable pageable); }