inblog logo
|
LifeLog, DevLog
    Redis

    Spring Cache

    + Caffeine 로컬 캐시
    KYJTHEYJ's avatar
    KYJTHEYJ
    Apr 02, 2026
    Spring Cache
    Contents
    Spring CacheCache 사용 셋업Gradle 추가Application 추가Application.yml 추가CacheManager 로 설정캐시 사용해보기@Cacheable, @CacheEvict, @CachePut, 사용 구조

    Spring Cache

    Spring이 제공하는 캐시 추상화

    → 어노테이션으로 처리할 수 있도록 마련됨

    → 캐시 구현체를 마련해주니 다양한 캐시로 연결이 가능

    Cache 사용 셋업

    Gradle 추가

    // Spring Cache
    implementation 'org.springframework.boot:spring-boot-starter-cache' 
    
    // caffeine local cache
    implementation 'com.github.ben-manes.caffeine:caffeine' 
    

    Application 추가

    @SpringBootApplication
    @EnableCaching // 캐시 기능 활성화 -> 없으면 캐시 활성화 안됨
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }

    Application.yml 추가

    spring:
      cache:
        caffeine:
          spec: maximumSize=100,expireAfterWrite=10s
    
    # 최대 100개 캐시 유지, 10초 후 만료
    
    # 테스트 시나리오
    # 0초 -> 조회 시작 (DB 조회 + 캐시 저장)
    # 5초 -> 재조회 (캐시 HIT)
    # 10초 이후 -> 만료
    # 11초 -> 다시 조회 (만료 감지 + DB 조회 + 캐시 저장)
    

    CacheManager 로 설정

    위와 동일한 설정이지만 따로 CacheManager 로도 설정이 가능

    @Configuration
    @EnableCaching
    public class CaffeineCacheConfig {
    
        @Bean
        public CacheManager caffeineCacheManager() {
            CaffeineCacheManager cacheManager = new CaffeineCacheManager();
    
            cacheManager.setCaffeine(
                Caffeine.newBuilder()
                    .expireAfterWrite(10, TimeUnit.SECONDS) // TTL 10초
                    .maximumSize(100) // 최대 1000개
            );
    
            return cacheManager;
        }
    }
    

    💡

    Caffeine 공식 문서

    https://github.com/ben-manes/caffeine

    캐시 사용해보기

    @Cacheable(value = "postCache", key = "'postId:' + #postId")
    // 실제 캐시 저장 형태 (Cache name : postCache, Key : 1, Value : PostDto 객체 내용)
    // postCache::postId:1
    public PostDto getPostById(long postId) {
        // 1 단계 : postId 기준으로 캐시에 값이 있는가? -> @Cacheable 이 처리해줌
        // 2 단계 : 값이 있으면 바로 리턴 -> @Cacheable 이 처리해줌
        // 3 단계 : 값이 없으면 DB 조회  -> 실제 구현 필요
        // 4 단계 : 조회된 값을 캐시에 저장 -> @Cacheable 이 처리해줌
        log.info("cache MISS , DB 조회");
        Post post = postRepository.findById(postId).orElseThrow(() -> new IllegalArgumentException("없는 게시글 입니다"));
    
        return PostDto.from(post);
    }
    
    // Post 삭제시 캐시에서도 삭제    
    @CacheEvict(value = "postCache", key = "#postId")
    public void deletePost(long postId) {
    		postRepository.deleteById(postId);
    }    
    
    // Post 업데이트시 캐시에서도 업데이트
    @CachePut(value = "postCache", key = "#postId")
    public PostDto updatePost(long postId, updatePostRequest request) {
    		Post post = postRepository.findById(postId).orElseThrow(() -> new IllegalArgumentException("없는 게시글 입니다"));
    		
    		post.update(request);
    		
    		postRepository.save(post);
    		
    		return PostDto.from(post)
    }
    

    image.png
    • 첫 조회 시는 캐시에 데이터가 없으므로 조회를 한 후 캐시에 저장함

    • 캐시 만료 시간 동안 그 다음 부터는 조회를 해도 캐시에서 가져오므로 DB 조회를 하지 않음

    • 같은 클래스 내부에서 어노테이션이 적용된 메서드로 호출은 Self-Invocation 으로 @Cacheable이 작동하지 않음 (AOP, @Transactional 과 동일한 문제)

    @Cacheable, @CacheEvict, @CachePut, 사용 구조

    (value, key) 의 구조를 띄워 사용

    • value → 캐시 저장소에 어디에

    • key → 무엇을 어떤 이름으로 저장할지 (이 때 메서드의 파라미터를 # 키워드를 통해 불러 붙일 수 있음)

    @Cacheable (value = “postCache”, key = “#postId”) 을 예시로 보면 postCache 란 곳에 postId 의 값을 이름으로 하여 저장하라는 뜻

    value 는 cacheNames 라고 지칭하여 사용되기도 함

    @Cacheable

    메서드 실행 전 캐시를 먼저 확인하고 없으면 DB 조회 후 저장

    @Cacheable(cacheNames = "products", key = "#id") // (value == cacheNames)
    @Transactional(readOnly = true)
    public ProductResponse getProduct(Long id) {
        return productRepository.findById(id).orElseThrow();
    }
    

    @CacheEvict

    캐시를 삭제, 데이터가 수정/삭제 될 경우에 사용

    // 특정 키만 삭제
    @CacheEvict(cacheNames = "products", key = "#id")
    @Transactional
    public void updateProduct(Long id, ProductUpdateRequest request) {
        Product product = productRepository.findById(id).orElseThrow();
        product.update(request);
    }
    
    // 해당 캐시 전체 삭제
    @CacheEvict(cacheNames = "products", allEntries = true)
    @Transactional
    public void createProduct(ProductCreateRequest request) {
        productRepository.save(Product.register(request));
    }
    

    @CachePut

    항상 DB를 조회 후 캐시 갱신, 캐시를 최신 상태로 유지

    // 항상 DB 조회 + 캐시 갱신
    // @Cacheable 과 달리 캐시 히트해도 DB 조회함
    @CachePut(cacheNames = "products", key = "#id")
    @Transactional
    public ProductResponse updateAndRefresh(Long id, ProductUpdateRequest request) {
        Product product = productRepository.findById(id).orElseThrow();
        product.update(request);
        return ProductResponse.from(product);
    }
    
    Share article
    Contents
    Spring CacheCache 사용 셋업Gradle 추가Application 추가Application.yml 추가CacheManager 로 설정캐시 사용해보기@Cacheable, @CacheEvict, @CachePut, 사용 구조

    LifeLog, DevLog - https://github.com/KYJTHEYJ

    RSS·Powered by Inblog