본문 바로가기
Coding/Java Spring

Spring Data Redis로 레디스 연동하는 방법

by Hide­ 2021. 10. 19.
반응형

개요

스프링에는 Redis와 연동하는 여러가지 방법이 존재하는데 Data JPA와 비슷하게 Data Redis라는 라이브러리를 제공한다. LettuceJedis 2가지 구현체를 통해 통신할 수 있는 방법이 있는데, Lettuce는 별다른 추가 설정없이 사용할 수 있지만 Jedis는 추가적인 의존성이 필요하다. 요즘에는 아래의 이유로 인해 Jedis보다 Lettuce를 쓰는 추세라고 한다.

- Lecttue는 비동기로 요청을 처리하기 때문에 성능적인 이점이 있음

- Lecttue는 Jedis와는 다르게 별도의 풀을 생성하지 않아도 되기 때문에 개발이 단순함

- Jedis는 Thread-safe하지 않고 Spring boot 2.0부터 deprecated됨

본 포스팅에서는 Data Redis를 통해 스프링에서 Redis와 통신하는 방법에 대해 기술한다.

Data Redis에서는 RedisTemplate, RedisRepository 총 2가지의 방법을 제공한다. 설정부터 2가지의 방법을 하나하나 살펴보자.

설정 및 의존성 설치

spring:
  redis:
    host: localhost
    port: 6379

먼저 application.yml파일을 열고 아래의 라인을 추가한다. 설정은 다른 방법을 사용할수도 있지만 본 포스팅에서는 최대한 간단한 설정을 위해 yml파일에 작성했다.

implementation 'org.springframework.boot:spring-boot-starter-data-redis'

다음으로 build.gradle을 열고 dependencies부분에 data-redis를 추가해준다.

RedisTemplate

@Configuration
public class RedisConfig {
    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private int port;

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory(host, port);
    }

    @Bean
    public RedisTemplate<?, ?> redisTemplate() {
        RedisTemplate<byte[], byte[]> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory());
        return redisTemplate;
    }
}

먼저 RedisConfig라는 클래스를 하나 생성해준다. 위에서 application.yml에 레디스 관련 host, port를 넣어줬기 때문에 @Value를 통해 해당 값을 가져와서 채워주기로 한다. 위 설정은 RedisRepository가 아닌 RedisTemplate를 통해 레디스와 통신하기 위한 설정값이다.

String 타입

@Service
@RequiredArgsConstructor
public class HealthCheckService {
    private final RedisTemplate<String, String> redisTemplate;
    
    public void testRedisString() {
        ValueOperations<String, String> operation = redisTemplate.opsForValue();
        String key = "string";
        operation.set(key, "test");
        operation.get(key);
        System.out.println(key);
    }
}

String타입은 opsForValue() 메소드를 사용하며 set(), get() 등의 메소드를 지원한다.

Set 타입

@Service
@RequiredArgsConstructor
public class HealthCheckService {
    private final RedisTemplate<String, String> redisTemplate;

    public void testRedisSet() {
        SetOperations<String, String> operation = redisTemplate.opsForSet();
        String key = "set";
        operation.add(key, "h", "i", "d", "e");
        Set<String> members = operation.members(key);
        System.out.println(members);
    }
}

Set타입은 opsForSet() 메소드를 사용하며 add(), members() 등의 메소드를 지원한다.

Hash 타입

@Service
@RequiredArgsConstructor
public class HealthCheckService {
    private final RedisTemplate<String, String> redisTemplate;

    public void testHash() {
        HashOperations<String, Object, Object> operation = redisTemplate.opsForHash();
        String key = "hash";
        operation.put(key, "hide", "test");
        Object value = operation.get(key, "hide");
        System.out.println(value);
    }
}

Hash타입은 opsForHash() 메소드를 사용하며 get(), put() 등의 메소드를 지원한다.

RedisRepository

RedisRepository를 사용하면 객체를 손쉽게 Hash타입의 자료구조로 변환하여 사용할 수 있다.

import lombok.Getter;
import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.RedisHash;

@Getter
@RedisHash(value = "user")
public class RedisUser {
    @Id
    private Long id;
    private String name;

    public RedisUser(Long id, String name) {
        this.id = id;
        this.name = name;
    }
}

먼저 위처럼 사용할 객체를 만든다. 위 예제에서는 간단하게 id, name이라는 2개의 필드만을 가진 RedisUser라는 클래스를 만들었다. @RedisHashvalue값에 특정한 값을 넣어줌으로써 추후 키를 생성할 때 prefix를 지정해줄 수 있다. 또한 @Id 어노테이션을 사용하여 prefix:구분자 형태로 값을 구분한다. 참고로 @Id 어노테이션은 코드에 나와있는 것 처럼 org.springframework.data.annotation.Id를 사용해야 한다. 

만약 TTL을 주고 싶다면 RedisHash에 timeToLive옵션에 정수값을 넣어주자

public interface UserRedisRepository extends CrudRepository<RedisUser, Long> {
}

다음으로 CrudRepository를 상속받은 Repository 인터페이스를 만들어준다. 이 부분은 Data Jpa와 완전히 동일하다.

@Service
@RequiredArgsConstructor
public class HealthCheckService {
    private final UserRedisRepository userRedisRepository;

    public void createUser() {
        RedisUser user = new RedisUser(1L, "hide");
        userRedisRepository.save(user);
    }
}

저장 또한 Data Jpa와 마찬가지로 객체를 생성하고 Repository의 save() 메소드를 통해 저장할 수 있다. 저장 후 Redis에서 키를 살펴보면

127.0.0.1:6379> keys *
1) "user"
2) "user:1"

위처럼 키가 생성된것을 알 수 있다. 위에서 해시로 저장할 RedisUser 객체를 생성할 때 @RedisHash의 value값을 user로 줬고 id부분에 @Id 어노테이션을 붙여줬기 때문에 prefix:구분자 형태로 저장되었다.

127.0.0.1:6379> hgetall user:1
1) "_class"
2) "slido.slidoclone.health.service.RedisUser"
3) "id"
4) "1"
5) "name"
6) "hide"

Hash형태로 저장이 되므로 hgetall을 통해 값을 살펴보면 정상적으로 저장이 되었음을 확인할 수 있다.

@Service
@RequiredArgsConstructor
public class HealthCheckService {
    private final UserRedisRepository userRedisRepository;

    public void getUser() {
        Optional<RedisUser> user = userRedisRepository.findById(1L);
        System.out.println(user.get());
    }
}

값을 가져올 때도 기존 Data Jpa와 마찬가지로 동일한 메소드를 사용하면 된다. 위에서는 id값을 통해 가져오기 위해 findById() 메소드를 사용했다. 해당 값을 출력시켜보면,

slido.slidoclone.health.service.RedisUser@3cf29300

정상적으로 출력됨을 확인할 수 있다.