DevOps/DevOps

[Redis] Redis 3편. Spring Boot와 Redis | Lettuce, RedisTemplate, RedisRepository

sebinChu 2024. 1. 8. 14:20

개요

Redis는 다양한 언어와 함께 사용할 수 있다.

이번 포스팅에서는 spring boot 프로젝트에서 사용하는 Redis에 대해 알아보도록 한다.

현재 개발 운영 방식에 알맞은 방법으로 Redis를 도입할 수 있도록 각각의 라이브러리와 방식을 공부하는 것이 목적이다.

 

전체 코드는 깃허브에서 확인 가능하다.

 


Lettuce와 Jedis

스프링 프로젝트에서 사용하는 레디스는 두 가지 종류가 있다.

 

 

Lettuce

Lettuce는 Netty 기반 라이브러리로 Asynchronous & Non-blocking으로 구현되어 있다. 

  • Netty란?
    • 네트워크 애플리케이션 개발을 위한 오픈소스 자바 프레임워크로, 비동기 이벤트 기반의 네트워크 애플리케이션을 쉽게 개발할 수 있도록 관련 도구와 라이브러리를 제공한다.
    • 자세한 내용은 여기서 확인할 수 있다.

 

여기서 비동기 방식이 핵심이다. 비동기 방식은 싱크를 맞추는 것과 상관없음. 즉, 하나의 작업이 완료될 때까지 대기하지 않고 다른 작업을 처리할 수 있는 특징이 있다. 이러한 비동기 방식으로 동작하는 Lettuce는 Redis와 하나의 커넥션만으로 여러 요청을 비동기적으로 처리할 수 있기에, 리소스를 효율적으로 사용하고 응답시간을 최적화한다.

 

또한, 비동기 논블로킹이므로 spring boot 애플리케이션이 멀티 스레드를 사용하더라도 안전한 프로그래밍을 할 수 있다.(Lettuce는 Synchronous도 제공한다.)

 

이러한 thread-safe 특성으로, spring boot 2.0부터는 Jedis가 기본 클라이언트에서 deprecated되고 Lettuce가 탑재되었다. 

더 자세한 사항은 공식 문서해당 이슈에서 확인 가능하다.

 

 

Lettuce Reference Guide

Connections to a Redis Standalone, Sentinel, or Cluster require a specification of the connection details. The unified form is RedisURI. You can provide the database, password and timeouts within the RedisURI. You have following possibilities to create a R

lettuce.io

 


Jedis

Lettuce와 비교하여 thread-non-safe하다는 점, java 기반 애플리케이션이라는 점만 알아두자.

 

 


 

Spring boot에서 Redis를 사용하는 방법

Lettuce를 통해 스프링 프로젝트와 레디스를 사용하는 방법은 두 가지다.

 

  1. o.s.data.redis.core의 RedisTemplate 클래스
  2. Spring Data 프로젝트에서 제공하는 CrudRepository를 확장한 RedisRepository 사용

 

 

RedisTemplate

첫 번째 방법인 RedisTemplate을 사용하는 것은 RedisRepository를 사용하는 것에 비해서 Redis 서버를 더 세밀하게 다루는 방법이다. Spring Data Redis에서 제공하는 다양한 Operations를 통해서 특정 데이터 타입을 다루거나 명령어를 수행함으로써 가능하다.

 

 

Working with Objects through RedisTemplate :: Spring Data Redis

Most users are likely to use RedisTemplate and its corresponding package, org.springframework.data.redis.core or its reactive variant ReactiveRedisTemplate. The template is, in fact, the central class of the Redis module, due to its rich feature set. The t

docs.spring.io

 

 

RedisTemplate으로 SpringBoot와 Redis 사용

 

일단 SpringBoot Redis를 사용하기 위해 의존성 및 빈 설정을 진행한다.

필자는 pom.xml 파일을 통해 여러 의존성과 라이브러리를 관리하고 있으므로 pom.xml 파일에 다음과 같이 추가해준다.

 

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
      <groupId>io.lettuce</groupId>
      <artifactId>lettuce-core</artifactId>
      <version>6.2.7.RELEASE</version>
</dependency>

 

 

그리고 빈등록을 위해 application.properties 파일에 필요한 속성들을 추가해준다. (디폴트값으로 설정된 값을 사용하기에 따로 추가를 해주지 않아도 된다. 하지만 프로퍼티 파일을 잘 쓰는 것은 하드코딩을 막을 수있고, 가시적으로 보이는 값들이기에 협업을 할 때에도 많은 도움을 받을 수 있다고 느꼈기에 직접 추가해주었다. - @Config 파일의 @Value로 사용할 수 있다.)

 

일단은 간단하게 아래 두 가지만 추가

 

application.properties

 

spring.data.redis.host=localhost
spring.data.redis.port=6379

 

 

빈등록을 위해 Config 파일에 RedisTemplate을 세팅한다.

 

config/AppConfig.Java

 

@Configuration
public class AppConfig {

    @Value("localhost")
    private String host;

    @Value("6379")
    private int port;

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

    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory());
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());

        return redisTemplate;
    }
}

 

 

이렇게 되면 SpringBoot와 Redis를 사용할 준비가 얼추 끝났다.

Template에 추가적으로 값을 세팅하여 더 세밀하게 Redis 서버를 다룰 수 있다.

 

 

이제 클라이언트 측에서 요청을 날리고 redis 서버에 원하는 동작이 이루어지는지 확인해보자.

 

RedisController.java

 

@RestController
@RequestMapping("/redis")
public class RedisController {

    @Autowired
    private StringRedisTemplate redisTemplate;


    @GetMapping("/{key}/{value}")
    public String stringsSet(@PathVariable String key, @PathVariable String value) {
        final ValueOperations<String, String> stringStringValueOperations = redisTemplate.opsForValue();

        stringStringValueOperations.set(key, value); // redis set 명령어
        System.out.println("stringStringValueOperations's key = " + stringStringValueOperations.get(key));
        return "ok";
    }
}

 

설정한 RedisTemplate의 의존성을 주입하고, URL 상으로 key와 value를 받아 이를 redis에 저장해본다. 위 코드를 실행하고 http://localhost:8080/redis/1/10이런식으로 요청을 날린 후 Redis에 값이 set되었는지 확인해보자.

 

 

요청 URL과 ok를 확인해주고, 여기서 key는 1, value는 10이다.

 

 

모든 key 값을 출력하는 keys 명령어를 통해 Redis에 저장된 Key를 확인해보면 좀 전에 controller로 요청한 1이 저장되어있다.

 

 

이 key 값을 통해 value도 출력해보면 value 값인 10이 잘 올라가있음을 확인할 수 있다.

 

 

 

 

 


RedisRepository

Spring Data Redis의 RedisRepository는 Spring JPA와 비슷한 방식으로 Redis에 접근할 수 있어, 스프링 개발자에게 좀 더 친숙한 방식이다. 

 

  • Config 설정, pom.xml, application.properties 설정은 Redis Template 방식과 동일하므로 생략한다.

 

우선 Repository 사용을 위해 간단한 Entity를 만들어 준다.

 

@Data
@RedisHash("member")
public class Member {
    @Id
    private String id;   // redis는 key- value 쌍이기 떄문에 member:<Id> 이런식으로 들어감
    private String  name;
    private int age;

    public Member(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

 

 

 

  • RedisHash: Member 클래스의 객체가 레디스에 저장될 때 Hash의 이름을 member로 저장하고, Hash 형태로 저장된다는 뜻이다. 일반적으로 레디스는 Key-Value 저장소이다. 그런데 Spring Data Redis는 객체를 레디스에 저장할 때 Hash 자료구조를 사용한다.

 

  • 위를 예로 들면 Member 객체를 레디스에 저장하면 다음과 같은 형태로 저장된다.
member:1 = {"id": "1", "name": "spring-redis", "age": 20}

 

 

RedisRepository는 간단하게 CrudRepository만 잘 상속받아 주면 된다. 이를 상속받음으로써 기본적인 CRUD 연산을 수행할 수 있는 메소드들이 제공된다. (필요 시 Override 가능)

 

public interface RedisRepository extends CrudRepository<Member, String> {
}

 

 

클라이언트에서 요청을 날리고, 이를 Redis 서버에 반영할 Controller와 Service 함수는 다음과 같다.

 

 

MemberController.java

 

@RestController
public class MemberController {

    private final RedisService redisService;

    public MemberController(RedisService redisService) {
        this.redisService = redisService;
    }

    @GetMapping
    public String setPersonInfo() {
        redisService.setRedisData();
        return "ok";
    }
}

 

 

MemberServiceImpl.java

 

@Service
public class RedisServiceImpl implements RedisService{
    @Autowired
    private final RedisRepository redisRepository;

    public RedisServiceImpl(RedisRepository redisRepository) {
        this.redisRepository = redisRepository;
    }

    public void setRedisData() {
        Member member = new Member("springredis", 99);
        redisRepository.save(member);
    }
}

 

 

 

localhost 서버를 띄워 요청을 날리고 Redis를 확인해보면 다음과 같이 값이 Hash로 잘 저장되어있음을 확인할 수 있다.

 

 

 


 

레디스는 생각보다 공식문서들이 잘 되어있어서 진입하는 데에는 그리 어렵지는 않았다.

앞으로 캐시를 위한 레디스 업무를 맡을것같은데... 일단 인프런 강의를 통해서 레디스의 주요 명령어들을 잘 배우고, 캐시를 위한 레디스는 어떻게 사용할 것인지에 대해 더 정리해보면 좋을 듯하다. 

그리고 실제 업무 환경에서 어떻게 호스팅할지? 알아봐야한다.