Skip to content

Conversation

@yunrry
Copy link
Contributor

@yunrry yunrry commented Aug 15, 2025

Post Redis 캐시 구현

개요

게시물 조회 성능 향상을 위한 Redis 캐시 시스템을 구현했습니다.

주요 변경사항

1. 캐시 설정 추가

  • POST_DETAIL, POST_LIST, POST_SLICE, POST_SUMMARY 캐시 타입 정의
  • 각각 다른 TTL과 최대 저장 개수 설정

2. 캐시 DTO 구현

  • PostCacheDto: Post 도메인 객체의 캐시 전용 DTO

    • Redis 직렬화/역직렬화 최적화
    • @JsonTypeInfo 어노테이션으로 타입 정보 보존
    • from(), toPost() 메서드로 변환 지원
  • PostSliceCacheDto: Slice 객체의 캐시 전용 DTO

    • 페이징 정보 포함 (hasNext, size, numberOfElements)
    • toSlice() 메서드로 Spring Data Slice 복원

3. 캐시 서비스 구현

@Service
@RequiredArgsConstructor
public class PostCacheService {
    
    @Cacheable(cacheNames = CacheName.POST_DETAIL_CACHE, key = "#postId", sync = true)
    public Post getPost(Long postId) {
        // 게시물 상세 조회
    }
    
    @Cacheable(cacheNames = CacheName.POST_SUMMARY_CACHE, key = "#postId", sync = true)
    public PostSummaryResponse getPostSummary(Long postId) {
        // 게시물 요약 조회
    }
    
    @CacheEvict(cacheNames = {
        CacheName.POST_DETAIL_CACHE,
        CacheName.POST_SUMMARY_CACHE
    }, key = "#postId")
    public void evictPost(Long postId) {
        // 특정 게시물 캐시 삭제
    }
}

기술적 특징

캐시 TTL 설정

  • POST_DETAIL: 10분 (상세 조회)
  • POST_LIST/SLICE: 5분 (목록 조회)
  • POST_SUMMARY: 5분 (요약 정보)

직렬화 최적화

  • @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)로 타입 안전성 보장
  • 복합 객체(Location, PostEmotionTag 등) 직렬화 지원
  • null 안전한 컬렉션 처리

성능 개선 효과

  • DB 조회 횟수 감소로 응답 시간 단축
  • 동시 접속자 증가 시 안정성 향상
  • 인기 게시물의 반복 조회 최적화

테스트 고려사항

  • 캐시 히트/미스 로깅 확인
  • 캐시 무효화 정책 검증
  • 메모리 사용량 모니터링 필요

@yunrry yunrry linked an issue Aug 15, 2025 that may be closed by this pull request
@yunrry yunrry requested a review from LJW22222 August 15, 2025 13:35
Copy link
Contributor

@LJW22222 LJW22222 left a comment

Choose a reason for hiding this comment

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

고생많으셨습니다!

Comment on lines 34 to 38
public PostCacheDto getPost(Long postId) {
log.debug("캐시 미스 - DB에서 게시물 조회: postId={}", postId);
Post post = getPostQueryService.handle(postId);
return PostCacheDto.from(post);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

해당 부분은 Post를 바로 반환하는게 맞는거 같습니다. DTO를 별도로 발행해서 반환해도 무방하지만, Post라는 Domain 자체가 VO객체이므로 Post를 반환후에, 별도로 데이터를 조합해서 내보내야 하는 경우에 Application계층이나 API계층에서 짜집기해서 내보내는 것이 맞는거 같습니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

해당 부분 수정했습니다!

Comment on lines +45 to +48
public Page<Post> getPostList(PostListRequest request) {
log.debug("캐시 미스 - DB에서 게시물 목록 조회: {}", request);
return getPostListQueryService.handle(request);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

현재는 Page를 해서 캐시에 저장하고 있는데, 해당 부분은 제가 공용으로 쓸 수 있는 클래스 만들고 해당 클래스를 사용하도록 하면 될 것 같습니다.

Comment on lines +58 to +72
public PostSummaryResponse getPostSummary(Long postId) {
log.info("캐시 미스 - DB에서 게시물 요약 조회: postId={}", postId);

if (!getPostQueryService.existsById(postId)) {
log.info("게시물이 존재하지 않음: postId={}", postId);
return new PostSummaryResponse(
null, null, null, null, null, null, null
);
}

PostSummaryResponse result = getPostQueryService.handlePostSummary(postId);
log.info("게시물 요약 조회 완료: postId={}", postId);

return result;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

해당 부분 또한 동일합니다.

Comment on lines -29 to 40
@Bean("redisCacheManager")
public CacheManager redisCacheManager(RedisConnectionFactory connectionFactory) {
public RedisCacheManager redisCacheManager(RedisConnectionFactory connectionFactory) {
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(redisCacheConfiguration(Duration.ofMinutes(1)))
.withInitialCacheConfigurations(customConfigurationMap())
.build();
}

@Bean @Primary
public CacheManager cacheManager(RedisCacheManager redis, MeterRegistry registry) {
return new MeteredCacheManager(redis, registry);
public CacheManager cacheManager(@Qualifier("redisCacheManager") RedisCacheManager redisCacheManager, MeterRegistry registry) {
return new MeteredCacheManager(redisCacheManager, registry);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

CacheManager로 해도 무방하지만, 정말 단순하게 궁금해서 그렇습니다. CacheManager -> RedisCacheManager로 바꾼 의도가 궁금합니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

이부분 노션 버그 리포트 정리해서 올렸습니다. 참고해주세요!
https://www.notion.so/leeporject/23e37ed0f4f880a9a179f7358efa0d81?source=copy_link

@LJW22222 LJW22222 merged commit dd5b8ae into main Aug 19, 2025
1 check passed
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.

Issue: Post 조회 Redis 캐시 추가

3 participants