From 5a8b7d8c83f00f589d5caadd3249a80ad48d0a9b Mon Sep 17 00:00:00 2001 From: khj011219 Date: Sat, 31 Oct 2026 02:40:22 +0900 Subject: [PATCH 1/5] =?UTF-8?q?fix:=20=EB=B0=B0=ED=8F=AC=20=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A6=BD=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/deploy.sh | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/scripts/deploy.sh b/scripts/deploy.sh index ef49cb9..0a54c87 100644 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -101,6 +101,23 @@ echo "Step 4: Removing old container ($OLD)..." docker compose -f docker-compose.yml stop $OLD || true docker compose -f docker-compose.yml rm -f $OLD || true +echo "Step 5: Cleaning up old application images..." + +PREVIOUS_IMAGE="" +if [ -f .last-deployed-tag ]; then + PREVIOUS_IMAGE=$(cat .last-deployed-tag) +fi + +APP_REPOSITORY="${APP_IMAGE%:*}" + +docker images "$APP_REPOSITORY" --format '{{.Repository}}:{{.Tag}}' | +while read -r IMAGE; do + if [ "$IMAGE" != "$APP_IMAGE" ] && + [ "$IMAGE" != "$PREVIOUS_IMAGE" ]; then + docker image rm "$IMAGE" || true + fi +done + docker image prune -f echo "$APP_IMAGE" > .last-deployed-tag From 2904221ded30d78c34b68ad86612ef0cbbd993de Mon Sep 17 00:00:00 2001 From: khj011219 Date: Sat, 31 Oct 2026 03:07:23 +0900 Subject: [PATCH 2/5] =?UTF-8?q?fix:=20=EB=A1=9C=EA=B7=B8=20=EB=A1=9C?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=84=A4=EC=A0=95=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 --- docker-compose.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 5a17168..c2b83a5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,3 +1,9 @@ +x-logging: &default-logging + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + services: nginx: image: nginx:stable-alpine @@ -15,6 +21,7 @@ services: image: ${APP_IMAGE} container_name: routee-app-blue restart: unless-stopped + logging: *default-logging expose: - "8080" environment: @@ -51,6 +58,7 @@ services: image: ${APP_IMAGE} container_name: routee-app-green restart: unless-stopped + logging: *default-logging expose: - "8080" environment: @@ -116,6 +124,7 @@ services: image: grafana/tempo:latest container_name: routee-tempo restart: unless-stopped + logging: *default-logging user: "0" command: [ "-config.file=/etc/tempo.yaml" ] volumes: From 1f09308e7a18366250eb832cc7f7d4946299b28a Mon Sep 17 00:00:00 2001 From: kymin Date: Wed, 4 Nov 2026 14:10:44 +0900 Subject: [PATCH 3/5] =?UTF-8?q?feat:=20=EC=B4=9D=20=ED=99=9C=EB=8F=99=20?= =?UTF-8?q?=ED=9A=9F=EC=88=98=20=EA=B0=90=EC=86=8C=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../routee/member/internal/repository/MemberRepository.java | 4 ++++ .../sopt/routee/member/internal/service/MemberService.java | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/routee-member/src/main/java/org/sopt/routee/member/internal/repository/MemberRepository.java b/routee-member/src/main/java/org/sopt/routee/member/internal/repository/MemberRepository.java index c2e6b20..a1b6060 100644 --- a/routee-member/src/main/java/org/sopt/routee/member/internal/repository/MemberRepository.java +++ b/routee-member/src/main/java/org/sopt/routee/member/internal/repository/MemberRepository.java @@ -19,4 +19,8 @@ public interface MemberRepository extends JpaRepository { @Query("UPDATE Member m SET m.totalActivityCount = m.totalActivityCount + 1 WHERE m.id = :memberId") void incrementTotalActivityCount(@Param("memberId") Long memberId); + @Modifying + @Query("UPDATE Member m SET m.totalActivityCount = m.totalActivityCount - 1 WHERE m.id = :memberId AND m.totalActivityCount > 0") + void decrementTotalActivityCount(@Param("memberId") Long memberId); + } diff --git a/routee-member/src/main/java/org/sopt/routee/member/internal/service/MemberService.java b/routee-member/src/main/java/org/sopt/routee/member/internal/service/MemberService.java index 75f9113..263352d 100644 --- a/routee-member/src/main/java/org/sopt/routee/member/internal/service/MemberService.java +++ b/routee-member/src/main/java/org/sopt/routee/member/internal/service/MemberService.java @@ -295,6 +295,11 @@ public void incrementTotalActivityCount(long memberId) { memberRepository.incrementTotalActivityCount(memberId); } + @Transactional + public void decrementTotalActivityCount(long memberId) { + memberRepository.decrementTotalActivityCount(memberId); + } + @Transactional(readOnly = true) public ActivitySummaryResult getActivitySummary(long memberId, int year, int month) { if (!memberRepository.existsById(memberId)) { From c5aaa8d205c13ba02e8ba55a32b0ea24efd8420d Mon Sep 17 00:00:00 2001 From: kymin Date: Wed, 4 Nov 2026 15:07:58 +0900 Subject: [PATCH 4/5] =?UTF-8?q?feat:=20=ED=99=9C=EB=8F=99=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EC=8B=9C=20=EC=B4=9D=20=ED=99=9C=EB=8F=99=20?= =?UTF-8?q?=ED=9A=9F=EC=88=98=20=EA=B0=90=EC=86=8C=20=EC=9D=B4=EB=B2=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B2=98=EB=A6=AC=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../routee/activity/api/event/ActivityDeletedEvent.java | 6 ++++++ .../routee/activity/internal/service/ActivityService.java | 2 ++ .../internal/listener/MemberActivityEventListener.java | 6 ++++++ 3 files changed, 14 insertions(+) create mode 100644 routee-activity/src/main/java/org/sopt/routee/activity/api/event/ActivityDeletedEvent.java diff --git a/routee-activity/src/main/java/org/sopt/routee/activity/api/event/ActivityDeletedEvent.java b/routee-activity/src/main/java/org/sopt/routee/activity/api/event/ActivityDeletedEvent.java new file mode 100644 index 0000000..1e2803b --- /dev/null +++ b/routee-activity/src/main/java/org/sopt/routee/activity/api/event/ActivityDeletedEvent.java @@ -0,0 +1,6 @@ +package org.sopt.routee.activity.api.event; + +public record ActivityDeletedEvent( + Long memberId +) { +} diff --git a/routee-activity/src/main/java/org/sopt/routee/activity/internal/service/ActivityService.java b/routee-activity/src/main/java/org/sopt/routee/activity/internal/service/ActivityService.java index f9aff13..ac9051a 100644 --- a/routee-activity/src/main/java/org/sopt/routee/activity/internal/service/ActivityService.java +++ b/routee-activity/src/main/java/org/sopt/routee/activity/internal/service/ActivityService.java @@ -12,6 +12,7 @@ import java.util.stream.Collectors; import org.sopt.routee.activity.api.event.ActivityCompletedEvent; +import org.sopt.routee.activity.api.event.ActivityDeletedEvent; import org.sopt.routee.activity.internal.entity.activity.Activity; import org.sopt.routee.activity.internal.entity.activity.ActivityStatus; import org.sopt.routee.activity.internal.entity.timeline.Timeline; @@ -384,6 +385,7 @@ public void delete(Long activityId, Long memberId) { if (activity.getActivityStatus().isCompleted()) { refreshDailySummaryAfterDelete(activity); + applicationEventPublisher.publishEvent(new ActivityDeletedEvent(memberId)); } return activity; diff --git a/routee-member/src/main/java/org/sopt/routee/member/internal/listener/MemberActivityEventListener.java b/routee-member/src/main/java/org/sopt/routee/member/internal/listener/MemberActivityEventListener.java index a630256..e006da9 100644 --- a/routee-member/src/main/java/org/sopt/routee/member/internal/listener/MemberActivityEventListener.java +++ b/routee-member/src/main/java/org/sopt/routee/member/internal/listener/MemberActivityEventListener.java @@ -1,6 +1,7 @@ package org.sopt.routee.member.internal.listener; import org.sopt.routee.activity.api.event.ActivityCompletedEvent; +import org.sopt.routee.activity.api.event.ActivityDeletedEvent; import org.sopt.routee.member.internal.service.MemberService; import org.springframework.modulith.events.ApplicationModuleListener; import org.springframework.stereotype.Component; @@ -17,4 +18,9 @@ class MemberActivityEventListener { void handleActivityCompleted(ActivityCompletedEvent event) { memberService.incrementTotalActivityCount(event.memberId()); } + + @ApplicationModuleListener + void handleActivityDeleted(ActivityDeletedEvent event) { + memberService.decrementTotalActivityCount(event.memberId()); + } } \ No newline at end of file From 0c9feb8f46d29804e2b4f4ea6e86c2be4a8ea4e6 Mon Sep 17 00:00:00 2001 From: kymin Date: Thu, 5 Nov 2026 15:40:53 +0900 Subject: [PATCH 5/5] =?UTF-8?q?fix:=20=EC=A4=91=EB=B3=B5=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20=EB=8F=99=EC=8B=9C=20=EC=B2=98=EB=A6=AC=20=EB=B0=A9?= =?UTF-8?q?=EC=A7=80=EB=A5=BC=20=EC=9C=84=ED=95=9C=20=EB=B9=84=EA=B4=80?= =?UTF-8?q?=EC=A0=81=20=EB=9D=BD=20=EC=A1=B0=ED=9A=8C=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activity/internal/repository/ActivityRepository.java | 6 ++++++ .../routee/activity/internal/service/ActivityService.java | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/routee-activity/src/main/java/org/sopt/routee/activity/internal/repository/ActivityRepository.java b/routee-activity/src/main/java/org/sopt/routee/activity/internal/repository/ActivityRepository.java index a33badb..decb576 100644 --- a/routee-activity/src/main/java/org/sopt/routee/activity/internal/repository/ActivityRepository.java +++ b/routee-activity/src/main/java/org/sopt/routee/activity/internal/repository/ActivityRepository.java @@ -9,10 +9,13 @@ import org.sopt.routee.activity.internal.entity.activity.Activity; import org.sopt.routee.activity.internal.entity.activity.ActivityStatus; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Lock; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; +import jakarta.persistence.LockModeType; + public interface ActivityRepository extends JpaRepository { boolean existsByMemberIdAndActivityStatusIn(Long memberId, Collection activityStatuses); @@ -26,6 +29,9 @@ List findIdsByMemberIdAndActivityStatusIn( Optional findByIdAndMemberId(Long id, Long memberId); + @Lock(LockModeType.PESSIMISTIC_WRITE) + Optional findForUpdateByIdAndMemberId(Long id, Long memberId); + List findByMemberIdAndActivityStatusAndStartedAtBetweenOrderByStartedAtDesc( Long memberId, ActivityStatus activityStatus, diff --git a/routee-activity/src/main/java/org/sopt/routee/activity/internal/service/ActivityService.java b/routee-activity/src/main/java/org/sopt/routee/activity/internal/service/ActivityService.java index ac9051a..b02c879 100644 --- a/routee-activity/src/main/java/org/sopt/routee/activity/internal/service/ActivityService.java +++ b/routee-activity/src/main/java/org/sopt/routee/activity/internal/service/ActivityService.java @@ -198,7 +198,7 @@ public UpdateActivityTitleResult updateTitle(UpdateActivityTitleCommand command) @Transactional public void complete(CompleteActivityCommand command) { - Activity activity = activityRepository.findByIdAndMemberId(command.activityId(), command.memberId()) + Activity activity = activityRepository.findForUpdateByIdAndMemberId(command.activityId(), command.memberId()) .orElseThrow(ActivityNotFoundException::new); if (activity.getActivityStatus().isCompleted()) { @@ -376,7 +376,7 @@ public void deleteActivitiesByMemberId(long memberId) { public void delete(Long activityId, Long memberId) { Activity deletedActivity = transactionTemplate.execute(status -> { - Activity activity = activityRepository.findByIdAndMemberId(activityId, memberId) + Activity activity = activityRepository.findForUpdateByIdAndMemberId(activityId, memberId) .orElseThrow(ActivityNotFoundException::new); routeRepository.deleteByActivityIdIn(List.of(activityId));