📍 Spring Boot 프로젝트에서 Spring Data Redis를 설정하고 사용하는 방법에 대해 알아보겠습니다.
Spring Data Redis 란?
Spring Data Redis는 Redis와 스프링 애플리케이션을 통합하여 Redis 데이터 저장소와 쉽게 상호작용할 수 있도록 지원하는 스프링 프로젝트입니다.
1. 의존성 추가
Spring Boot 프로젝트에서 Spring Data Redis를 사용하려면 spring-boot-starter-data-redis 의존성을 추가해야 합니다.
Redis 클라이언트인 Lettuce와 Jedis를 사용할 수 있는데 Spring Boot에서 Redis 클라이언트로 기본적으로 사용되는 라이브러리는 Lettuce입니다. Lettuce는 비동기 및 동시성을 지원하고, 리액티브 프로그래밍에도 적합하기 때문에 Spring의 기본 선택입니다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
2. Redis 연결 설정
Spring Boot는 기본적으로 application.properties 또는 application.yml 파일을 통해 Redis 설정을 할 수 있습니다.
spring:
redis:
host: 127.0.0.1
port: 6379
3. Redis Configuration
Redis 연결을 위한 Configuration 클래스를 작성합니다.
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
RedisTemplate<String, Object> objectRedisTemplate(RedisConnectionFactory connectionFactory) {
// PolymorphicTypeValidator는 Jackson이 다형성 타입을 안전하게 직렬화/역직렬화할 수 있도록 합니다.
// allowIfSubType(Object.class)는 Object 클래스의 모든 하위 타입을 허용합니다.
PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator
.builder()
.allowIfSubType(Object.class)
.build();
// ObjectMapper 구성
var objectMapper = new ObjectMapper()
// JSON에 알 수 없는 속성이 있어도 역직렬화 실패를 방지합니다.
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
// Java 8 날짜/시간 타입(예: LocalDate, LocalDateTime)을 지원합니다.
.registerModule(new JavaTimeModule())
// 다형성 타입 처리를 활성화합니다. 이는 객체의 실제 타입 정보를 JSON에 포함시킵니다.
.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.NON_FINAL)
// 날짜를 타임스탬프 대신 ISO-8601 형식의 문자열로 직렬화합니다.
.disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS);
// RedisTemplate을 구성
var template = new RedisTemplate<String, Object>();
template.setConnectionFactory(connectionFactory); // 주입된 Redis 연결 팩토리를 설정합니다.
template.setKeySerializer(new StringRedisSerializer()); // 키를 문자열로 직렬화합니다.
template.setValueSerializer(new GenericJackson2JsonRedisSerializer(objectMapper)); // 값을 JSON으로 직렬화합니다. 커스텀 ObjectMapper를 사용하여 위에서 구성한 모든 설정을 적용합니다.
return template;
}
}
4. RedisRepository 사용
RedisRepository 인터페이스를 사용하여 더 간단하게 CRUD 작업을 수행할 수 있습니다. 이를 위해서는 저장할 객체에 반드시 @RedisHash 어노테이션을 추가해야 합니다.
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.RedisHash;
import org.springframework.data.redis.core.index.Indexed;
import java.time.LocalDateTime;
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
@RedisHash("Person", timeToLive = 30L)
public class RedisPerson {
@Id
private String id;
private String name;
private int age;
@Indexed
private String email;
private LocalDateTime createdDt;
private LocalDateTime updatedDt;
}
이제 CrudRepository 인터페이스를 확장한 PersonRepository를 통해 데이터를 저장하고 조회할 수 있습니다.
import org.springframework.data.repository.CrudRepository;
public interface RedisPersonRepository extends CrudRepository<Person, String> {
}
5. 캐싱 적용하기
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class PersonService {
private final PersonRepository personRepository;
private final RedisPersonRepository redisPersonRepository;
// 1. 캐싱 방식: redisPersonRepository Redis 전용 저장소를 통해 직접 캐시된 데이터를 확인하고 없을 경우 데이터베이스에서 조회한 후 캐시에 저장하는 방식을 사용합니다.
// 2. 캐시 조회: 먼저 Redis에서 findById(id)로 캐시된 데이터를 조회합니다.
// 3. 캐시 미스 시 데이터베이스 조회: 캐시에 데이터가 없으면 데이터베이스(personRepository)에서 해당 사용자를 조회한 후, 그 결과를 Redis에 저장합니다.
public RedisPerson getPerson(final Long id) {
var cachedPerson = redisPersonRepository(id).orElseGet(() -> {
Person person = personRepository(id).orElseThrow();
return redisPersonRepository.save(RedisPerson.builder()
.id(person.getId())
.name(person.getName())
.email(person.getEmail())
.age(person.getAge())
.createdDt(person.getCreatedDt())
.updatedDt(person.getUpdatedDt())
.build());
});
return cachedPerson;
}
}
Spring의 @Cacheable 사용하면 간단하게 코드를 적용할 수 있습니다.
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class UserService {
private final PersonRepository personRepository;
@Cacheable(cacheNames = CACHE1, key = "'person:' + #id")
public User getPerson(final Long id) {
return personRepository.findById(id).orElseThrow();
}
}
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.boot.autoconfigure.cache.RedisCacheManagerBuilderCustomizer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
import java.util.List;
@EnableCaching
@Configuration
public class CacheConfig {
public static final String CACHE1 = "cache1";
public static final String CACHE2 = "cache2";
@AllArgsConstructor
@Getter
public static class CacheProperty {
private String name;
private Integer ttl;
}
@Bean
public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {
PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator
.builder()
.allowIfSubType(Object.class)
.build();
var objectMapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.registerModule(new JavaTimeModule())
.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.NON_FINAL)
.disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS);
// 캐시 속성 정의: properties 리스트는 두 개의 캐시 속성을 정의합니다.
// CACHE1은 300초 TTL을 가지며, CACHE2는 30초 TTL을 가집니다.
List<CacheProperty> properties = List.of(
new CacheProperty(CACHE1, 300),
new CacheProperty(CACHE2, 30)
);
return (builder -> {
properties.forEach(i -> {
builder.withCacheConfiguration(i.getName(), RedisCacheConfiguration
.defaultCacheConfig()
.disableCachingNullValues()
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer(objectMapper)))
.entryTtl(Duration.ofSeconds(i.getTtl())));
});
});
}
}
간단하게 Spring Boot Cache 적용하는 방법을 정리했습니다.
Spring Data Redis는 Spring 애플리케이션에서 Redis를 쉽고 효율적으로 사용할 수 있게 해줍니다. 캐싱, 데이터 저장, 메시징 등 다양한 용도로 활용할 수 있어, 애플리케이션의 성능과 확장성을 크게 향상시킬 수 있습니다.
'Study > DB' 카테고리의 다른 글
Redis CLI 시작하기: 알아두면 유용한 명령어와 활용 팁 (2) | 2024.09.04 |
---|---|
Redis - Docker 사용하여 Redis 설치하고 실행하기 (MacOS) (3) | 2024.09.03 |
Redis (Remote Dictionary Server) 알아보기 (0) | 2024.09.01 |
In-Memory Database (0) | 2024.09.01 |