본문 바로가기
DB/Redis

TIL 230830 : Redis 1 - Spring에서 연동하기 (lettuce, jedis, RedisTemplate, CRUDRepository 활용)

by 우인입니다 2023. 8. 31.

 

지난 시간에 로컬로 Redis서버를 실행하고 RedisInsight라는 GUI를 이용해 Redis 데이터를 저장해보는 기본적인 기능을 테스트 해봤다.

 

이번엔 Spring에서 Redis로 CRUD 요청을 보내는 세팅과 기본적인 CRUD 명령어들을 연습해본다.


0. Lettuce vs Jedis

세팅에 앞서 Lettuce와 Jedis라는 단어를 구글링하는 동안 많이 들어봤을 텐데, 짚고 넘어간다.

우선 정체를 하자면 Java의 Redis Client다.

쉽게 말하자면 Redis연동을 도와주는 라이브러리이다.

크롬이 서버에 요청하고 응답을 받는 클라이언트인것처럼 Redis서버에 요청하고 응답을 받아줄 수 있는 Client 역할이다.

 

결론적으로는, Lettuce를 쓰게된다.

Spring Boot 2버젼이후에서는 기본적으로 Lettuce라이브러리가 의존성에 포함되어있고,

성능 또한 비동기 기반으로 동작하는 Lettuce가 월등히 좋다.

 

이후 종종 등장하는 단어에 대해 당황하지 않고 그냥 쓰면 우선은 된다.

 

 

 

 

1. RedisConfig

@Configuration
public class RedisConfig {


    @Bean
    @ConfigurationProperties(prefix = "spring.data.redis") 
    // 해당주소를 상위 주소로 갖고 있는 환경변수들을 가져와 바인딩 해준다.)
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory(); //Lettuce를 사용한다.
    }

    @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;
    }

}

RedisConnectionFactory

 : Redis에 연결해주는 동작을 하며, Lettuce를 사용해 연결합니다. 이에 관련된 정보들을 @ConfigurationProperties(prefix = "spring.data.redis") 어노테이션을 통해 정보를 받아와 객체를 생성해낸다.

 

RedisTemplate

 : 스프링부트 Redis 의존성을 통해서 가져온 클래스. 여기서 설정해준 RedisTemplate을 빈으로 등록해두고 이후 사용가능하다. 여기서 직렬화/역직렬화에 사용할 방식을 지정해줄 수 있는데 우선은 가장 무난한 String기반으로 설정해뒀다. 이 부분은 이후 디테일하게 커스텀이 필요할 때 다뤄볼 수 있는 요소이다.

 

 

 

 

 

 

1-1. 환경변수

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

이는 사실 기본값들이고, 이 외에도 설정해줄 것들이 많긴 한데 아직은 비밀번호만 명시해준다.

 

+host, port외에 옵션들

변수 기본값 설명
spring.redis.database 0 커넥션 팩토리에 사용되는 데이터베이스 인덱스
spring.redis.host localhost 레디스 서버 호스트
spring.redis.password   레디스 서버 로그인 패스워드
spring.redis.pool.max-active 8 pool에 할당될 수 있는 커넥션 최대수 (음수로 하면 무제한)
spring.redis.pool.max-idle 8 pool의 "idle" 커넥션 최대수 (음수로 하면 무제한)
spring.redis.pool.max-wait -1 pool이 바닥났을 때 예외 발생 전, 커넥션 할당 차단 최대 시간
(단위 밀리세컨드, 음수는 무제한 차단)
spring.redis.pool.min-idle 0 풀에서 관리하는 idle 커넥션의 쵀소수 대상 (양수일 때만 유효)
spring.redis.port 6379 레디스 서버 포트
spring.redis.sentinel.master   레디스 서버 이름
spring.redis.sentinel.nodes   호스트: 포트 쌍 목록 (콤마로 구분)
spring.redis.timeout 0 커넥션 타임아웃 (단위 밀리세컨드)

- 참고 사이트

https://zetawiki.com/wiki/%EC%8A%A4%ED%94%84%EB%A7%81_REDIS_%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0

 

 

 

 

 

2-0. RedisTemplate vs RedisRepository

Redis에 접근하는 방법은 크게 두 가지로 나뉘는데 RedisTemplate과 RedisRepository이다.

 

우선 이 둘은 각각의 차이와 그에 따른 장단점이 있는데,

쉽게 요약하면 수동 -> RedisTemplate, 자동 -> RedisRepository이다.

RedisTemplate가 좀 더 로우언어에 직접적인 요청을 수행할 수 있는 반면에

RedisRepository는 JPARepository처럼 간편하게 엔티티를 통해 쿼리를 요청할 수 있다.

 

 

 

2-1. RedisTemplate

// 의존성 주입
    private final RedisTemplate<String, String> redisTemplate;

우선 앞서 @Bean으로 등록해두었던 RedisTemplate를 주입 받아온다.

 

    public void createByTemplate() {
        ValueOperations<String, String> stringValueOperations = redisTemplate.opsForValue();
        
        // 생성 - 기존에 없던 키를 입력해주면 생성한다.
        stringValueOperations.set("keyUsername", "valueRefreshToken", LIMIT_TIME, TimeUnit.SECONDS);
        
        // 조회 - 고유한 키를 통해 값을 가져온다.
        stringValueOperations.get("keyUsername");
        
        // 수정 - 따로 수정메소드가 있지 않고, 해당 키 값에 덮어씌우는 형식.
        stringValueOperations.getAndSet("keyUsername", "modified");
        
        // 삭제 - redisTemplate에서 직접 삭제요청한다.
        redisTemplate.delete("keyUsername")
    }

redisTemplate.opsForValue();

 : RedisTemplate에 내장된 메소드로 ValueOperations를 생성해준다.

메서드 명 레디스 타입
opsForValue String
opsForList List
opsForSet Set
opsForZSet Sorted Set
opsForHash Hash

   이 때, 사용하게 될 자료타입에 맞게 메소드를 사용해준다.

이후 해당 ValueOperation을 통해 요청을 보낸다.

 

결과

create

키-값이 잘 저장되어 있다. +TTL 설정

read

modify

 

 

 

 

2-2. RedisRepository

 

Dto생성

JPARepository를 사용했던 것처럼, interface를 생성 후 상속받아서 사용하면 된다.

다만, 여기서는 CRUDRepository를 상속받아서 사용하고, 엔티티타입에 사용할 Dto 클래스를 담아준다.

@RedisHash 라는 어노테이션 이름에서 유추할 수 있듯,  Hash의 형태로 저장된다.

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@RedisHash("refreshTokenFolder") // RDB에서 테이블 이름처럼 사용하게 된다.
public class RefreshTokenResponseDto {
    @Id // @Id로 명시해준 멤버가 위에 명시해준 RedisHash값과 합쳐져 key의 이름으로 활용된다.
    private String username;

    private String refreshToken;
    ...// 추가로 멤버를 추가해줄 수 있다.
}

 

 

 

Repository 생성

CRUDRepository를 상속받아준다. 첫번째 지네릭 타입으로 값에 담길 객체의 타입을 명시해준다.

public interface RefreshTokenRedisRepository extends CrudRepository<RefreshTokenResponseDto, String> {
}

 

 

Service단에서 실행

 

객체를 생성해주고 기존 JPARepository처럼 사용해주면 된다.

        var responseDto = RefreshTokenResponseDto.builder()
                .username("username " + i)
                .refreshToken("testToken " + i)
                .build();

        return refreshTokenRedisRepository.save(responseDto);

 

결과

 

SET

@Id를 지정해줬던 멤버들의 값들을 SET형태로 가지고 있다.

 

HASH

Repository를 생성할 때 선언해준 타입의 형태로 구성되어 있다.

키:값들이 여러 묶음으로 되어있는 Hash형태로 저장되어 있다.

 

이렇듯 Repository를 활용한 방법으로는 간편하지만 자료구조의 한계가 있을 수 있다.

 

 


더 공부할 것

  • 캐싱서버로 활용할 때의 세팅.
  • 실제 DB에 저장해두고 불러와둘 수 있는 기능 여부.
  • 그 외에 기본적인 기능들.

  이후에, 활용하게 될 refreshToken, 랭킹리스트 호출 등을 이용하여 성능적 개선에 도전해보고 싶다.