Redis를 Spring boot에서 이용하는 방법은 2가지 방법이 있다.
1. Redis Repository 방법 (JPA 처럼 사용)
2. Redis Template 방법
간단한 테스트를 위해서 entity, service, controller, repository를 구현해보자
1. Redis Repository 방법
Test.java
@Getter
@AllArgsConstructor
@NoArgsConstructor
@RedisHash("test")
public class Test {
@Id
private String id;
private String nickname;
private String description;
private int age;
}
TestController
@RestController
@RequiredArgsConstructor
public class TestController {
private final TestService testService;
@PostMapping("/test-post")
public Test postTest() {
Test test = new Test("id", "닉네임", "설명", 12);
return testService.postTest(test);
}
}
TestService
@Service
@RequiredArgsConstructor
public class TestService {
private final TestRedisRepository testRedisRepository;
public Test postTest(Test test) {
testRedisRepository.save(test);
return test;
}
}
TestRedisRepository
public interface TestRedisRepository extends CrudRepository<Test, String> {
}
/test-post 로 요청을 보내고 난후, 레디스에 값이 들어간 것을 확인할 수 있다.
Redis Repository를 사용하면 Jpa와 같은 편리한 CRUD 기능을 제공하지만, 트랜잭션이 적용되지 않기 때문에 트랜잭션을 적용하려면 Redis Template를 사용해야 한다.
2. Redis Template 방법
RedisConfig
- Redis Template를 사용하려면 RedisConfig 파일에 RedisTemplate 빈을 등록해야한다.
@Getter
@Configuration
@RequiredArgsConstructor
@EnableRedisRepositories
public class RedisConfig {
@Value("${spring.cache.redis.host}")
private String host;
@Value("${spring.cache.redis.port}")
private int port;
// 내장 혹은 외부의 Redis를 연결 - lettuce
@Bean
public RedisConnectionFactory redisConnectionFactory(){
return new LettuceConnectionFactory(host, port);
}
// RedisConnection에서 넘겨준 byte 값 객체 직렬화
@Bean
public RedisTemplate<?,?> redisTemplate(){
RedisTemplate<byte[], byte[]> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new StringRedisSerializer());
return redisTemplate;
}
}
TestService 코드 수정 - 이전과 같은 데이터를 넣으려면 하나하나 번거롭게 넣어줘야하는 귀찮음이 있다.
@Service
@Slf4j
@RequiredArgsConstructor
public class TestService {
private final RedisTemplate<String, String> redisTemplate;
public void postTest() {
redisTemplate.execute(new SessionCallback<>() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
operations.multi();
// Redis에 정보 등록
operations.opsForValue().set("user2", "222");
operations.opsForHash().put("test:id","id", "id");
operations.opsForHash().put("test:id","age", "12");
operations.opsForHash().put("test:id","nickname", "닉네임");
operations.opsForHash().put("test:id","description", "설명");
return operations.exec();
}
});
}
Map을 이용해서 그나마 간편하게 만든 버전
@Service
@Slf4j
@RequiredArgsConstructor
public class TestService {
private final RedisTemplate<String, String> redisTemplate;
public void postTest() {
redisTemplate.execute(new SessionCallback<>() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
operations.multi();
// Redis에 정보 등록
setHashValues("test:id", Map.of(
"id", "id",
"age", "12",
"nickname", "닉네임",
"description", "설명"
));
return operations.exec();
}
});
}
private void setHashValues(String key, Map<String, String> fieldValues) {
fieldValues.forEach((field, value) -> redisTemplate.opsForHash().put(key, field, value));
}
TestController는 위에 작성한 코드와 동일하고, 전에 만들어 둔 TestRedisRepository는 필요 없다.
마찬가지로 /test-post 로 요청을 보내고 난후, 레디스에 값이 들어간 것을 확인할 수 있다.
그렇다면 트랜잭션이 잘 작동되는지 확인해보자.
age를 999로 바꿔주고 RuntimeException을 무조건 걸리게끔 코드를 수정하고 요청을 다시 보냈다.
public void postTest() {
redisTemplate.execute(new SessionCallback<>() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
operations.multi();
// Redis에 정보 등록
operations.opsForValue().set("user2", "222");
operations.opsForHash().put("test:id","id", "id");
operations.opsForHash().put("test:id","age", "999");
if(true) {
throw new RuntimeException("exception");
}
operations.opsForHash().put("test:id","nickname", "닉네임");
operations.opsForHash().put("test:id","description", "설명");
return operations.exec();
}
});
}
Spring에서는 Exception이 걸렸고
Redis는 값이 변경되지 않은 것을 확인할 수 있다. (트랜잭션이 잘 동작한다.)
객체처럼 사용하는 것이 맞나 싶긴한데 JPA처럼 사용하는 것이 편해서 이렇게 사용해봤다.
Redis를 사용할 때, 프로젝트의 요구사항과 환경에 따라 Redis Repository와 Redis Template 중 어떤 것을 사용할지 선택해야할 것 같다.
Redis Repository를 사용하면 JPA와 유사한 방식으로 코드를 작성할 수 있다. 이는 코드 작성을 빠르게 하고 가독성을 높일 수 있는 장점이 있지만 Redis Repository에서는 트랜잭션을 지원하지 않는다.
Redis Template을 사용하면 코드는 더 복잡해질 수 있지만, 트랜잭션을 적용할 수 있는 큰 장점이 있다. 이를 통해 Redis에서 복잡한 작업을 수행하거나 트랜잭션을 관리할 수 있다.
결국, 프로젝트의 요구사항과 상황에 따라 Redis Repository와 Redis Template 중 어떤 것을 선택할지 결정해야 한다. 가독성과 빠른 개발이 중요하다면 Redis Repository를 사용하는 것이 좋겠지만, 트랜잭션을 적용해야 한다면 Redis Template을 사용하는 것이 바람직한 것 같다.