Skip to content

✨ Fix: Redis 캐시 적용 및 게시글 목록 역직렬화 오류 해결#39

Merged
angoroa merged 7 commits intodevelopfrom
feat/redis
Jan 31, 2026
Merged

✨ Fix: Redis 캐시 적용 및 게시글 목록 역직렬화 오류 해결#39
angoroa merged 7 commits intodevelopfrom
feat/redis

Conversation

@angoroa
Copy link
Copy Markdown
Member

@angoroa angoroa commented Jan 31, 2026

#️⃣ Issue Number

  • closed #

📝 요약(Summary)

Redis 캐시를 도입하면서 발생한 게시글 목록 조회(postList) 역직렬화 오류를 해결하고,
facility API와 post API의 캐시 전략을 분리하여 안정적으로 동작하도록 개선했습니다.

  • facilityNear / facilityMarkers API는 기존 동작을 유지하면서 캐시 키 전략을 명확히 분리
  • postList 캐시는 Page → 전용 DTO(PostPageResponse)로 변환하여 캐시 저장
  • 캐시 타입 불일치로 인한 ClassCastException 문제 해결

🛠️ PR 유형

어떤 변경 사항이 있나요?

  • 새로운 기능 추가
  • 버그 수정
  • CSS 등 사용자 UI 디자인 변경
  • 코드에 영향을 주지 않는 변경사항(오타 수정, 탭 사이즈 변경, 변수명 변경)
  • 코드 리팩토링
  • 주석 추가 및 수정
  • 문서 수정
  • 테스트 추가, 테스트 리팩토링
  • 빌드 부분 혹은 패키지 매니저 수정
  • 파일 혹은 폴더명 수정
  • 파일 혹은 폴더 삭제

📸스크린샷 (선택)

  • Redis 캐시 적용 후 near / markers / postList API 정상 동작 확인

💬 공유사항 to 리뷰어

  • facility 캐시는 기존 Object 기반 직렬화를 유지하고,
    postList 캐시는 전용 DTO(PostPageResponse)로 직렬화 방식을 분리했습니다.
  • 캐시 타입 불일치로 인한 오류 재발 방지를 위해 RedisCacheConfig에서 캐시별 Serializer를 분리했습니다.

✅ PR Checklist

PR이 다음 요구 사항을 충족하는지 확인하세요.

  • 커밋 메시지 컨벤션에 맞게 작성했습니다.
  • 변경 사항에 대한 테스트를 했습니다.(버그 수정/기능에 대한 테스트).

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • Redis 캐싱을 통한 성능 최적화 추가
    • 게시물 삭제 기능 추가
    • 시설 검색 결과 캐싱으로 빠른 응답 속도 제공
  • 개선 사항

    • 게시물 목록 API 응답에 상세한 페이지 정보 추가 (현재 페이지, 총 개수, 다음 페이지 여부 등)
  • 의존성

    • Redis 데이터 접근 및 캐싱 라이브러리 추가

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 31, 2026

Caution

Review failed

The pull request is closed.

Walkthrough

이 PR은 Redis 캐싱 인프라를 추가하고 시설 및 포스트 도메인에 캐싱 기능을 구현합니다. Spring Boot Redis/Cache 의존성을 추가하고, 캐시 키 생성 인터페이스와 구현체들을 도입하며, TTL 설정을 포함한 Redis 캐시 구성을 새로 생성합니다.

Changes

Cohort / File(s) Summary
캐싱 인프라
build.gradle, src/main/java/com/be/sportizebe/global/config/RedisCacheConfig.java, src/main/java/com/be/sportizebe/global/cache/PostListKeyGenerator.java
Redis 의존성 추가, CacheManager 빈 정의, 캐시별 TTL 설정(facilityNear/facilityMarkers 60초, postList 30초), Jackson 직렬화 구성, 포스트 목록 캐시용 KeyGenerator 구현.
시설 도메인 캐싱
src/main/java/com/be/sportizebe/domain/facility/dto/CacheKeyProvider.java, src/main/java/com/be/sportizebe/domain/facility/dto/request/FacilityNearRequest.java, src/main/java/com/be/sportizebe/domain/facility/dto/request/FacilityMarkerRequest.java, src/main/java/com/be/sportizebe/domain/facility/service/SportsFacilityService.java, src/main/java/com/be/sportizebe/domain/facility/service/SportsFacilityServiceImpl.java
CacheKeyProvider 인터페이스 도입, 요청 DTO에 캐시 키 생성 로직 추가(위도/경도 4자리, 반경, 제한, 유형 포함), SportsFacilityService를 인터페이스로 리팩토링, 캐싱 기능이 포함된 구현체 추가.
포스트 도메인 응답 및 캐싱
src/main/java/com/be/sportizebe/domain/post/dto/response/PageInfoResponse.java, src/main/java/com/be/sportizebe/domain/post/dto/response/PostPageResponse.java, src/main/java/com/be/sportizebe/domain/post/service/PostService.java, src/main/java/com/be/sportizebe/domain/post/service/PostServiceImpl.java, src/main/java/com/be/sportizebe/domain/post/controller/PostController.java
페이지 정보 및 포스트 목록을 캡슐화하는 새로운 응답 DTO 생성, getPosts 반환 타입 변경, 캐싱 및 캐시 무효화 어노테이션 추가(@Cacheable, @CacheEvict), deletePost/updatePost 메서드 추가 및 캐시 관리 로직 통합.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Controller
    participant Service
    participant Cache as Redis Cache
    participant Repository
    participant Mapper

    Client->>Controller: getNear(FacilityNearRequest)
    Controller->>Service: getNear(request)
    Service->>Service: generateCacheKey()
    Service->>Cache: check cache key
    alt Cache Hit
        Cache-->>Service: FacilityNearResponse list
    else Cache Miss
        Service->>Repository: findNearby(lat, lng, radius)
        Repository-->>Service: List<Facility>
        Service->>Mapper: map to FacilityNearResponse
        Mapper-->>Service: FacilityNearResponse list
        Service->>Cache: store with TTL (60s)
    end
    Service-->>Controller: FacilityNearResponse list
    Controller-->>Client: BaseResponse<List<FacilityNearResponse>>
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 캐시의 마법으로 속도는 증가하고,
Redis 데이터가 빠르게 흐르고,
TTL 시간으로 신선함 지키며,
시설과 포스트 모두 행복해하네!
🚀✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.65% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 Redis 캐시 적용과 게시글 목록 역직렬화 오류 해결이라는 주요 변경사항을 명확히 요약하고 있습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/redis

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@angoroa angoroa merged commit 5a37319 into develop Jan 31, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/main/java/com/be/sportizebe/domain/facility/dto/request/FacilityMarkerRequest.java (1)

27-47: ⚠️ Potential issue | 🟡 Minor

radiusM/limit의 null 입력과 Locale에 따른 캐시 키 불안정성 개선

@Min/@max 제약조건은 null 값을 검증하지 않으므로, radiusM과 limit이 null일 경우 generateCacheKey()의 %d 언박싱에서 NPE가 발생할 수 있습니다. 또한 Locale.ROOT를 지정하지 않으면 String.format()이 시스템 로케일에 따라 소수점 구분자가 변경되어 (예: fr_FR에서는 "37,2869", en_US에서는 "37.2869") 캐시 키의 일관성이 깨질 수 있습니다.

@NotNull 추가와 Locale.ROOT 지정으로 개선하세요. FacilityNearRequest도 동일한 패턴을 사용하므로 함께 적용하세요.

제안 변경
     `@Schema`(description = "반경(미터)", example = "5000", defaultValue = "5000")
+    `@NotNull`(message = "반경은 필수입니다")
     `@Min`(value = 100, message = "반경은 최소 100m 이상이어야 합니다")
     `@Max`(value = 20000, message = "반경은 최대 20km까지 가능합니다")
     private Integer radiusM = 5000;

     `@Schema`(description = "조회 개수 제한", example = "200", defaultValue = "200")
+    `@NotNull`(message = "limit는 필수입니다")
     `@Min`(value = 1, message = "limit는 최소 1 이상이어야 합니다")
     `@Max`(value = 500, message = "limit는 최대 500까지 가능합니다")
     private Integer limit = 200;

     `@Override`
     public String generateCacheKey() {
         double gridLat = Math.round(this.lat * 10000) / 10000.0;
         double gridLng = Math.round(this.lng * 10000) / 10000.0;
         String typeName = (type == null) ? "ALL" : type.name();

-        return String.format("%.4f:%.4f:%d:%d:%s",
+        return String.format(java.util.Locale.ROOT, "%.4f:%.4f:%d:%d:%s",
                 gridLat, gridLng, radiusM, limit, typeName);
src/main/java/com/be/sportizebe/domain/facility/dto/request/FacilityNearRequest.java (1)

27-48: ⚠️ Potential issue | 🟡 Minor

캐시 키 생성 시 null 처리와 Locale 안정화 필요

radiusM/limit이 Integer 래퍼 타입이면서 @NotNull 없이 명시적으로 null로 설정될 경우 String.format의 %d 언박싱에서 NPE가 발생할 수 있습니다. 또한 String.format은 JVM 기본 Locale에 따라 소수점 구분자가 달라져(미국은 ., 유럽은 ,) 캐시 키의 일관성이 깨질 수 있습니다. 각 필드에 @NotNull을 추가하고 String.format에 Locale.ROOT를 명시하여 안정화하세요.

제안 변경
-    `@Min`(value = 100, message = "반경은 최소 100m 이상이어야 합니다")
-    `@Max`(value = 10000, message = "반경은 최대 10km까지 가능합니다")
-    private Integer radiusM = 3000;
+    `@NotNull`(message = "반경은 필수입니다")
+    `@Min`(value = 100, message = "반경은 최소 100m 이상이어야 합니다")
+    `@Max`(value = 10000, message = "반경은 최대 10km까지 가능합니다")
+    private Integer radiusM = 3000;

-    `@Min`(value = 1, message = "limit는 최소 1 이상이어야 합니다")
-    `@Max`(value = 100, message = "limit는 최대 100까지 가능합니다")
-    private Integer limit = 50;
+    `@NotNull`(message = "limit는 필수입니다")
+    `@Min`(value = 1, message = "limit는 최소 1 이상이어야 합니다")
+    `@Max`(value = 100, message = "limit는 최대 100까지 가능합니다")
+    private Integer limit = 50;

-        return String.format("%.4f:%.4f:%d:%d:%s",
+        return String.format(java.util.Locale.ROOT, "%.4f:%.4f:%d:%d:%s",
                 gridLat, gridLng, radiusM, limit, typeName);

동일한 문제가 FacilityMarkerRequest.java에도 있습니다.

🤖 Fix all issues with AI agents
In
`@src/main/java/com/be/sportizebe/domain/facility/service/SportsFacilityService.java`:
- Line 12: The inline comment on the getNear method contains a typo ("조히") — in
the SportsFacilityService interface update the comment for the method
List<FacilityNearResponse> getNear(FacilityNearRequest request) to read "현재 위치
기준 반경 내 체육시설을 거리순으로 조회" (replace "조히" with "조회"); simply correct the Korean
comment text adjacent to the getNear method declaration.
🧹 Nitpick comments (2)
src/main/java/com/be/sportizebe/global/config/RedisCacheConfig.java (1)

26-45: 캐시 ObjectMapper를 공용 빈에 맞춰 설정 드리프트를 줄여주세요.

현재 캐시 전용 ObjectMapper를 새로 만들어 JacksonConfig와 설정이 분리될 수 있습니다. 공용 ObjectMapper를 주입받아 copy() 후 필요한 모듈만 추가하는 방식이 유지보수에 안전합니다. 또한 “타입 정보 포함” 주석은 실제 동작과 어긋나니 정정이 필요합니다.

♻️ 제안 변경
-    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
-        // 캐시 역직렬화를 위해 타입 정보(`@class`)를 포함하도록 설정
-        ObjectMapper objectMapper = JsonMapper.builder()
-                .addModule(new JavaTimeModule())
-                .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
-                // ❗ 타입 정보 절대 쓰지 않음
-                .build();
+    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory, ObjectMapper objectMapper) {
+        // 캐시 직렬화 전용 설정(공용 ObjectMapper 기반)
+        ObjectMapper cacheObjectMapper = objectMapper.copy()
+                .registerModule(new JavaTimeModule())
+                .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
-        defaultValueSerializer.setObjectMapper(objectMapper);
+        defaultValueSerializer.setObjectMapper(cacheObjectMapper);
...
-        postListValueSerializer.setObjectMapper(objectMapper);
+        postListValueSerializer.setObjectMapper(cacheObjectMapper);
src/main/java/com/be/sportizebe/global/cache/PostListKeyGenerator.java (1)

14-18: KeyGenerator 파라미터 검증으로 실패 원인을 명확히 해주세요.

현재는 params 길이/타입이 다르면 ClassCastException이 발생합니다. 명시적 검증으로 빠르게 실패하도록 하는 편이 안전합니다.

♻️ 제안 변경
     `@Override`
     public Object generate(Object target, Method method, Object... params) {
-
-        PostProperty property = (PostProperty) params[0];
-        Pageable pageable = (Pageable) params[1];
+        if (params.length < 2
+                || !(params[0] instanceof PostProperty)
+                || !(params[1] instanceof Pageable)) {
+            throw new IllegalArgumentException("postListKeyGenerator expects (PostProperty, Pageable)");
+        }
+        PostProperty property = (PostProperty) params[0];
+        Pageable pageable = (Pageable) params[1];

public interface SportsFacilityService {

private final SportsFacilityRepository sportsFacilityRepository;
List<FacilityNearResponse> getNear(FacilityNearRequest request); // 현재 위치 기준 반경 내 체육시설을 거리순으로 조히
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

주석 오탈자 수정 제안(“조히” → “조회”).

✍️ 수정 제안
-    List<FacilityNearResponse> getNear(FacilityNearRequest request); // 현재 위치 기준 반경 내 체육시설을 거리순으로 조히
+    List<FacilityNearResponse> getNear(FacilityNearRequest request); // 현재 위치 기준 반경 내 체육시설을 거리순으로 조회
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
List<FacilityNearResponse> getNear(FacilityNearRequest request); // 현재 위치 기준 반경 내 체육시설을 거리순으로 조히
List<FacilityNearResponse> getNear(FacilityNearRequest request); // 현재 위치 기준 반경 내 체육시설을 거리순으로 조회
🤖 Prompt for AI Agents
In
`@src/main/java/com/be/sportizebe/domain/facility/service/SportsFacilityService.java`
at line 12, The inline comment on the getNear method contains a typo ("조히") — in
the SportsFacilityService interface update the comment for the method
List<FacilityNearResponse> getNear(FacilityNearRequest request) to read "현재 위치
기준 반경 내 체육시설을 거리순으로 조회" (replace "조히" with "조회"); simply correct the Korean
comment text adjacent to the getNear method declaration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant