본문 바로가기
DB/Redis

[Redis] Redis + Spring boot 연동 (2)

by lucas_owner 2023. 3. 30.

1. Redis + Spring Boot 연결

Spring Boot 에서 Spring-data-redis 라이브러리를 통해 활용해보는 방법을 알아보겠습니다. 

 

- Redis Docker 설치 방법은 이전 포스팅을 참고 하세요!

https://lucas-owner.tistory.com/56

 

[Redis] Redis란? - Docker로 간단 Redis(Local) 설치 (1)

1. Redis 란? - in-memory 방식의 No-SQL 기반 DBMS - Key-Value(키-값) 구조의 데이터를 저장, 관리 - Singel-Thread 기반으로 동작함.(명령 수행) - DB, Cache(캐시),Message Broker(메시지브로커) 용도로 주로 사용함. -

lucas-owner.tistory.com

 

○ 개발 환경

- Spring Boot 2.7.10

- Redis 7.0.10

- Docker

- Mac OS Monterey 12.6

 

 

2. Spring Boot - Redis 설정

-  우선 Spring boot 에서 Redis를 사용하는 방법은 RedisRepository, RedisTemplate 2가지 방식이 존재합니다. 

   해당 사용방법은 설정 이후 자세하게 기술하겠습니다. 

 

- Java에서 Redis 를 사용하기 위한 Client에는 2가지가 존재한다. 

   1. Lettuce

   2. Jedis 

 

Jedis 를 주로 사용했었지만, 최근에는 Lettuce를 사용한다.

-Jedis : 단점(멀티 스레드 환경 불안정, Pool 한계 등등)

- Lettuce : 사용이유(Netty 기반 환경이라 비동기 지원, Jedis 보다 빠른 속도, 안정성)  

* 해당 글에서는 Lettuce를 사용하여 기술됩니다.


 

2-1.  라이브러리 추가

- build.gradle 에 아래의 라이브러리를 추가 후 빌드.

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

 

2-2. yml 설정

spring:
  redis:
    host: localhost
    port: 6379

- yml 파일에 위의 설정을 추가. 

- docker로 실행중이어도 local  <-> Docker(Redis) 포트가 연결되어있음.

 

 

2-3. RedisConfig - 공통 설정 파일

- redis를 스프링부트에서 사용하기 위해서는 아래의 공통 파일을 생성 해주어야 한다. 

- 해당 설정은 Redis 저장소와 연결하는 과정이다.

package com.boot.redis.config;

import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/************
 * @info : Redis Repository Config 클래스
 * @name : RedisRepositoryConfig
 * @version : 1.0.0
 * @Description : Lettuce 사용(비동기 요청 처리), RedisRepository 방식.
 ************/
@Configuration
@RequiredArgsConstructor
@EnableRedisRepositories
public class RedisRepositoryConfig {

    private final RedisProperties redisProperties;

    // lettuce
    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory(redisProperties.getHost(), redisProperties.getPort());
    }

    // Redis template 
    @Bean
    public RedisTemplate<?, ?> redisTemplate() {
        RedisTemplate<?, ?> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory());   //connection
        redisTemplate.setKeySerializer(new StringRedisSerializer());    // key
        redisTemplate.setValueSerializer(new StringRedisSerializer());  // value
        return redisTemplate;
    }
}

- key, value Serializer 설정하는 이유는 RedisTemplate를 사용 한다면, 스프링과 redis 사이에 데이터의 직렬화, 역직렬화 방식이 JDK 직렬화 방식이기 때문이다. 동작에는 전혀 영향이 있지않다, 하지만 redis-cli 를 통해 직접 데이터를 조회할때 데이터 형식을 사람이 알아볼 수 없는 형태로 출력되기 때문에 해당 설정을 적용해주어야 하는것이다. 

 

 

3. RedisRepository 

- JPA 를 사용을 해보았다면, 아주 쉽게 이해 할 수 있다(구조가 Spring Data JPA 와 비슷하다.)

- 객체를 담아서 저장한다.

- 트랜잭션을 지원하지 않는다.

 

3-1. Entity 

@RedisHash("user") // options: timeToLive = 10
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class User {

    @Id
    private String id; // userId: 입력안하면 임의의 값 생성됨.

    private String name;
    private String major; // 전공: back, front
    private int age;

    //private List<String> skils; // List 필요시

}

- @RedisHash 내부에 timeToLive = {seconds} 기입시 해당 데이터의 만료시간을 설정 할 수 있다. 

기본값은 만료시간이 없는 -1L 이다. 

- @Id 가 붙은 필드가 redis 키값이 된다 -> user:{key값}

 

3-2. Controller 

/************
 * @info : Redis User Controller
 * @name : UserController
 * @version : 1.0.0
 * @Description :
 ************/
@RestController
@RequiredArgsConstructor
@Slf4j
public class UserController {

    private final UserSerivce serivce;

    /**
     * @info    : Redis에 User 정보를 저장한다.
     * @name    : addUser
     * @version : 1.0.0
     */
    @PostMapping("/redis/v1/post")
    public User addUser(@RequestBody User user) {
        log.info("Controller Request: {}", user);

        User result = serivce.addUser(user);
        
        log.info("Controller result: {}", result);

        return result;
    }// save


    /**
     * @info    : Redis에 ID 값으로 유저 정보를 가져온다.
     * @name    : getUser
     * @version : 1.0.0
     * @Description :
     */
    @GetMapping("/redis/v1/getUser")
    public User getUser(@RequestParam String reqId) {
        User userById = serivce.getUserById(reqId);
        return userById;
    }

 

3-3. Service

**********
 * @info : Redis - User Service
 * @name : UserSerivce
 * @version : 1.0.0
 * @Description :
 ************/
@Service
@RequiredArgsConstructor
@Slf4j
public class UserSerivce {

    private final UserRedisRepositroy repository;

    /**
     * @info    : Redis - save (User)
     * @name    : addUser
     * @version : 1.0.0
     * @Description :
     */
    @Transactional
    public User addUser(User user) {
        // save
        User save = repository.save(user);

        // find
        Optional<User> result = repository.findById(save.getId());

        // Handling
        // 해당 data 존재시 return.
        if(result.isPresent()) {
            return result.get();
        }else {throw new RuntimeException("Database has no Data");}
    }//save

    /**
     * @info    : Redis - get
     * @name    : name
     * @version : 1.0.0
     * @Description :
     */
    @Transactional(readOnly = true)
    public User getUserById(String reqId) {
        Optional<User> result = repository.findById(reqId);

        // Handling
        if(result.isPresent()) {
            return result.get();
        }else {throw new RuntimeException("Database has no Data");}
    }

- 저장시 save(), 조회시 findById() 를 사용한다. = JPA 와 같음.

- 삭제시 delete() 사용한다. 

- 저장시 필드의 Id값을 지정하지 않으면, 랜덤한 키값이 생성되어 들어간다. 

 

3-4.Repository

- <Class, Id 데이터 타입> 으로 작성 -> Spring Data JPA 와 같음. 

public interface UserRedisRepositroy extends CrudRepository<User, String> {
}

 

3-5.  저장 후 조회 Test

- keys *  : Redis 에 저장된 모든 키를 조회한다.(운영에서는 사용하면 위험하다. 싱글스레드이기 때문에 다른작업 불가)

- hgetall user:{key} : 키값을 기준으로 키-밸류를 조회한다. 

    -->  skils.[0] 의 경우 hash 내부에 List 를 넣었기 때문에 출력됨.

- hkeys user:{Key} : 해당 키값의 키들을 출력한다.

- hvals user:{key} : 해당 키값의 값들을 출력한다.

 

4. RedisTemplate 

- 위의 방식은 RedisRepository 방식으로 JPA 와 같이 사용할 수 있었다. 

- RedisRepository 는 객체를 담아서 저장했다면, RedisTemplate는 특정 자료구조에 값을 저장하는 방식이다.

   -> 특정 Entity 뿐 아니라 개발자가 원하는 여러가지 데이터 타입을 넣을 수 있다.

- 또한 RedisRepository는 트랜잭션을 지원하지 않는다. 

/************
 * @info : Redis - Service Test - Template
 * @name : UserSerivceTest
 * @version : 1.0.0
 * @Description :
 ************/
@SpringBootTest
class UserSerivceTest {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

	// 저장 테스트
    @Test
    void testStrings() {
        //given
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
        String key = "first";

        //when
        valueOperations.set(key, "helloWorld!");

        //then
        String value = valueOperations.get(key);
        Assertions.assertThat(value).isEqualTo("helloWorld!");
    }

}

- 위의 테스트 코드와 같이 RedisTemplate를 주입해서 사용한다. 

- 제네릭의 타입을 명시해주어야 한다 -> Redis Config 파일에서 <?,?> 로 타입을 추론 하도록 설정했기 때문이다.

(주입된 재네릭 타입과, 사용 제네릭 타입이 다르면 어차피 에러가 발생한다.)

 

반응형

댓글