Notice
Recent Posts
Recent Comments
Link
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- data
- 개발자
- goorm
- backend
- 자격증
- QueryDSL
- Docker
- goorm x kakao
- 기본형
- mapping
- 오블완
- spring
- 자바
- 티스토리챌린지
- nosql
- backenddeveloper
- jvm
- Spring Boot
- serverless
- bootcamp
- CodeCommit
- MSA
- aws
- Cache
- 스터디
- java
- jpa
- s3
- Redis
- orm
Archives
- Today
- Total
gony-dev 님의 블로그
[Cache] Spring Boot Cache 본문
이전 포스팅에서는 Cache-Control Header를 이용한 캐싱 기능을 사용해봤다.
이번에는 서버 내부에서 Spring Boot Cache를 이용하여 캐싱 기능을 이용해보도록 하겠다.

Spring Boot Cache vs. Cache-Control Header
과연 둘의 차이는 뭘까?
아래의 표를 통해 알아보자!
| Spring Boot Cache | Cache-Control Header | |
| 위치 | 서버 내부 | 클라이언트 or 중간 캐시 |
| 대상 | 메서드 실행 결과 | HTML 응답 전체 |
| 목적 | 서버 처리 속도 향상 | 네트워크 트래픽 감소 |
| 설정 위치 | Java 코드 | HTTP 응답 헤더 |
| TTL 관리 | Redis TTL 사용 | max-age, no-cache 등의 값으로 결정 |
결론적으로 쓰는 목적과 사용 범위가 다르기 때문에 둘을 비교하기에는 다소 애매하지만
이 둘을 함께 사용한다면 더 큰 캐싱 효과를 발휘할 수도 있다!
1. Spring Boot Cache Annotation
Spring 내부에서 캐싱을 사용하기 위해 사용되는 어노테이션을 알아보자!
| Annotation | 설명 |
| @EnableCaching | Spring Boot Cache를 사용하기 위해 캐시 활성화를 선언하는 어노테이션(Configuarion 클래스에 사용) |
| @CacheConfig | 캐시 정보를 클래스 단위로 사용하고 관리하기 위한 어노테이션 |
| @Cacheable | 캐시 정보를 메모리 상에 저장하거나 조회해오는 기능을 수행하는 어노테이션 |
| @CachePut | 캐시 정보를 메모리 상에 저장하며 존재 시 갱신하는 기능을 수행하는 어노테이션 |
| @CacheEvict | 캐시 정보를 메모리 상에 삭제하는 기능을 수행하는 어노테이션 |
| @Caching | 여러 개의 캐시 어노테이션을 하나의 메서드에 사용할 때 사용하는 어노테이션 |
2. Cache Configuration
본격적으로 Spring 프로젝트에 캐싱을 적용하기 위해 사전 준비를 진행해보자!
1. build.gradle
{ implementation 'org.springframework.boot:spring-boot-starter-cache' }
2. CacheConfig.class
@Configuration @EnableCaching public class CacheConfig { @Bean public CacheManager cacheManager() { ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager(); cacheManager.setAllowNullValues(false); cacheManager.setStoreByValue(false); cacheManager.setCacheNames(List.of("post")); return cacheManager; } }
ConcurrentMapCacheManager를 구성하는 setter 메서드들은 다음과 같다.
메서드 사용 목적 setAllowNullValues(boolean) null 값을 캐시에 저장할 수 있도록 허용할지 여부를 설정한다. setStoreByValue(boolean) 캐시 항목을 복사해서 저장할지 여부를 설정한다. setCacheNames(Collection<String>) 초기화 시 사용할 캐시 이름들을 설정한다. setCacheLoader(Function<String>) 커스텀 캐시 로더를 설정한다. 원하는 방식으로 Cache 객체를 생성 가능 setRemoveCachesAfterAccess(boolean) 사용 직후 캐시를 제거할지 여부를 설정한다. (비사용 권장, 메모리 최적화 목적)
3. 프로젝트 적용하기
내가 프로젝트에 적용하는 플로우는 다음과 같다.
목표 - 메인 페이지에 조회되는 인기 게시글을 캐싱 적용
1. 스케줄링을 이용하여 특정 시점마다 진행되는 메서드 구현
2. 해당 스케줄링 작업 동안 데이터를 캐싱하기
3. 조회 API 호출 시, Spring Boot Cache 어노테이션을 활용하여 불필요한 데이터 호출을 줄이기
1. PostService.class
@Slf4j @Service @RequiredArgsConstructor @CacheConfig(cacheNames = "post") public class PostService { private final PostRepository postRepository; @CachePut(key = "#root.args[0]") @CacheEvict(key = "'postAll'") public Post createPost(PostReqDto reqDto) { Post post = new Post(reqDto); return postRepository.save(post); } @Cacheable(key = "'postAll'") public List<Post> getAllPosts() { log.info("가져오는중"); return postRepository.findAll(); } @Cacheable(key = "#id", unless = "'#result' == null") public Post getPostById(Long id) { return postRepository.findById(id).orElseThrow(RuntimeException::new); } @CachePut(key = "#root.args[0]") @CacheEvict(key = "'postAll'") @Transactional public Post updatePost(Long id, PostReqDto reqDto) { Post post = postRepository.findById(id).orElseThrow(RuntimeException::new); post.updatePost(reqDto); return post; } @Caching(evict = { @CacheEvict(key = "'postAll'"), @CacheEvict(key = "#id") }) public void deletePost(Long id) { Post post = postRepository.findById(id).orElseThrow(RuntimeException::new); postRepository.delete(post); } }
2. PostController.class
@Slf4j @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/post") public class PostController { private final PostService postService; @PostMapping public ResponseEntity<?> createPost(@RequestBody PostReqDto postReqDto) { postService.createPost(postReqDto); return ResponseEntity.ok("Post created successfully"); } @GetMapping public ResponseEntity<?> getAllPosts() { log.info("Get all posts"); return ResponseEntity.ok(postService.getAllPosts()); } @GetMapping("/{id}") public ResponseEntity<?> getPostById(@PathVariable Long id) { return ResponseEntity.ok(postService.getPostById(id)); } @PutMapping("/{id}") public ResponseEntity<?> updatePost(@PathVariable Long id, @RequestBody PostReqDto postReqDto) { log.info("Updating post with id {}", id); postService.updatePost(id, postReqDto); return ResponseEntity.ok("Post updated successfully"); } @DeleteMapping("/{id}") public ResponseEntity<?> deletePost(@PathVariable Long id) { postService.deletePost(id); return ResponseEntity.ok("Post deleted successfully"); } }
4. 검증하기
기존의 캐싱을 사용하지 않은 상태에서 getAllPost()를 반복해서 요청하면 다음과 같이 controller와 service 로직을 반복해서 실행시키는 것을 확인할 수 있다.
“Get all posts”
“가져오는중”
“Get all posts”
“가져오는중”
“Get all posts”
“가져오는중”
하지만 캐싱을 적용한다면 Controller Layer에서 캐싱된 결과만을 조회하는 것을 확인할 수 있다!!

- 이렇게 해서 우리는 Spring Boot Cache를 사용하여
요청이 많지만 데이터는 거의 변경되지 않는 API에 캐싱을 적용하고
무분별한 데이터 접근을 방지하여 트래픽을 줄일 수 있다!
'Caching' 카테고리의 다른 글
| [Cache] Cache-Control Header (3) | 2025.07.24 |
|---|---|
| [Redis] Connection Mode-2 (0) | 2024.12.08 |
| [Redis] Connection Mode-1 (0) | 2024.12.06 |
| [Redis] Spring batch vs. Scheduler (0) | 2024.10.17 |
| [Redis] Transaction (1) | 2024.10.15 |