Spring Boot 캐싱 전략 비교: 어노테이션·Redis·Caffeine
Spring Boot에서 어노테이션 기반 캐시와 Redis, Caffeine을 비교하여 장단점, 설정 예제, 성능 및 운영 관점을 정리한 기술 분석문서
목차
개요
애플리케이션 성능 최적화에서 캐싱은 필수 요소다. Spring Boot는 어노테이션 기반 방식으로 개발 편의성을 제공하며, 내부 메모리 캐시인 Caffeine과 분산 캐시인 Redis를 쉽게 통합할 수 있다. 이 글은 기본 개념부터 설정 예제, 운영 고려사항까지 초심자도 이해할 수 있도록 정리한다. 주요 키워드는 spring cache caffeine 예제, spring boot redis cache vs caffeine, caching 전략 spring boot 이다.
캐싱 기초와 어노테이션
기본 개념
캐시는 반복 조회 비용이 큰 데이터를 임시 저장하여 응답 시간을 줄인다. 캐시계층은 원본 데이터 소스 부하를 낮추고 레이턴시를 개선하지만, 일관성과 메모리 관리는 신경 써야 한다.
Spring 어노테이션
Spring은 간단한 어노테이션으로 캐시를 제어한다.
- @Cacheable: 메서드 결과를 캐시에 저장하고 다음 호출에서 캐시를 반환
- @CachePut: 캐시를 갱신하면서 메서드는 항상 실행
- @CacheEvict: 캐시 항목을 삭제(또는 전체 삭제)
- @Caching: 여러 캐시 연산을 한 번에 적용
예시 서비스
package com.example.service;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Cacheable(cacheNames = "users", key = "#id")
public User getUserById(String id) {
// DB 호출이나 외부 API 호출 등 비용이 큰 작업
return findUserFromDb(id);
}
private User findUserFromDb(String id) {
// 실제 DB 조회 로직
return new User(id, "name");
}
}
Caffeine 소개 및 설정
특징
- 애플리케이션 내 로컬 캐시(인메모리)
- 낮은 레이턴시, 높은 처리량
- GC 영향과 메모리 한계에 유의
설정 예시
스프링 부트에서 Caffeine을 사용할 때는 의존성 추가와 CacheManager 설정이 필요하다.
# build.gradle
dependencies {
implementation 'com.github.ben-manes.caffeine:caffeine'
implementation 'org.springframework.boot:spring-boot-starter-cache'
}
# Java 설정 예
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
CaffeineCacheManager manager = new CaffeineCacheManager("users");
manager.setCaffeine(com.github.benmanes.caffeine.cache.Caffeine.newBuilder()
.expireAfterWrite(java.time.Duration.ofMinutes(10))
.maximumSize(10_000));
return manager;
}
}
Redis 소개 및 설정
특징
- 분산 캐시로 여러 인스턴스 간 공유 가능
- 데이터 내구성(옵션), 만료 정책 지원
- 네트워크 오버헤드와 시리얼라이제이션 비용 고려
설정 예시
Redis는 네트워크 호출이 발생하므로 데이터 직렬화 방식과 TTL 관리가 중요하다.
# build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.springframework.boot:spring-boot-starter-cache'
}
# application.yml 예
spring:
redis:
host: localhost
port: 6379
cache:
type: redis
# Java 설정 예 (직렬화 설정 포함)
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
RedisTemplate template = new RedisTemplate();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
}
Redis vs Caffeine 비교
- 성능: Caffeine은 로컬 메모리라서 레이턴시가 가장 낮다.
- 확장성: Redis는 분산 환경에서 캐시 일관성 유지에 유리하다.
- 운영: Redis는 별도 인프라(클러스터, 모니터링)가 필요하다.
- 일관성: Caffeine은 인스턴스별 캐시 분산으로 캐시 동기화가 어렵다.
- 비용: 메모리 기반 로컬 캐시는 비용이 적지만 전체 시스템 메모리 사용량이 증가한다.
실무 고려사항
캐시 설계 시 다음을 검토한다.
- 데이터 특성: 자주 바뀌는 데이터는 짧은 TTL 또는 무효화 전략 필요
- 일관성 요구사항: 강한 일관성이 필요하면 캐시 사용 자체를 재검토
- 모니터링: 히트율, 적중/미스 비율, 메모리 사용량 모니터링
- 오염 방지: 캐시 스톰(동시 갱신) 방지 위한 mutex 전략 또는 요청 제한
권장 패턴
- 읽기 중심, 짧은 응답 시간 요구: Caffeine 우선 고려
- 여러 인스턴스에서 데이터 공유 필요: Redis 권장
- 혼합 전략: 애플리케이션 로컬에 Caffeine을 두고, 중요한 데이터는 Redis로 계층화
- 테스트: 성능 테스트를 통해 TTL, 사이즈, 직렬화 비용을 검증
마무리
Spring의 어노테이션은 개발 생산성을 높인다. 그러나 어떤 캐시를 선택할지는 성능 목표, 일관성 요구, 운영 여건에 따라 달라진다. 간단한 조회 가속에는 Caffeine이 빠르고 효율적이며, 분산 환경이나 데이터 공유가 필요하면 Redis가 더 적합하다. 실제 환경에서는 spring boot redis cache vs caffeine 관점에서 두 옵션을 조합하여 운영하는 경우가 많으므로 caching 전략 spring boot를 팀 요구에 맞게 설계하는 것이 중요하다. 마지막으로 spring cache caffeine 예제와 같은 실전 테스트를 통해 히트율과 레이턴시를 측정해 최적 설정을 도출한다.