DevOps/Redis

[Redis] @Cacheable을 이용한 Cache Aside 전략

ryuneng 2025. 1. 25. 00:04
반응형

Cache Aside 전략이란?
🔗 https://velog.io/@ryuneng2/Redis-캐시Cache-캐싱Caching이란

❓ @Cacheable

  • @Cacheable 어노테이션을 부착하면 Cache Aside 전략으로 캐싱이 적용된다.
    즉, 해당 메서드로 요청이 들어오면 레디스를 먼저 확인한 후에 데이터가 있다면(Cache Hit) 레디스의 데이터를 조회해서 바로 응답한다.
    만약 데이터가 없다면(Cache Miss) 메서드 내부의 로직을 실행시킨 뒤에 return 값으로 응답한 후, 그 return 값을 레디스에 저장한다.

✅ @Cacheable의 속성 값

  • cacheNames : 캐시 이름 설정
  • key : Redis에 저장할 Key의 이름 설정
  • cacheManager : 사용할 cacheManager의 Bean 이름을 지정
    (RedisCacheConfig에 CacheManager 타입으로 정의한 메서드명과 일치해야 함)



💡 사용 예시

1. build.gradle 의존성 추가

dependencies {
    // redis 추가
    implementation 'org.springframework.boot:spring-boot-starter-data-redis'
}

2. application.yml 설정

spring:
  data:
    redis:
      host: ${REDIS_HOST}
      port: ${REDIS_PORT}

logging:
  level:
    org.springframework.cache: trace # redis와 관련된 정보가 로그에 출력되도록 설정

3. RedisConfig 작성

@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));
    }
}

4. RedisCacheConfig 작성

@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();
    }
}

5. Service 클래스

@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();
    }
}

6. Controller 클래스

@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 전략으로 캐싱이 적용되는 과정을 확인할 수 있다.

1. API 요청

2. 로그 확인

  • 1) Cache Miss 상태
    No cache entry for key 'boards:page:1:size:10' in cache(s) [getBoards]
  • 2) 원래 로직에서 데이터베이스 조회하는 SQL문이 실행된다.
    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
  • 3) 데이터베이스에서 클라이언트가 응답받은 데이터를 캐시(Redis)에 저장한다.
    Creating cache entry for key 'boards:page:1:size:10' in cache(s) [getBoards]

3. 만료시간 이내에 API 재요청 (브라우저 새로고침)

4. 로그 확인

  • Cache entry for key 'boards:page:1:size:10' found in cache(s) [getBoards]
    : 캐시에 저장된 데이터를 조회해서 반환했다는 의미이다.

📌 저장된 데이터 확인

  • 1) cmd 실행 및 redis-cli 명령어 입력
    혹은 Redis를 설치한 경로를 열어 redis-cli.exe를 실행
  • 2) keys * 명령어 입력 - Key가 잘 저장되었는지 확인
  • 3) get [key] 명령어 입력 - 해당 Key에 저장된 데이터 조회
  • 4) ttl [key] 명령어 입력 - 해당 Key의 만료시간 확인



Reference

 


< 해당 글은 velog에서 이전하며 옮겨온 글로, 가독성이 좋지 않을 수 있는 점 양해 부탁드립니다. >

🔗 velog 버전 보기 : https://velog.io/@ryuneng2/Redis-Cacheable을-이용한-Cache-Aside-전략