자바 스프링 부트 개념 정리(Cache)

제이 용·2025년 12월 18일

캐시(Cache)란?

자주 사용하는 데이터를 메모리(In-memory)에 임시로 저장해,

DB나 외부 API 호출 없이 빠르게 응답하기 위한 기술이다.


캐시의 핵심 목적

  • 응답 속도 향상

  • DB 부하 감소

  • 트래픽 및 외부 API 비용 절감

  • 캐시 히트(Hit) 시에는 메모리에서 바로 응답하므로 매우 빠름.

  • 캐시 미스(Miss) 시에만 DB를 조회한다.


캐시 기본 구조

요청(Request)
   ↓
[ Cache ] → HIT → 즉시 응답
   ↓
 MISS
   ↓
[ DB ] → 조회 후 캐시에 저장 → 응답

처음 요청은 느리지만(Cache Miss),

이후 요청은 빠르다(Cache Hit)


DB와 캐시의 역할 차이

구분DBCache
목적정확성, 정합성속도, 효율
저장영구 저장임시 저장
속도상대적으로 느림매우 빠름
데이터항상 최신최신 보장 X
  • DB는 신뢰성 중심,
  • 캐시는 성능 최적화를 위한 보조 수단

실무에서는 DB + Cache를 함께 사용하는 구조가 기본이다.


비유로 이해하기

  • DB = 책상 서랍
    → 안전하지만 꺼내는 데 시간이 걸림

  • Cache = 책상 위
    → 바로 사용 가능하지만 오래되면 다시 확인 필요

실무 시스템은 “서랍에서 꺼내 자주 쓰는 것을 책상 위에 올려두는 구조”


Cache Hit / Miss

용어설명성능
Cache Hit캐시에 데이터 존재매우 빠름
Cache Miss캐시에 없음 → DB 조회느림 (1회성)
  • 예시

    • DB 응답: 약 50ms

    • Cache 응답: 1~3ms
      ➡ 약 15~50배 성능 차이


캐시가 필요한 이유

  • 캐시가 없을 때

    • 반복적인 DB 조회

    • 외부 API 호출 지연

    • 트래픽 증가 시 서버 병목 발생

  • 캐시 도입 효과

    • 응답 속도 대폭 향상

    • DB 및 외부 API 부하 감소

    • 대량 트래픽 대응 가능

특히 조회(Read)가 많은 API에서 효과가 크다.


캐시 유형

로컬 캐시 : 애플리케이션 내부 메모리 [ConcurrentHashMap, Caffeine]
분산 캐시 : 외부 서버에서 공유 [Redis, Memcached]

  • 로컬 캐시

    • 매우 빠름

    • 서버 간 데이터 공유 불가

  • 분산 캐시

    • 네트워크 비용 존재

    • 여러 서버에서 캐시 공유 가능


캐시 전략(Cache Strategy)

  • 대표 전략
전략설명특징
Write-throughDB와 캐시 동시 갱신일관성 ↑
Write-back캐시에 먼저 쓰고 나중에 DB 반영성능 ↑, 위험 ↑
Cache-aside요청 시 캐시 확인 후 DB 조회가장 일반적
Cache Invalidation데이터 변경 시 캐시 삭제일관성 핵심
  • Cache-aside 흐름
요청 → 캐시 확인
   ↓
없음 → DB 조회
   ↓
캐시에 저장 (TTL 설정)
   ↓
응답 반환

장점: 단순하고 범용적

단점: 최초 요청은 느림


캐시 일관성 문제

  • 문제 상황

    • DB는 최신인데, 캐시는 이전 데이터
  • 주요 원인

    • TTL이 너무 김

    • DB 업데이트 시 캐시 무효화 누락

캐시의 가장 큰 기술적 과제는 “성능과 정확성 사이의 균형”


TTL & Eviction Policy

  • TTL (Time-To-Live)

    • 캐시 데이터의 유효 시간

    • 만료 시 자동 삭제

SET key value EX 300   # 300초 유지
  • Eviction Policy (삭제 정책)

    • LRU: 최근 사용 안 한 데이터 제거

    • LFU: 사용 빈도 낮은 데이터 제거

    • FIFO: 먼저 들어온 데이터 제거

  • 실무 캐시 활용 사례

서비스캐시 대상특징
뉴스 메인인기 기사 목록짧은 TTL
상품 상세상품 정보DB 접근 최소화
환율 조회외부 API 응답주기적 갱신
가게 리스트지역별 리스트Key 분리
  • 캐싱 설계 시 주의점

    • 캐시는 DB의 보조 수단

    • TTL은 너무 짧아도, 길어도 문제

    • 변경 잦은 데이터보다 조회 많은 데이터에 적용

    • 캐시 Key 설계 명확히

user:42
post:1001
  • 캐시 장애 시 DB fallback 처리 필수

요약

캐시는 “정확성을 희생하지 않는 선에서 성능을 극대화하기 위한 도구”이며,

읽기 많은 시스템에서 필수적인 인프라이다.


Spring Cache 개념과 사용법 정리

Spring Cache

Spring이 제공하는 캐싱 추상화(Cache Abstraction) 기능으로,

캐시 로직을 직접 구현하지 않고 어노테이션 기반으로 캐시를 제어할 수 있게 해준다.

  • 핵심 포인트

    • 캐시 구현체와 비즈니스 로직 분리

    • 동일한 코드로 로컬 캐시 ↔ Redis 전환 가능

    • 캐시 접근 로직을 AOP 방식으로 처리

  • 구조 개념

@Cacheable / @CacheEvict / @CachePut
        ↓
[ Spring Cache Abstraction ]
        ↓
CacheManager
        ↓
Cache 구현체 (Caffeine, Redis 등)

캐시 기술이 바뀌어도 서비스 코드는 그대로 유지

기본 설정

  • 의존성 추가 (Caffeine 로컬 캐시 예시)
implementation 'org.springframework.boot:spring-boot-starter-cache'
implementation 'com.github.ben-manes.caffeine:caffeine'
  • 캐시 활성화
@SpringBootApplication
@EnableCaching // 캐시 기능 활성화
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
  • @EnableCaching 없으면
    → 어노테이션이 있어도 캐시 동작 안 함

  • 캐시 설정 (application.yml)

spring:
  cache:
    caffeine:
      spec: maximumSize=100,expireAfterWrite=10s
  • 최대 캐시 엔트리: 100개

  • 데이터 생성 후 10초 뒤 만료


TTL 동작 시나리오 이해

시점동작상태
0초첫 조회DB 조회 + 캐시 저장
5초재조회Cache HIT
10초TTL 만료 도달아직 삭제 X
11초재조회캐시 만료 → DB 재조회
  • expireAfterWrite 기준

    • 조회를 계속해도 TTL은 연장되지 않음

    • 만료 후 접근 시 새 캐시 생성


@Cacheable — 조회 결과 캐싱

@Cacheable(value = "postCache", key = "#postId")
public PostDto getPostById(long postId) {
    log.info("캐시에 없으니 DB에서 직접 조회");
    Post post = postRepository.findById(postId)
        .orElseThrow(() -> new IllegalArgumentException("등록된 포스트가 없습니다."));
    return PostDto.from(post);
}
  • 동작 방식

    • 첫 호출 → DB 조회 후 캐시에 저장

    • 동일 파라미터 재호출 → DB 접근 없이 캐시 반환

두 번째 호출부터는 로그조차 찍히지 않음 = 캐시 정상 동작


@Cacheable의 value & key 구조

  • value (Cache Name)

    • 캐시 저장소의 이름

    • 하나의 캐시 그룹(namespace)

@Cacheable(value = "postCache", key = "#postId")

"postCache"라는 캐시 영역 생성

  • key (Entry Key)

    • 캐시 내부에서 데이터를 구분하는 식별자

    • 메서드 파라미터 기반으로 생성


Cache name: postCache
Key: 1
Value: PostDto(...)
  • 전체 캐시 구조 예시
CacheManager
 ┣━━ userCache
 │     ┗━━ key: 1 → UserDto
 ┣━━ postCache
 │     ┣━━ key: 10 → PostDto
 │     ┗━━ key: 11 → PostDto
 ┗━━ commentCache
       ┗━━ key: 101 → CommentDto

value = 캐비닛 이름, key = 서류 번호


value & key 설계 기준

  • 같은 도메인은 같은 value 사용
@Cacheable(value = "postCache", key = "#postId")
@Cacheable(value = "postCache", key = "#username")

같은 캐시 그룹, 다른 key

  • key 충돌 주의

postCache::1
postCache::"1"

username이 "1"이면 충돌 가능성 발생

  • 해결 방법: prefix 사용
@Cacheable(value = "postCache", key = "'id:' + #postId")
@Cacheable(value = "postCache", key = "'username:' + #username")
  • 결과
postCache::id:1
postCache::username:kim

캐시 Key 네이밍 규칙

  • 기본 원칙

    • 고유하고 일관된 형식

    • 도메인 중심 설계

user:1
post:1001
search:keyword:page
  • 실무 팁

    • : 또는 :: 구분자 사용

    • Redis 사용 시 prefix 기반 관리

    • 운영 환경에서는 짧지만 의미 명확하게


@CacheEvict — 캐시 삭제

@CacheEvict(value = "postCache", key = "#postId")
public void deletePost(long postId) {
    // 게시글 삭제 로직
}
  • 사용 시점

    • 삭제

    • 상태 변경

    • 더 이상 캐시가 유효하지 않을 때

@CachePut — 캐시 갱신

@CachePut(value = "postCache", key = "#postId")
public PostDto updatePost(long postId) {
    // 게시글 수정 로직
}
  • 특징

    • 항상 메서드 실행

    • 실행 결과를 캐시에 강제로 갱신

    • 조회용(@Cacheable)과 역할 분리

요약

Spring Cache는 캐시 구현체와 비즈니스 로직을 분리해

어노테이션만으로 일관성 있고 안전한 캐싱을 가능하게 해준다.


Redis

Redis(Remote Dictionary Server) 는

모든 데이터를 메모리(RAM)에 저장하는 초고속 Key-Value 데이터베이스이다.

  • 디스크가 아닌 메모리 기반 → 매우 빠른 읽기/쓰기

  • 주 용도: 캐시, 세션, 실시간 데이터 처리

실무에서는 보통

DB의 보조 저장소(Cache Server) 로 사용된다.


RDB vs Key-Value DB (Redis)

구분RDB (MySQL 등)Redis
데이터 구조테이블 (행/열)Key - Value
조회 방식SQL (복잡한 조건 가능)Key 기반 단일 조회
저장 위치디스크메모리
속도상대적으로 느림매우 빠름
용도영구 데이터 저장캐시, 세션, 실시간 처리

DB = 정확성 / Redis = 속도


Redis가 빠른 이유

항목디스크 기반 DBRedis
데이터 위치디스크메모리
접근 방식파일 I/OCPU 직접 접근
응답 속도ms 단위μs 단위
안정성매우 높음서버 종료 시 휘발

Redis는디스크 I/O가 없기 때문에 압도적으로 빠르다.


Redis의 주요 특징

특징설명
In-memory모든 데이터를 메모리에 저장
Key-Value단순하고 빠른 구조
자료구조 제공String, List, Set, Hash, ZSet 등
TTL 지원Key 단위 만료 시간 설정 가능

단순 캐시를 넘어 자료구조 서버 역할까지 수행 가능


Redis 자료형과 활용 사례

  • String

    • 가장 기본적인 타입

    • 문자열, 숫자 모두 저장 가능

  • 활용

    • 게시글/상품 캐싱

    • 로그인 토큰

    • 조회수, 좋아요 수 카운팅

set viewCount 10
incr viewCount

  • List

    • 순서가 있는 데이터 구조

    • Queue / Stack 용도로 활용 가능

  • 활용

    • 실시간 채팅 메시지

    • 최근 본 상품 목록

    • 주문 대기열

lpush recent_posts "post1"
rpush recent_posts "post2"
lrange recent_posts 0 -1

  • Set

    • 중복 없는 집합
  • 활용

    • 좋아요 누른 사용자 목록

    • 태그 관리

    • 팔로워 / 팔로잉 관계

sadd tags "spring"
sadd tags "redis"
smembers tags

  • Hash

    • 하나의 Key에 여러 필드-값 저장 (Map 구조)
  • 활용

    • 사용자 정보 캐싱

    • 상품 상세 정보

    • 로그인 세션 데이터

hset user:1 name "Ravi" age 30
hgetall user:1

RDB의 한 Row를 Redis Hash로 표현하는 경우가 많다.

  • Sorted Set (ZSet)

    • score 기반 자동 정렬
  • 활용

    • 랭킹 시스템

    • 인기 게시글 순위

    • 실시간 순위 갱신

zadd ranking 100 "ravi"
zrevrange ranking 0 -1 WITHSCORES

Redis 데이터 영속성 (Persistence)

Redis는 기본적으로 휘발성이지만,

옵션을 통해 디스크에 저장 가능하다.

방식설명특징
RDB주기적으로 전체 스냅샷 저장빠름, 백업 용도
AOF실행 명령어를 순차 기록안정성 높음
HybridRDB + AOF 혼합Redis 7.x 기본

그래도 RDB만큼의 안정성은 아님

Redis 사용 시 주의사항

  • Redis는 주 저장소가 아니다

  • 메모리 기반 → 장애 시 데이터 유실 가능

  • 반드시 DB와 함께 사용해야 안정적

  • 이상적인 구조

  • 원본 데이터 : MySQL, PostgreSQL

  • 빠른 접근 : Redis

  • 조합 : DB는 진실, Redis는 복사본


요약

Redis는 초고속 메모리 기반 Key-Value 저장소로,

DB의 부하를 줄이고 실시간 처리를 가능하게 해주는 캐시 서버이다.


RedisTemplate과 Cache-Aside 패턴 정리

RedisTemplate

Spring이 제공하는 Redis 전용 클라이언트로,

Java 코드로 Redis 명령어를 실행할 수 있게 해주는 도구이다.

즉,

우리는 Java 코드로 set, get을 호출하고

RedisTemplate이 이를 Redis 명령어(SET, GET 등) 로 변환해 서버에 전달한다.

Redis에서 받은 결과는 다시 Java 객체로 변환되어 반환된다.


RedisTemplate 동작 흐름

[ Java 코드 ]
   "user:1 = Ravi" 저장 요청
        ↓
[ RedisTemplate ]
   SET user:1 "Ravi"
        ↓
[ Redis 서버 ]
   메모리에 저장
        ↓
[ 결과 반환 ]

Redis 명령어를 직접 쓰지 않아도 되는 이유


간단한 예제

redisTemplate.opsForValue().set("user:1", "Ravi");
String name = (String) redisTemplate.opsForValue().get("user:1");
System.out.println(name); // Ravi
  • 내부적으로 실행되는 Redis 명령:
SET user:1 "Ravi"
GET user:1
  • opsForValue()
    → Redis의 String 타입을 다루는 도우미 객체

RedisTemplate 주요 역할 정리

메서드Redis 자료형Redis 명령
opsForValue()StringSET / GET
opsForHash()HashHSET / HGET
opsForList()ListLPUSH / LRANGE
opsForSet()SetSADD / SMEMBERS
opsForZSet()Sorted SetZADD / ZRANGE

Redis의 모든 자료구조를 Java API로 다룰 수 있음


TTL(Time To Live) 설정

redisTemplate.opsForValue()
    .set("temp:data", "123", 10, TimeUnit.MINUTES);
  • Redis 내부 명령:
SET temp:data "123" EX 600
  • TTL이 지나면 자동 삭제
    ➡ 캐시 데이터 최신성 유지

Cache-Aside 패턴 (읽기 캐싱)

읽을 때는 캐시를 먼저 확인하고,

없으면 DB에서 조회한 뒤 캐시에 저장하는 방식이다.

  • 기본 원칙

    • Read: 캐시 → DB → 캐시 저장

    • Write: DB 변경 후 캐시 삭제(Evict)

  • 캐시 없는 버전

public Post getPost(Long postId) {
    return postRepository.findById(postId)
        .orElseThrow(() -> new RuntimeException("Post not found"));
}
  • Redis 적용 버전 (Cache-Aside)
public Post getPost(Long postId) {
    String key = PREFIX + postId;

    // 1. 캐시 확인
    Post cached = (Post) redisTemplate.opsForValue().get(key);
    if (cached != null) {
        System.out.println("Cache Hit");
        return cached;
    }

    // 2. 캐시 미스 → DB 조회
    Post post = postRepository.findById(postId)
        .orElseThrow(() -> new RuntimeException("Post not found"));

    // 3. 캐시에 저장
    redisTemplate.opsForValue().set(key, post, 10, TimeUnit.MINUTES);
    return post;
}
  • 핵심 포인트

    • 캐시에 있으면 DB 접근 ❌

    • 없을 때만 DB 조회

    • 조회 결과를 캐시에 저장


Redis 캐시 Key 네이밍 전략

Redis는 모든 데이터가 Key 기반이기 때문에

Key 설계가 곧 운영 안정성이다.

  • 기본 규칙
{도메인}:{리소스}:{식별자}
  • 예시
용도Key 예시설명
사용자 정보user:123userId=123
게시글post:987postId=987
사용자 피드feed:user:123특정 사용자 피드
댓글 리스트comment:post:987게시글 댓글
랭킹ranking:game:weekly주간 랭킹
  • : 로 계층 구조 표현

  • 고정 prefix로 역할 구분

  • Key만 봐도 의미 파악 가능하게

  • 코드에서는 상수로 관리

private static final String USER_CACHE_PREFIX = "user:";
private static final String POST_CACHE_PREFIX = "post:";

TTL 설계 전략

  • TTL은 캐시의 생명주기다.

  • 예시

redisTemplate.opsForValue()
    .set("post:1", post, 10, TimeUnit.MINUTES);
  • 10분 후 자동 만료

  • 데이터별 TTL 예시
    | 데이터 | TTL | 이유 |
    | ------- | ------ | ------ |
    | 사용자 프로필 | 1시간~1일 | 변경 적음 |
    | 게시글 상세 | 5~30분 | 수정 가능성 |
    | 실시간 인기글 | 1~5분 | 빠른 변동 |
    | 로그인 세션 | 10~60분 | 보안 |
    | 통계/대시보드 | 5분 이내 | 최신성 중요 |

  • TTL 설계 팁

    • 변경 주기 ⬆ → TTL 짧게

    • 조회 빈도 ⬆ & 변경 적음 → 캐싱 효율 ↑

    • TTL 너무 짧음 ❌ → 캐시 효과 감소

    • TTL 너무 김 ❌ → 데이터 불일치 위험

요약

RedisTemplate은 Java 코드로 Redis 자료구조와 명령을 다룰 수 있게 해주는 도구이며,

Cache-Aside 패턴과 함께 사용하면 읽기 성능을 크게 향상시킬 수 있다.


Spring Boot와 Redis 연동 실습 정리

Spring Boot + Redis 연동 목적

  • Redis를 캐시 서버로 사용해 조회 성능 개선

    • DB 부하 감소

    • 실무에서 가장 많이 쓰이는 Cache-Aside 패턴 직접 구현


의존성 추가

// Redis 연동
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
  • spring-boot-starter-data-redis

    • RedisConnectionFactory

    • RedisTemplate

    • Lettuce 클라이언트 기본 제공


RedisTemplate 설정 (중요 ⭐)

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(
            RedisConnectionFactory connectionFactory
    ) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        // Key는 문자열
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());

        // Value는 JSON (LocalDateTime 대응)
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

        template.afterPropertiesSet();
        return template;
    }
}
  • 설정 이유

  • StringRedisSerializer : Redis Key를 사람이 읽을 수 있게

  • GenericJackson2JsonRedisSerializer : 객체(JSON) 직렬화 + LocalDateTime 대응

  • Object 타입 : 다양한 도메인 객체 캐싱 가능

  • 이 설정 없으면
    → 직렬화 깨짐 / LocalDateTime 에러 자주 발생


Cache-Aside 기본 캐싱

  • 핵심 개념

    • 읽기(Read): 캐시 → DB → 캐시 저장

    • 쓰기(Write): DB 수정 후 캐시 삭제

  • 게시글 조회 흐름

[Client]
  ↓
[Controller]
  ↓
[Service]
  ↓
1. Redis 캐시 조회
  ↓
2. Cache Miss → DB 조회
  ↓
3. Redis 캐시에 저장
  ↓
4. 응답 반환
  • 게시글 수정(업데이트) 흐름
[Client PUT 요청]
  ↓
1. DB 데이터 수정
  ↓
2. Redis 캐시 삭제 (Invalidate)

캐시는 항상 DB보다 늦게 믿는다

  • 실무 포인트

수정 시 캐시를 “갱신”보다 삭제(Evict) 하는 것이 안전

다음 조회 시 최신 데이터로 재캐싱


조회수 기반 인기 게시글 (Sorted Set)

  • 왜 ZSet을 쓸까?

    • 자동 정렬

    • 점수(score) 기반 순위 관리

    • 실시간 랭킹에 최적화

  • 게시글 조회 시 흐름

[게시글 조회 요청]
   ↓
1. Redis 캐시 조회
   ↓
2. Cache Miss → DB 조회 후 캐시 저장
   ↓
3. ZSet 조회수 +1 (ZINCRBY)
   ↓
4. 게시글 응답
  • 조회수는 DB에 바로 반영하지 않고
    → Redis에서 실시간 집계

  • 인기 게시글 조회 흐름

[인기글 조회 요청]
   ↓
1. ZSet에서 상위 N개 postId 조회 (ZREVRANGE)
   ↓
2. postId 기반 캐시 조회
   ↓
3. 캐시 없으면 DB 조회 후 Redis 저장
   ↓
4. 조회수 순서대로 PostDto 반환
  • 이 구조의 장점

    • 조회수 증가 = Redis 연산 (초고속)

    • 인기글 조회 시 DB 정렬 ❌

    • 대규모 트래픽에서도 성능 유지


전체 구조 요약

  • 역할 분리
    • 원본 데이터 : RDB (MySQL 등)
    • 빠른 조회 : Redis (Cache)
    • 실시간 랭킹 : Redis Sorted Set

DB는 진실, Redis는 가속기


실무 관점에서 배울 점

  • RedisTemplate은 저수준 제어에 적합

  • Spring Cache(@Cacheable)는 선언적 캐싱

  • 랭킹, 카운팅은 Redis 자료구조가 압도적으로 유리

  • 캐시 설계는 “어디까지 Redis를 믿을지” 결정하는 작업


요약

Spring Boot에서 Redis를 연동해 Cache-Aside 패턴과 Sorted Set을 활용하면,

조회 성능 개선과 실시간 랭킹을 동시에 만족하는 구조를 만들 수 있다.


공부할 양이.. 너무 많다..(;′⌒`)

2개의 댓글

comment-user-thumbnail
2025년 12월 18일

(;′⌒`) 괜찮아요 ! 저는 오늘 순공시간이 세시간 반이였는걸요!

1개의 답글