SpringBoot -redis with cache

희운·2025년 7월 19일

Redis

목록 보기
2/2

2-1. RedisTemplate\<K, V>

정의: 스프링이 제공하는 범용 Redis 접근 객체.
특징:

  • opsForValue(), opsForHash(), opsForList(), opsForSet(), opsForZSet(), opsForStream() 등 → Redis 자료구조 전부 다룸
  • 직렬화 전략을 네가 Bean 등록 시 지정 (String, JSON, Kryo 등)
  • 멱등/파이프라인/트랜잭션, Pub/Sub 모두 가능

언제 주로 사용?

  • 객체를 JSON 으로 저장
  • List/Set/ZSet/Stream 같은 구조 직접 활용
  • 캐시라기보다 “실시간 상태 저장소” “이벤트 큐” 로 쓸 때

간단 커스터마이징 예 (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;
}

2-2. StringRedisTemplate

정의: RedisTemplate<String, String> 을 편하게 쓸 수 있도록 Spring Boot 가 자동으로 만들어 주는 특수 버전.

특징:

  • Key / Value / Hash Field / Hash Value / Set Member 전부 UTF-8 String 직렬화
  • 별도 Bean 선언 필요 없음 (의존성 + Redis 설정만 있으면 자동)
  • CLI 로 값 보면 그대로 읽힘 → 디버깅 최고
  • 객체를 넣고 싶으면 직접 JSON 변환 (ObjectMapper)
stringRedisTemplate.opsForValue().set("greeting", "hello");
stringRedisTemplate.opsForSet().add("likes:post:1", "user123");
Long size = stringRedisTemplate.opsForSet().size("likes:post:1");

언제?

  • 값이 정말 싹 다 문자열 / 숫자(문자열로 변환 가능)
  • 빠르게 프로토타입
  • 카운터, 토큰, 플래그, Set 기반 중복체크 등

2-3. RedisCacheManager

정의: Spring Cache 추상화를 Redis 로 “백엔드” 매핑해 주는 관리자.
→ 네가 @Cacheable, @CachePut, @CacheEvict 를 붙이면 AOP 가 메서드 호출 전후로 자동으로 Redis 접근.

흐름 ( @Cacheable ):

  1. 메서드 호출 직전 키 생성 (cacheName + 파라미터)
  2. Redis 에 “이미 값 있으면” → 메서드 본문 안 타고 바로 반환
  3. 없으면 메서드 실행 → 결과 직렬화해서 Redis 저장 → 반환

장점:

  • 코드에 캐시 로직 if/else 안 섞임
  • TTL, 직렬화 정책 중앙 관리
  • 로컬/Redis 다른 구현으로 쉽게 교체

기본 설정 예:

@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) { ... }

3

한 줄 요약
RedisTemplate: “Redis 명령을 직접 다루는 범용 공구 상자.”
StringRedisTemplate: “그 공구 상자 중 ‘문자열만’ 빠르게 쓰는 미니 세트.”
RedisCacheManager: “특정 메서드 결과를 Redis 로 자동 캐시 해 주는 관리자(AOP).”

4

Controller
  ↓
Service
  ├─ @Cacheable getArticle(id)  ← RedisCacheManager (자동)
  ├─ likePost(postId, userId)   ← StringRedisTemplate (Set: 중복방지)
  ├─ feedRecent()               ← RedisTemplate (ZSet or Stream 소비)
  • 읽기 많은 엔드포인트@Cacheable
  • 실시간 상태(좋아요 중복, 카운트)StringRedisTemplate
  • 복잡 구조(ZSet 정렬, Stream 이벤트)RedisTemplate (JSON 직렬화)

5

질문예/답선택
“그냥 문자열+숫자만 저장?”yesStringRedisTemplate
“객체(JSON) 직렬화해서 다양한 구조 사용?”yes커스텀 RedisTemplate
“메서드 결과 캐시하고 싶고 코드에 if문 넣기 싫어?”yesRedisCacheManager + @Cacheable
“TTL/직렬화 정책을 캐시별로 쉽게 다루고 싶어?”yesRedisCacheManager
“Pub/Sub, Stream, ZSet 직접?”yesRedisTemplate
“이후 Kafka 가거나 다른 백엔드로 교체 가능하게?”캐시만 추상화RedisCacheManager 유지

6

실수왜 문제해결
JDK 기본 직렬화 그대로 사용값이 이상한 바이트 → 디버깅 어려움, 클래스 변경 시 깨짐JSON 직렬화로 교체
@Cacheable + 내부 메서드 self 호출AOP 프록시 안 타서 캐시 미동작외부 호출 경로, 또는 self-injection
캐시 키 폭발: @Cacheable 에 파라미터 과다메모리 급증key 전략 (필요 필드만), TTL
Set / Hash 혼용 시 직렬화 다름조회 못함동일 Serializer 정책 강제
캐시 무효화 잊음 (@CacheEvict)stale 데이터create/update/delete 마다 Evict 또는 Put

7

시나리오: 게시글 상세 + 좋아요 기능

기능사용
게시글 상세 조회 (읽기 많음)@Cacheable("article") (RedisCacheManager)
게시글 수정@CachePut or @CacheEvict
좋아요 클릭 중복 체크 & 카운트StringRedisTemplate (SADD, SCARD)
실시간 인기글 (점수=좋아요수+시간감쇠)RedisTemplate.opsForZSet() (ZSet)
좋아요 이벤트 비동기 처리RedisTemplate.opsForStream() (Stream)

8. 한눈에 흐름 그림

[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 {...}

9

  1. StringRedisTemplate: 기본 Redis 명령 감각 익히기
  2. RedisTemplate (JSON 커스터마이징): ZSet, Hash, Stream 활용
  3. RedisCacheManager + @Cacheable: 서비스 레이어 캐싱 구조화
  4. (추가) TTL 전략, 캐시 스탬피드 방지, 분산락, 비동기 이벤트

10

  1. @Cacheable 썼는데 Redis 에 값이 안 생김. 가능한 원인 2가지?
  2. Set 중복 방지를 위해 어떤 템플릿과 명령?
  3. 객체를 JSON 으로 넣고 싶다. 어느 Bean 에 어떤 Serializer?
  4. Stream 으로 이벤트 소비 시 ACK 안 하면 어디에 남나?

(필요하면 답도 알려줄게.)


  • RedisTemplate: “원하는 방식으로 직렬화 설정해서 Redis 자료구조를 직접 건드리는 범용 도구”
  • StringRedisTemplate: “전부 문자열로 단순·가볍게 Redis 명령 쓰는 빠른 스타터”
  • RedisCacheManager: @Cacheable 같은 선언형 애노테이션을 Redis 저장/조회로 연결해 주는 캐시 관리자”

profile
기록하는 공간

0개의 댓글