Reactive Redis는 위 문제점을 해결할 수 있는 비동기 논블로킹을 지원하는 레디스 클라이언트 혹은 라이브러리이다.

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'org.springframework.boot:spring-boot-starter-data-r2dbc'
**implementation 'org.springframework.boot:spring-boot-starter-data-redis-reactive'**
implementation "io.asyncer:r2dbc-mysql:1.0.2" // spi 1.0.0 (spring boot3)
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.projectreactor:reactor-test'
}
# lazy하게 연결을 해서 참고만 할 뿐 (틀리게 적어도 실행은 된다)
spring:
r2dbc:
url: r2dbc:mysql://localhost:3306/sns
username: root
password: 1234
**data:
redis:
host: 127.0.0.1
port: 6379**
lazy하게 연결을 해서 참고만 할 뿐 (틀리게 적어도 실행은 된다)
@Slf4j
@RequiredArgsConstructor
@Configuration
public class RedisConfig implements ApplicationListener<ApplicationReadyEvent> {
private final ReactiveRedisTemplate<String, String> redisTemplate;
// 서버 실행 시 연결 검증을 위함
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
redisTemplate.opsForValue().get("1")
.doOnSuccess(s -> log.info("Initialize to redis connection"))
.doOnError(e -> log.error("Failed to initialize redis connection: {}", e.getMessage()))
.subscribe();
}
@Bean
public ReactiveRedisTemplate<String, User> reactiveRedisTemplate(ReactiveRedisConnectionFactory connectionFactory) {
ObjectMapper objectMapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) // 모르는 값 무시
.registerModule(new JavaTimeModule())
.disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS);
// Redis에 데이터를 저장하거나 읽어올 때 사용할 직렬화 컨텍스트 설정
Jackson2JsonRedisSerializer<User> jsonRedisSerializer = new Jackson2JsonRedisSerializer<>(objectMapper, User.class);
RedisSerializationContext<String, User> serializationContext = RedisSerializationContext
.<String, User> newSerializationContext()
.key(RedisSerializer.string())
.value(jsonRedisSerializer)
.hashKey(RedisSerializer.string())
.hashValue(jsonRedisSerializer)
.build();
return new ReactiveRedisTemplate<>(connectionFactory, serializationContext);
}
}
public Mono<User> findById(Long id) {
/*
1. redis 조회
2. 값이 있으면 응답
3. 없으면 DB에 쿼리 후 결과를 redis에 저장
*/
return reactiveRedisTemplate.opsForValue()
.get(getUserCacheKey(id))
.switchIfEmpty(
userR2dbcRepository.findById(id)
.flatMap(u -> // redis에 저장
reactiveRedisTemplate.opsForValue()
.set(getUserCacheKey(id), u, Duration.ofSeconds(30))
.then(Mono.just(u))
)
);
}
public Mono<Void> deleteById(Long id) {
return userR2dbcRepository.deleteById(id)
.then(reactiveRedisTemplate.unlink(getUserCacheKey(id)))
.then(Mono.empty());
}
public Mono<User> update(Long id, String name, String email) {
return userR2dbcRepository.findById(id)
.flatMap(u -> {
u.setName(name);
u.setEmail(email);
return userR2dbcRepository.save(u);
})
.flatMap(u -> reactiveRedisTemplate.unlink(getUserCacheKey(id)).then(Mono.just(u)));
}