ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring Boot] RedisJSON 연동
    Spring Boot/기타 2023. 7. 17. 22:16
    반응형

    RedisJSON 모듈은 Redis에 대한 JSON 지원을 제공하며, 다른 Redis 데이터 유형과 마찬가지로 Redis 데이터베이스에서 JSON 값을 저장, 업데이트 및 검색할 수 있습니다.

    RedisJSON : https://redis.io/docs/stack/json/

     

    1. Docker를 사용하여 Redis 설치

    Docker를 사용하여 Redis를 로컬에 설치하세요.

    docker run -d --name redis-stack -p 6379:6379 -p 8001:8001 redis/redis-stack:latest

    포트 6379는 Redis 연결, 포트 8001는 RedisInsight에 사용됩니다.

    Docker Hub : https://hub.docker.com/r/redis/redis-stack

     

    RedisInsight

    RedisInsight : https://redis.com/redis-enterprise/redis-insight/

     

    2. 의존성 추가 

    Redis Json 명령어를 사용하기 위해 'Jedis'를 추가하세요.

    build.gradle

    plugins {
        id 'java'
        id 'org.springframework.boot' version '3.2.2'
        id 'io.spring.dependency-management' version '1.1.4'
    }
    
    group = 'com.example'
    version = '0.0.1-SNAPSHOT'
    
    java {
        sourceCompatibility = '17'
    }
    
    repositories {
        mavenCentral()
    }
    
    dependencies {
    
        // SpringBoot
        implementation 'org.springframework.boot:spring-boot-starter-web'
        testImplementation 'org.springframework.boot:spring-boot-starter-test'
    
        // Redis
        implementation 'org.springframework.boot:spring-boot-starter-data-redis'
    
        // Jedis
        implementation 'redis.clients:jedis:5.1.0'
    
        // Lombok
        compileOnly 'org.projectlombok:lombok'
        annotationProcessor 'org.projectlombok:lombok'
        testCompileOnly 'org.projectlombok:lombok'
        testAnnotationProcessor 'org.projectlombok:lombok'
    }
    
    test {
        exclude '**/*'
    }

    Maven Repository : https://mvnrepository.com/artifact/redis.clients/jedis/5.1.0

     

    3. RedisConfig 설정

    Redis 클라이언트 라이브러리인 Jedis를 사용하여 Redis 서버와의 연결을 설정하세요.
    RedisConfig.java

    package com.example.jedissample.config;
    
    import org.springframework.beans.factory.annotation.Value;
    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.RedisStandaloneConfiguration;
    import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
    import redis.clients.jedis.JedisPooled;
    
    @Configuration
    public class RedisConfig {
    
      @Value("${redis.stand-alone.host}")
      private String standAloneHost;
    
      @Value("${redis.stand-alone.port}")
      private String standAlonePort;
    
    
      /**
       * RedisConnectionFactory 빈을 생성
       *
       * @return RedisConnectionFactory 빈
       */
      @Bean
      public RedisConnectionFactory redisConnectionFactory() {
        return new JedisConnectionFactory(
            new RedisStandaloneConfiguration(standAloneHost, Integer.parseInt(standAlonePort)));
      }
    
      /**
       * RedisConnectionFactory를 사용하여 JedisPooled 빈을 생성
       *
       * @param redisConnectionFactory RedisConnectionFactory
       * @return JedisPooled 빈
       */
      @Bean
      public JedisPooled jedisPooled(RedisConnectionFactory redisConnectionFactory) {
        JedisConnectionFactory jedisConnectionFactory =
            (JedisConnectionFactory) redisConnectionFactory;
        return new JedisPooled(
            jedisConnectionFactory.getPoolConfig(),
            jedisConnectionFactory.getHostName(),
            jedisConnectionFactory.getPort());
      }
    }

     

    4. RedisComponent 설정

    Redis에 데이터 조회 및 저장을 위해 JedisCluster 객체를 사용하여 메서드를 구현하세요.
    RedisComponent.java

    package com.example.jedissample.component;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    import lombok.RequiredArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Component;
    import org.springframework.util.ObjectUtils;
    import redis.clients.jedis.JedisPooled;
    import redis.clients.jedis.json.Path;
    
    @Slf4j
    @RequiredArgsConstructor
    @Component
    public class RedisComponent {
    
      private final JedisPooled jedisPooled;
    
      /**
       * Redis에 객체를 JSON 형식으로 저장
       *
       * @param key     Redis 키
       * @param t       저장할 데이터 객체
       * @param seconds 만료 시간(초)
       * @param <T>     데이터 객체의 유형
       */
      public <T> void setJson(String key, T t, long seconds) {
        jedisPooled.jsonSetLegacy(key, t);
        jedisPooled.expire(key, seconds);
      }
    
      /**
       * Redis에서 키를 사용하여 저장된 객체를 조회
       *
       * @param key   Redis 키
       * @param clazz 검색할 데이터 객체의 클래스 유형
       * @param <T>   검색할 데이터 객체의 유형
       * @return Redis에서 검색한 데이터 객체
       */
      public <T> T getJson(String key, Class<T> clazz) {
        return jedisPooled.jsonGet(key, clazz);
      }
    
    
      /**
       * 키와 경로를 사용하여 Redis에서 저장된 객체를 조회
       *
       * @param key  Redis 키
       * @param path 가져올 데이터 객체의 경로
       * @return Redis에서 검색한 데이터 객체
       */
      public Object getJson(String key, Path path) {
        return jedisPooled.jsonGet(key, path);
      }
    
    
      /**
       * Redis에서 저장된 JSON 배열을 조회
       *
       * @param key   Redis에서의 키
       * @param clazz 검색할 데이터 객체의 클래스 유형
       * @param path  JSON 배열의 경로
       * @param <T>   검색할 데이터 객체의 유형
       * @return 검색된 데이터 객체의 목록
       * @throws IOException JSON을 파싱하는 동안 발생할 수 있는 예외
       */
      public <T> List<T> getJsonArray(String key, Class<T> clazz, String path) throws IOException {
    
        List<T> list = new ArrayList<>();
    
        Object object = jedisPooled.jsonGet(key, new Path(path));
        if (!ObjectUtils.isEmpty(object)) {
          ObjectMapper objectMapper = new ObjectMapper();
          String jsonString = objectMapper.writeValueAsString(object);
          list = objectMapper.readValue(
              jsonString,
              objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
        }
    
        return list;
      }
    }

     

    5. EmployeeService 추가

    EmployeeService.java

    package com.example.jedissample.service;
    
    import com.example.jedissample.component.RedisComponent;
    import com.example.jedissample.dto.EmployeeDto;
    import java.util.List;
    import lombok.RequiredArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Service;
    
    /** *
     * EmployeeDto 객체를 Redis에 저장 및 조회
     *
     * <p>
     * Storing and Querying JSON documents using Redis Stack
     * https://developer.redis.com/howtos/redisjson/getting-started
     * </p>
     */
    @Slf4j
    @RequiredArgsConstructor
    @Service
    public class EmployeeService {
    
      private final RedisComponent redisComponent;
    
      /**
       * EmployeeDto.Info를 Redis에 JSON 형식으로 저장
       *
       * @param key          Redis 키
       * @param employeeInfo EmployeeDto.Info
       * @return EmployeeDto.Info
       */
      public EmployeeDto.Info saveEmployeeInfo(String key, EmployeeDto.Info employeeInfo) {
        redisComponent.setJson(key, employeeInfo, 60 * 5);
        return redisComponent.getJson(key, EmployeeDto.Info.class);
      }
    
      /**
       * EmployeeDto를 Redis에 JSON 형식으로 저장
       *
       * @param key      Redis 키
       * @param employee EmployeeDto
       * @return EmployeeDto
       */
      public EmployeeDto saveEmployee(String key, EmployeeDto employee) {
        redisComponent.setJson(key, employee, 60 * 5);
        return redisComponent.getJson(key, EmployeeDto.class);
      }
    
      /**
       * 키(key)를 사용하여 Redis에 저장된 객체를 조회
       *
       * @param key Redis 키
       * @return EmployeeDto
       */
      public EmployeeDto findEmployee(String key) {
        return redisComponent.getJson(key, EmployeeDto.class);
      }
    
      /**
       * 키(key)와 경로(path)를 사용하여 Redis에 저장된 객체를 조회
       *
       * @param key  Redis 키
       * @param path 경로
       * @return List&lt;EmployeeDto.Info&lt;
       */
      public List<EmployeeDto.Info> findEmployee(String key, String path) throws Exception {
        return redisComponent.getJsonArray(key, EmployeeDto.Info.class, path);
      }
    }

     

    6. EmployeeDto 추가

    EmployeeDto.java

    package com.example.redisjson.dto;
    
    import java.util.List;
    import lombok.AllArgsConstructor;
    import lombok.Builder;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    import lombok.Setter;
    
    @Getter
    @Setter
    @Builder
    public class EmployeeDto {
    
      List<Info> employees;
    
      @Getter
      @Setter
      @Builder
      @NoArgsConstructor
      @AllArgsConstructor
      public static class Info {
    
        private String name;
        private String email;
        private int age;
      }
    }

     

    7. 저장 및 조회 확인

    EmployeeServiceTest.java

    package com.example.jedissample.service;
    
    import static org.junit.jupiter.api.Assertions.assertFalse;
    
    import com.example.jedissample.dto.EmployeeDto;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import java.util.ArrayList;
    import java.util.List;
    import lombok.extern.slf4j.Slf4j;
    import org.junit.jupiter.api.DisplayName;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.util.ObjectUtils;
    
    @Slf4j
    @SpringBootTest
    class EmployeeServiceTest {
    
      @Autowired
      private EmployeeService employeeService;
    
      @Autowired
      private ObjectMapper objectMapper;
    
      @DisplayName("saveEmployeeInfo_EmployeeDto.Info를 Redis에 JSON 형식으로 저장")
      @Test
      void testSaveEmployeeInfo() throws Exception {
    
        // Given
        String key = "employee_profile";
        EmployeeDto.Info employeeInfo = EmployeeDto.Info.builder()
            .name("Paul John")
            .email("paul.john@example.com")
            .age(42)
            .build();
    
        // When
        EmployeeDto.Info responseEmployeeInfo = employeeService.saveEmployeeInfo(key, employeeInfo);
    
        // Then
        log.debug("responseEmployeeInfo: {}", objectMapper.writeValueAsString(responseEmployeeInfo));
        assertFalse(ObjectUtils.isEmpty(responseEmployeeInfo));
      }
    
      @DisplayName("saveEmployee_EmployeeDto를 Redis에 JSON 형식으로 저장")
      @Test
      void testSaveEmployee() throws Exception {
    
        // Given
        String key = "employee_info";
        List<EmployeeDto.Info> employees = new ArrayList<>();
        employees.add(EmployeeDto.Info.builder()
            .name("Alpha")
            .email("alpha@gmail.com")
            .age(23)
            .build());
        employees.add(EmployeeDto.Info.builder()
            .name("Beta")
            .email("beta@gmail.com")
            .age(28)
            .build());
        employees.add(EmployeeDto.Info.builder()
            .name("Gamma")
            .email("gamma@gmail.com")
            .age(33)
            .build());
        employees.add(EmployeeDto.Info.builder()
            .name("Theta")
            .email("theta@gmail.com")
            .age(41)
            .build());
    
        EmployeeDto employee = EmployeeDto.builder()
            .employees(employees)
            .build();
    
        // When
        EmployeeDto responseEmployee = employeeService.saveEmployee(key, employee);
    
        // Then
        log.debug("responseEmployee: {}", objectMapper.writeValueAsString(responseEmployee));
        assertFalse(ObjectUtils.isEmpty(responseEmployee));
      }
    
      @DisplayName("findEmployee_키(key)를 사용하여 Redis에 저장된 객체를 조회")
      @Test
      void testFindEmployee() throws Exception {
    
        // Given
        String key = "employee_info";
    
        // When
        EmployeeDto responseEmployee = employeeService.findEmployee(key);
    
        // Then
        log.debug("responseEmployee: {}", objectMapper.writeValueAsString(responseEmployee));
        assertFalse(ObjectUtils.isEmpty(responseEmployee));
      }
    
      @DisplayName("findEmployee_키(key)와 경로(path)를 사용하여 Redis에 저장된 객체를 조회")
      @Test
      void testFindEmployeePath() throws Exception {
    
        // Given
        String key = "employee_info";
        String path = "$.employees[?(@.age>20&&@.age<40)]";
    
        // When
        List<EmployeeDto.Info> responseEmployee = employeeService.findEmployee(key, path);
    
        // Then
        log.debug("responseEmployee: {}", objectMapper.writeValueAsString(responseEmployee));
        assertFalse(ObjectUtils.isEmpty(responseEmployee));
      }
    }

     

    8. 콘솔 확인

     

    9. RedisInsight 확인

     

    redisdeveloper : https://developer.redis.com/

    반응형

    댓글

Designed by Tistory.