From aaca0a6740c6ce488a4506a5fe597e05192c841c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=82=98=EB=AF=B8?= Date: Tue, 11 Aug 2026 16:12:20 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EA=B3=B5=EA=B0=9C=20=EC=BD=94=EC=8A=A4?= =?UTF-8?q?=20=EC=93=B0=EA=B8=B0=20=EC=9E=91=EC=97=85=20=EC=8B=9C=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EC=BA=90=EC=8B=9C=20=EC=A6=89=EC=8B=9C=20?= =?UTF-8?q?=EB=AC=B4=ED=9A=A8=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dev/main 정합성 점검 중 발견 — main엔 이미 반영된 캐시 무효화 로직 (#208)이 dev엔 빠져있어서, dev 환경에서 코스 추가/삭제/공개코스 수정을 해도 조회 캐시(전체페이지수/마라톤/추천, TTL 1분)가 최대 1분간 오래된 목록을 계속 보여주는 문제가 있었다. main과 동일하게 @CacheEvict를 코스 삭제/공개코스 생성·삭제·수정 지점에 추가했다. --- .../org/runnect/server/course/service/CourseService.java | 4 ++++ .../server/publicCourse/service/PublicCourseService.java | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/src/main/java/org/runnect/server/course/service/CourseService.java b/src/main/java/org/runnect/server/course/service/CourseService.java index 1f6c6ca..ecec62f 100644 --- a/src/main/java/org/runnect/server/course/service/CourseService.java +++ b/src/main/java/org/runnect/server/course/service/CourseService.java @@ -27,6 +27,7 @@ import org.runnect.server.user.exception.userException.NotFoundUserException; import org.runnect.server.user.repository.UserRepository; import org.runnect.server.user.service.UserStampService; +import org.springframework.cache.annotation.CacheEvict; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -136,6 +137,9 @@ public UpdateCourseResponseDto updateCourse(Long userId, Long courseId, String t return UpdateCourseResponseDto.of(course); } + // 조건부로 공개 코스가 같이 삭제될 수 있어(비공개 코스면 스킵), 매번 무효화해도 + // 안전한 쪽(과다 무효화)을 택함 — 공개 코스 목록 캐시(전체페이지수/마라톤/추천) 대상 + @CacheEvict(value = {"publicCourseTotalPageCount", "marathonPublicCourse", "recommendPublicCourse"}, allEntries = true) @Transactional public DeleteCoursesResponseDto deleteCourses(List courseIdList, Long userId) { courseIdList.forEach(courseId -> { diff --git a/src/main/java/org/runnect/server/publicCourse/service/PublicCourseService.java b/src/main/java/org/runnect/server/publicCourse/service/PublicCourseService.java index 8b500cb..7c40109 100644 --- a/src/main/java/org/runnect/server/publicCourse/service/PublicCourseService.java +++ b/src/main/java/org/runnect/server/publicCourse/service/PublicCourseService.java @@ -39,6 +39,7 @@ import org.runnect.server.user.exception.userException.NotFoundUserException; import org.runnect.server.user.repository.UserRepository; import org.springframework.beans.factory.annotation.Value; +import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -304,6 +305,9 @@ public GetPublicCourseDetailResponseDto getPublicCourseDetail(final Long userId, } + // 코스 목록에 영향을 주는 쓰기 작업이라, 작성자 본인 캐시만이 아니라 + // 전체 유저가 보는 목록 캐시(전체페이지수/마라톤/추천)를 다 지워야 함 + @CacheEvict(value = {"publicCourseTotalPageCount", "marathonPublicCourse", "recommendPublicCourse"}, allEntries = true) @Transactional public CreatePublicCourseResponseDto createPublicCourse( final Long userId, @@ -348,6 +352,7 @@ public CreatePublicCourseResponseDto createPublicCourse( } + @CacheEvict(value = {"publicCourseTotalPageCount", "marathonPublicCourse", "recommendPublicCourse"}, allEntries = true) @Transactional public DeletePublicCoursesResponseDto deletePublicCourses( Long userId, @@ -393,6 +398,8 @@ public DeletePublicCoursesResponseDto deletePublicCourses( return DeletePublicCoursesResponseDto.from(publicCourses.size()); } + // 제목/설명만 바뀌고 코스 개수는 그대로라 전체페이지수 캐시는 지울 필요 없음 + @CacheEvict(value = {"marathonPublicCourse", "recommendPublicCourse"}, allEntries = true) @Transactional public UpdatePublicCourseResponseDto updatePublicCourse(Long userId, Long publicCourseId, String title, String description) { PublicCourse publicCourse = publicCourseRepository.findById(publicCourseId)