From 9dc200f711d812f0ac25281fd411644e7f01dc83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=82=98=EB=AF=B8?= Date: Wed, 29 Jul 2026 23:00:33 +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 @Cacheable만 있고 @CacheEvict가 없어 코스 생성/수정/삭제 후에도 최대 TTL(1분) 동안 오래된 목록이 그대로 노출되던 문제 수정. 코스 목록은 전체 유저 공용이라 작성자 캐시만이 아니라 allEntries로 전체페이지수/마라톤/추천 캐시를 모두 무효화하도록 함. --- .../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 9a485c9..7bfd3e0 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 9d6956a..5a757fd 100644 --- a/src/main/java/org/runnect/server/publicCourse/service/PublicCourseService.java +++ b/src/main/java/org/runnect/server/publicCourse/service/PublicCourseService.java @@ -38,6 +38,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; @@ -300,6 +301,9 @@ public GetPublicCourseDetailResponseDto getPublicCourseDetail(final Long userId, } + // 코스 목록에 영향을 주는 쓰기 작업이라, 작성자 본인 캐시만이 아니라 + // 전체 유저가 보는 목록 캐시(전체페이지수/마라톤/추천)를 다 지워야 함 + @CacheEvict(value = {"publicCourseTotalPageCount", "marathonPublicCourse", "recommendPublicCourse"}, allEntries = true) @Transactional public CreatePublicCourseResponseDto createPublicCourse( final Long userId, @@ -344,6 +348,7 @@ public CreatePublicCourseResponseDto createPublicCourse( } + @CacheEvict(value = {"publicCourseTotalPageCount", "marathonPublicCourse", "recommendPublicCourse"}, allEntries = true) @Transactional public DeletePublicCoursesResponseDto deletePublicCourses( Long userId, @@ -389,6 +394,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)