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로 생성하도록 하지 않음
구체적인 예시를 보자면
*엔티티 생성 : 직접 객체 생성, repository의 메서드를 호출해서 반환값을 받음
스프링 어플리케이션에서 캐싱할 데이터를 직렬화, 역직렬화하기 위한 serializer을 추가해준다
레디스의 데이터는 키와 밸류로 구성된다
*직렬화 : 레디스 바이트로 해당 자료형을 저장
RedisSerializer<String> keySerializer = new StringRedisSerializer();RedisSerializer<Object> valueSerializer = new GenericJackson2JsonRedisSerializer();이때 value serializer에 Object를 추가해주는 이유는
cache config에서 다룰 자료형을 다양하게 열어두기 위해서이다
<T> 와 같은 제너릭으로 두게 될 경우 나중에 type을 결정할 수는 있지만 하나의 cache config의 value serializer을 고정된 type으로 쓸 수밖에 없는 단점 발생Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
이때, 신청예약목록조회는 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();
}
}