Skip to content
사고 기록
뒤로

Redis 인메모리 저장소와 캐싱 학습

Edit page

인메모리 저장소와 캐싱 전략 강의를 들으며 핵심 개념을 정리했다. Redis를 단순히 빠른 저장소로만 생각했는데, 용도에 맞는 자료형과 명령을 선택하는 것이 설계의 핵심이라는 점을 다시 알게 됐다.

Table of contents

Open Table of contents

인메모리와 Redis의 역할

  • RDB는 데이터의 영속성과 정합성을 보장하는 데 적합하지만, 변경이 잦은 임시 데이터를 매번 처리하면 부하가 커질 수 있다.
  • Redis는 메모리 기반 Key-Value NoSQL로 세션, 캐시, 리더보드, 조회수 등을 관리하는 데 적합하다.
  • 메모리는 빠르지만 데이터가 유실될 수 있다. 따라서 Redis에 저장할 데이터와 장애 발생 시 복구 방법을 함께 설계해야 한다.

Redis 자료형과 용도

타입핵심 명령대표 용도
StringSET/GET, INCR/DECR, MSET/MGET단순 값, 조회수 카운터
ListLPUSH/RPUSH/LPOP/RPOP, LRANGE큐, 스택, 타임라인
SetSADD/SREM, SISMEMBERS, 집합 연산중복 없는 방문, 토큰 블랙리스트
HashHSET/HGET/HGETALL객체 한 건, 사용자별 장바구니
Sorted SetZADD/ZINCRBY/ZREVRANGE리더보드, Rate Limiter

키의 자료형과 맞지 않는 명령을 쓰면 오류가 난다. DEL, EXPIRE(TTL)는 자료형과 관계없이 쓸 수 있다. KEYS *FLUSHDB는 주의해서 사용할 것.

// RedisTemplate 설정 예시
template.setKeySerializer(RedisSerializer.string());
template.setValueSerializer(RedisSerializer.json());

Spring에서 Redis 쓰기

  1. @RedisHash + CrudRepository

    • JPA와 비슷하게 CRUD를 빠르게 만들 수 있다.
    • 저장하면 itemitem:1(Hash + _class) 같은 키가 생성된다.
    • 복잡한 자료형이나 도메인 로직을 다루기에는 한계가 있다.
  2. RedisTemplate / StringRedisTemplate

    • Key/Value 타입과 serializer를 직접 설정한다.
    • opsForValue(), opsForSet(), opsForZSet() 등으로 자료형별 연산을 수행한다.
    • JSON serializer를 적용하면 Redis에 저장된 데이터를 사람이 읽기 쉬운 형태로 유지할 수 있다.

Session Clustering

  • HTTP는 무상태라 쿠키(JSESSIONID)와 서버 세션으로 상태를 유지한다.
  • 서버를 여러 대로 늘렸을 때 로컬 세션만 쓰면 A 서버에 저장한 세션을 B 서버에서 읽지 못한다.
  • Sticky Session: 같은 사용자를 한 서버에 고정한다. 구현은 쉽지만 부하가 쏠리고 서버 장애 시 세션을 잃을 수 있다.
  • Session Clustering: Redis 같은 외부 저장소에 세션을 보관해 여러 서버가 공유한다. 요청 분산과 확장에는 유리하지만 외부 저장소 관리 비용과 네트워크 지연이 추가된다.
  • spring-session-data-redis를 추가하면 세션을 Redis에 저장할 수 있다. JSON 직렬화 시에는 SecurityContext 등의 역직렬화 이슈도 확인해야 한다.

캐싱 전략과 Spring Cache

캐시 동작의 기준은 Cache Hit / Miss, Eviction, TTL(고정 만료), TTI(접근 시 만료 연장)이다.

  • Cache-Aside: 조회 시 캐시를 확인하고, 없으면 원본에서 가져와 캐시에 저장한다. 필요한 데이터만 캐싱할 수 있지만 첫 요청이 느리고 최신 데이터 보장은 어렵다.
  • Write-Through: 쓰기 시 캐시와 원본을 함께 갱신한다. 캐시는 최신 상태를 유지하지만 사용하지 않는 데이터도 갱신할 수 있다.
  • Write-Behind: 캐시에 먼저 쓰고 원본에는 비동기로 저장한다. DB 부하는 줄지만 장애 시 데이터 유실 위험이 있다.
애노테이션동작
@CacheableCache Hit이면 메서드 실행을 건너뛴다. Cache-Aside에 가깝다.
@CachePut항상 메서드를 실행하고 결과를 캐시에 넣는다. Write-Through처럼 쓸 수 있다.
@CacheEvict지정한 캐시를 삭제한다. 목록 캐시 무효화 등에 사용한다.

@EnableCachingRedisCacheManager에서 TTL, null 캐싱 여부, serializer를 설정할 수 있다. 캐시 키는 args[0], #result.id, { args[0], args[1].pageNumber, args[1].pageSize }처럼 구성한다.

캐시를 적용할 때는 성능뿐 아니라 데이터 정합성, 만료 시점, 무효화 시점을 같이 정해야 한다.

실습에 적용해 볼 설계

  • 게시글 조회수: 전체 조회수는 String에 INCR, 로그인 사용자 조회 기록과 인기글은 Set과 Sorted Set 조합
  • 장바구니: Hash에 EXPIRE로 TTL, 수량이 0이면 해당 필드 제거
  • 주문과 상품: 단순 CRUD는 @RedisHash, 인기 상품은 Sorted Set, 주문 저장은 Write-Behind 검토

정리

  • Redis는 단순한 빠른 임시 저장소가 아니라, 적절한 자료형과 명령으로 세션, 캐시, 랭킹처럼 자주 쓰이고 바뀌는 데이터의 성능 문제를 푸는 도구에 가깝다.
  • Spring에서는 Repository로 빠르게 시작하고, 세밀한 제어가 필요하면 RedisTemplate과 Session/Cache 추상화를 쓸 수 있다.
  • 캐시 설계에서는 Hit 비율뿐 아니라 정합성, 만료 정책, 장애 시 유실 가능성까지 같이 봐야 한다.

다음에는 세션 클러스터링, 캐싱, Sorted Set, Write-Behind를 기존 프로젝트에 적용해 실습해 보려 한다.


Edit page