Cache Aside 전략이란?
🔗 https://velog.io/@ryuneng2/Redis-캐시Cache-캐싱Caching이란
@Cacheable
어노테이션을 부착하면 Cache Aside 전략으로 캐싱이 적용된다.Cache Hit
) 레디스의 데이터를 조회해서 바로 응답한다.Cache Miss
) 메서드 내부의 로직을 실행시킨 뒤에 return 값으로 응답한 후, 그 return 값을 레디스에 저장한다.cacheManager
의 Bean 이름을 지정dependencies {
// redis 추가
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
}
spring:
data:
redis:
host: ${REDIS_HOST}
port: ${REDIS_PORT}
logging:
level:
org.springframework.cache: trace # redis와 관련된 정보가 로그에 출력되도록 설정
@Configuration
public class RedisConfig {
@Value("${spring.data.redis.host}")
private String host;
@Value("${spring.data.redis.port}")
private int port;
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
// Lettuce라는 라이브러리를 활용해 Redis 연결을 관리하는 객체를 생성하고
// Redis 서버에 대한 정보(host, port)를 설정한다.
return new LettuceConnectionFactory(new RedisStandaloneConfiguration(host, port));
}
}
@Configuration
@EnableCaching // SpringBoot의 캐싱 설정을 활성화
public class RedisCacheConfig {
@Bean
public CacheManager boardCacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration
.defaultCacheConfig()
// Redis에 Key를 저장할 때 String으로 직렬화(변환)해서 저장
.serializeKeysWith(
RedisSerializationContext.SerializationPair.fromSerializer(
new StringRedisSerializer()))
// Redis에 Value를 저장할 때 Json으로 직렬화(변환)해서 저장
.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(
new GenericJackson2JsonRedisSerializer()
)
)
// 데이터의 만료기간(TTL) 설정
.entryTtl(Duration.ofMinutes(1L)); // 1분마다 데이터 갱신
return RedisCacheManager
.RedisCacheManagerBuilder
.fromConnectionFactory(redisConnectionFactory)
.cacheDefaults(redisCacheConfiguration)
.build();
}
}
@Service
public class BoardService {
private final BoardRepository boardRepository;
public BoardService(BoardRepository boardRepository) {
this.boardRepository = boardRepository;
}
@Cacheable(cacheNames = "getBoards", key = "'boards:page:' + #page + ':size:' + #size", cacheManager = "boardCacheManager")
public List<Board> getBoards(int page, int size) {
Pageable pageable = PageRequest.of(page - 1, size);
Page<Board> pageOfBoards = boardRepository.findAllByOrderByCreatedAtDesc(pageable);
return pageOfBoards.getContent();
}
}
@RestController
@RequestMapping("boards")
public class BoardController {
private final BoardService boardService;
public BoardController(BoardService boardService) {
this.boardService = boardService;
}
@GetMapping()
public List<Board> getBoards(@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size
) {
return boardService.getBoards(page, size);
}
}
이외에도 기본적으로 Board 엔티티 클래스, BoardRepository 클래스는 필요하다.
API를 요청하면 로그에서 Cache Aside 전략으로 캐싱이 적용되는 과정을 확인할 수 있다.
No cache entry for key 'boards:page:1:size:10' in cache(s) [getBoards]
select b1_0.id,b1_0.content,b1_0.created_at,b1_0.title from boards b1_0 order by b1_0.created_at desc limit ?
select count(b1_0.id) from boards b1_0
Creating cache entry for key 'boards:page:1:size:10' in cache(s) [getBoards]
Cache entry for key 'boards:page:1:size:10' found in cache(s) [getBoards]
redis-cli
명령어 입력keys *
명령어 입력 - Key가 잘 저장되었는지 확인get [key]
명령어 입력 - 해당 Key에 저장된 데이터 조회ttl [key]
명령어 입력 - 해당 Key의 만료시간 확인Reference