정의: 스프링이 제공하는 범용 Redis 접근 객체.
특징:
opsForValue(), opsForHash(), opsForList(), opsForSet(), opsForZSet(), opsForStream() 등 → Redis 자료구조 전부 다룸String, JSON, Kryo 등)언제 주로 사용?
간단 커스터마이징 예 (JSON 값):
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory cf) {
RedisTemplate<String, Object> t = new RedisTemplate<>();
t.setConnectionFactory(cf);
t.setKeySerializer(new StringRedisSerializer());
t.setValueSerializer(new GenericJackson2JsonRedisSerializer());
t.setHashKeySerializer(new StringRedisSerializer());
t.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
t.afterPropertiesSet();
return t;
}
정의: RedisTemplate<String, String> 을 편하게 쓸 수 있도록 Spring Boot 가 자동으로 만들어 주는 특수 버전.
특징:
stringRedisTemplate.opsForValue().set("greeting", "hello");
stringRedisTemplate.opsForSet().add("likes:post:1", "user123");
Long size = stringRedisTemplate.opsForSet().size("likes:post:1");
언제?
정의: Spring Cache 추상화를 Redis 로 “백엔드” 매핑해 주는 관리자.
→ 네가 @Cacheable, @CachePut, @CacheEvict 를 붙이면 AOP 가 메서드 호출 전후로 자동으로 Redis 접근.
흐름 ( @Cacheable ):
장점:
기본 설정 예:
@EnableCaching
@Configuration
public class CacheConfig {
@Bean
public RedisCacheManager redisCacheManager(RedisConnectionFactory cf) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10))
.serializeKeysWith(RedisSerializationContext.SerializationPair
.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(cf)
.cacheDefaults(config)
.withInitialCacheConfigurations(Map.of(
"article", config.entryTtl(Duration.ofMinutes(5)),
"userProfile", config.entryTtl(Duration.ofHours(1))
))
.build();
}
}
사용 예:
@Cacheable(value = "article", key = "#id")
public ArticleDto getArticle(Long id) { ... }
@CacheEvict(value = "article", key = "#id")
public void deleteArticle(Long id) { ... }
@CachePut(value = "article", key = "#result.id")
public ArticleDto updateArticle(ArticleDto dto) { ... }
| 한 줄 요약 |
|---|
| RedisTemplate: “Redis 명령을 직접 다루는 범용 공구 상자.” |
| StringRedisTemplate: “그 공구 상자 중 ‘문자열만’ 빠르게 쓰는 미니 세트.” |
| RedisCacheManager: “특정 메서드 결과를 Redis 로 자동 캐시 해 주는 관리자(AOP).” |
Controller
↓
Service
├─ @Cacheable getArticle(id) ← RedisCacheManager (자동)
├─ likePost(postId, userId) ← StringRedisTemplate (Set: 중복방지)
├─ feedRecent() ← RedisTemplate (ZSet or Stream 소비)
@CacheableStringRedisTemplateRedisTemplate (JSON 직렬화)| 질문 | 예/답 | 선택 |
|---|---|---|
| “그냥 문자열+숫자만 저장?” | yes | StringRedisTemplate |
| “객체(JSON) 직렬화해서 다양한 구조 사용?” | yes | 커스텀 RedisTemplate |
| “메서드 결과 캐시하고 싶고 코드에 if문 넣기 싫어?” | yes | RedisCacheManager + @Cacheable |
| “TTL/직렬화 정책을 캐시별로 쉽게 다루고 싶어?” | yes | RedisCacheManager |
| “Pub/Sub, Stream, ZSet 직접?” | yes | RedisTemplate |
| “이후 Kafka 가거나 다른 백엔드로 교체 가능하게?” | 캐시만 추상화 | RedisCacheManager 유지 |
| 실수 | 왜 문제 | 해결 |
|---|---|---|
| JDK 기본 직렬화 그대로 사용 | 값이 이상한 바이트 → 디버깅 어려움, 클래스 변경 시 깨짐 | JSON 직렬화로 교체 |
@Cacheable + 내부 메서드 self 호출 | AOP 프록시 안 타서 캐시 미동작 | 외부 호출 경로, 또는 self-injection |
캐시 키 폭발: @Cacheable 에 파라미터 과다 | 메모리 급증 | key 전략 (필요 필드만), TTL |
| Set / Hash 혼용 시 직렬화 다름 | 조회 못함 | 동일 Serializer 정책 강제 |
캐시 무효화 잊음 (@CacheEvict) | stale 데이터 | create/update/delete 마다 Evict 또는 Put |
시나리오: 게시글 상세 + 좋아요 기능
| 기능 | 사용 |
|---|---|
| 게시글 상세 조회 (읽기 많음) | @Cacheable("article") (RedisCacheManager) |
| 게시글 수정 | @CachePut or @CacheEvict |
| 좋아요 클릭 중복 체크 & 카운트 | StringRedisTemplate (SADD, SCARD) |
| 실시간 인기글 (점수=좋아요수+시간감쇠) | RedisTemplate.opsForZSet() (ZSet) |
| 좋아요 이벤트 비동기 처리 | RedisTemplate.opsForStream() (Stream) |
[Client] ──HTTP──> [Service]
[Service.getArticle(id)]
└─ AOP: CacheInterceptor
├─ Redis GET (cache key: article::id)
├─ 없으면 -> DB 조회 -> Redis SET (TTL)
└─ 반환
[Service.like(postId,userId)]
├─ StringRedisTemplate SADD likes:post:1 user123
├─ (added==1) => INCR likes:post:1:count
└─ RedisTemplate XADD stream:like {...}
@Cacheable 썼는데 Redis 에 값이 안 생김. 가능한 원인 2가지?(필요하면 답도 알려줄게.)
@Cacheable 같은 선언형 애노테이션을 Redis 저장/조회로 연결해 주는 캐시 관리자”