redis cache 적용 - cache config

cochocho·2026년 4월 21일

Configuration의 추가

component의 경우 해당 클래스를 빈으로 등록해 관리하는 것을 의미한다
반면 configuration은, 빈을 만드는 설정 클래스이다
해당 클래스의 메서드를 통해 빈을 생성할 수 있음을 의미한다
@Configuration

둘의 가장 큰 차이는 프록시의 사용에 관한 것인데
밑과 같이 코드를 구성하게 될 경우
메서드 b를 통해 생성되는 객체와 상관없이 메서드 A에 의해 B가 새로 생성되어 싱글턴 패턴이 무너질 수 있는 문제가 발생 가능하다

따라서 이를 극복하기 위해 proxy 객체를 대입하고 실제 사용 시점에 b 메서드로 생성되어있는 B를 사용함으로서 보장해주게 되는 것이다

@Configuration
public class Config {

    @Bean
    public A a() {
        return new A(b()); // 여기서 b() 호출
    }

    @Bean
    public B b() {
        return new B();
    }
}

*caching 시에도 aop를 활용한 proxy가 쓰이는데 이는 해당 조회 요청 시 서비스 메서드 로직을 타지 않고 먼저 캐싱 데이터를 활용할 수 있는지 확인하는 것에 있다

그렇다면 component 를 활용할 경우는 객체를 직접 생성해주지 않는 것일까?

그렇다, 보통의 service class 를 예시로 보면 해당 class에서 의존 객체를 new로 생성하거나 해당 클래스 객체를 new로 생성하도록 하지 않음

구체적인 예시를 보자면

  • 생성자를 통한 객체 생성 : 외부 객체의 경우 DI를 통해 주입받도록 인터페이스를 정의

*엔티티 생성 : 직접 객체 생성, repository의 메서드를 호출해서 반환값을 받음

serializer

스프링 어플리케이션에서 캐싱할 데이터를 직렬화, 역직렬화하기 위한 serializer을 추가해준다
레디스의 데이터는 키와 밸류로 구성된다
*직렬화 : 레디스 바이트로 해당 자료형을 저장

  • RedisSerializer<String> keySerializer = new StringRedisSerializer();
  • RedisSerializer<Object> valueSerializer = new GenericJackson2JsonRedisSerializer();

이때 value serializer에 Object를 추가해주는 이유는
cache config에서 다룰 자료형을 다양하게 열어두기 위해서이다

  • <T> 와 같은 제너릭으로 두게 될 경우 나중에 type을 결정할 수는 있지만 하나의 cache config의 value serializer을 고정된 type으로 쓸 수밖에 없는 단점 발생

공통 config의 구성

  • serializer
  • ttl : 기본 5분
  • disableCachingNullValues() : null을 캐싱할 필요 없으므로 추가

Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();

  • String : cache 이름
  • RedisCacheConfiguration : cache config

이때, 신청예약목록조회는 ttl을 15초로 설정해 추가

map을 통해 cache configuration들을 관리해줄 때 hashmap을 쓰는 이유는
정렬이 필요없고, 평균 O(1)로 빠른 조회가 가능하기 때문에
해당 목적으로 부합함


@Configuration
public class RedisCacheConfig {

    @Bean
    public RedisCacheManager redisCacheManager(RedisConnectionFactory connectionFactory) {
        RedisSerializer<String> keySerializer = new StringRedisSerializer();

        // value는 list, dto 등 다양한 type 가능하므로 Object
        RedisSerializer<Object> valueSerializer =
                new GenericJackson2JsonRedisSerializer(); // 객체를 json으로 바꾸고 class 정보 저장

        // 공통 config 설정
        RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig()
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer))
                .disableCachingNullValues()
                .entryTtl(Duration.ofMinutes(5)); // 기본 5분

        Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();

        // 신청 예약 목록 조회용 캐시
        cacheConfigurations.put("appliedReservations", defaultConfig.entryTtl(Duration.ofSeconds(15)));

        return RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(defaultConfig) // default config 추가
                .withInitialCacheConfigurations(cacheConfigurations) // 신청 예약 목록용 캐시 추가
                .transactionAware() // 트랜잭션 커밋 성공 후 캐시에 반영
                .build();
    }
}

동작 과정

  • 컴포넌트 스캔으로 인해 spring bean으로 등록된 후 앱 시작시 생성됨
  • 이후 메서드 호출 시 먼저 캐싱 정보 조회 - 이후 없을 때 실제 서비스 메서드

0개의 댓글