somoly.tistory.com

RedisTemplate 을 사용하여 json 으로 통신하기 위해 커스터마이징을 해 보도록 하겠습니다.

 

pom.xml 디펜던시 설정

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    <!--자동설정에서 connection pool 관련 설정이 들어가 있어 필요-->
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-pool2</artifactId>
    </dependency>

    <!--ObjectMapper 사용-->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
    </dependency>

    <dependency>
      <groupId>com.fasterxml.jackson.datatype</groupId>
      <artifactId>jackson-datatype-jsr310</artifactId>
    </dependency>

    <dependency>
      <groupId>com.fasterxml.jackson.datatype</groupId>
      <artifactId>jackson-datatype-jdk8</artifactId>
    </dependency>

    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
    </dependency>

    <!--junit5-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
      <exclusions>
        <exclusion>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
        </exclusion>
      </exclusions>
    </dependency>

    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-engine</artifactId>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

 

application.properties 서버 설정

jedis 말고, lettuce connection factory 를 이용하기 위해 lettuce 로 기본 설정

spring.redis.database=0
# Connection URL. Overrides host, port, and password. User is ignored. Example: redis://user:password@example.com:6379
spring.redis.url=redis://192.168.99.100:6379
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.max-wait=-1ms
spring.redis.lettuce.pool.min-idle=0
spring.redis.lettuce.shutdown-timeout=100ms
spring.redis.ssl=false
spring.redis.timeout=3s

 

테스트를 위한 User Class 를 생성합니다.

@Data
public class User {
    private String userId;
    private String username;
    private Optional<String> option;
    private Instant createdAt;
}

 

Entity Class 를 Json으로 변환시에 java 날짜, Optional 타입 매핑을 위해 아래와 같이 ObjectMapper 를 @Bean 으로 등록합니다

User Class 매핑을 위한 RedisTemplate @Bean 설정을 추가 합니다.

@Configuration
public class RedisConfig {
  @Bean
  public ObjectMapper objectMapper() {
    var mapper = new ObjectMapper();
    mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    mapper.registerModules(new JavaTimeModule(), new Jdk8Module());
    return mapper;
  }
  
  @Bean
  public RedisTemplate<String, User> userRedisTemplate(RedisConnectionFactory connectionFactory, ObjectMapper objectMapper) {
    var serializer = new Jackson2JsonRedisSerializer<>(User.class);
    serializer.setObjectMapper(objectMapper);

    var tpl = new RedisTemplate<String, User>();
    tpl.setConnectionFactory(connectionFactory);
    tpl.setKeySerializer(new StringRedisSerializer());
    tpl.setValueSerializer(serializer);
    tpl.setHashKeySerializer(new StringRedisSerializer());
    tpl.setHashValueSerializer(serializer);
    return tpl;
  }
}

 

junit 을 이용하여 hash operation 테스트를 해 봅니다.

@Slf4j
@SpringBootTest
class RedisTemplateTest {
  @Autowired
  private RedisTemplate<String, User> userRedisTemplate;
  
  private User newUser(String id) {
    var user = new User();
    user.setUserId(id);
    user.setUsername("Marcia Rose");
    user.setOption(Optional.empty());
    user.setCreatedAt(Instant.now());
    return user;
  }
  
  @Test
  void set_get_test() {
    userRedisTemplate.opsForHash().put("user", "1", newUser("1"));

    User user = (User) userRedisTemplate.opsForHash().get("user", "1");
    log.info("{}", user);

    assertThat(user).isNotNull();
    assertThat(user.getUserId()).isEqualTo("1");
    assertThat(user.getUsername()).isEqualTo("Marcia Rose");
    assertThat(user.getOption()).isEmpty();
  }
}

 

redis-cli 를 이용하여 직접 저장된 데이터를 조회하여 봅니다.

192.168.99.100:6379> hgetall user
1) "1"
2) "{\"userId\":\"1\",\"username\":\"Marcia Rose\",\"option\":null,\"createdAt\":\"2019-07-09T06:50:48.778837500Z\"}"

 

위와 같이 사용할 경우 Entity Class 가 여러개인 경우 @Bean 설정이 계속 추가 되어야 합니다.

따라서 범용 Jackson Serializer 를 이용하여 redisTemplate @Bean 을 생성해 보겠습니다.

Serializer 를 Jackson2JsonRedisSerializer -> GenericJackson2JsonRedisSerializer 로 변경하였습니다.

@Configuration
public class RedisConfig {
  @Bean
  public ObjectMapper objectMapper() {
    var mapper = new ObjectMapper();
    mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    mapper.registerModules(new JavaTimeModule(), new Jdk8Module());
    return mapper;
  }
  
  @Bean
  public RedisTemplate<String, Object> userRedisTemplate(RedisConnectionFactory connectionFactory, ObjectMapper objectMapper) {
    var serializer = new GenericJackson2JsonRedisSerializer(objectMapper);
    
    var tpl = new RedisTemplate<String, Object>();
    tpl.setConnectionFactory(connectionFactory);
    tpl.setKeySerializer(new StringRedisSerializer());
    tpl.setValueSerializer(serializer);
    tpl.setHashKeySerializer(new StringRedisSerializer());
    tpl.setHashValueSerializer(serializer);
    return tpl;
  }
}

 

junit 을 이용하여 다시 테스트 해 보도록 하겠습니다.

@Slf4j
@SpringBootTest
class RedisTemplateTest {
  @Autowired
  private RedisTemplate<String, Object> redisTemplate;
  
  private User newUser(String id) {
    var user = new User();
    user.setUserId(id);
    user.setUsername("Marcia Rose");
    user.setOption(Optional.empty());
    user.setCreatedAt(Instant.now());
    return user;
  }
  
  @Test
  void set_get_by_generic_jackson_serializer() {
    redisTemplate.opsForHash().put("user", "1", newUser("1"));

    Object result = userRedisTemplate.opsForHash().get("user", "1");
    
    // User result = (User) userRedisTemplate.opsForHash().get("user", "1");
    // 위와 같이 형 변환을 통해 바로 이용할 수 없음
    // java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class 에러
    // objectMapper.convertValue 를 이용하여 변환
    
    User user = objectMapper.convertValue(result, User.class);
    log.info("{}", user);

    assertThat(user).isNotNull();
    assertThat(user.getUserId()).isEqualTo("1");
    assertThat(user.getUsername()).isEqualTo("Marcia Rose");
    assertThat(user.getOption()).isEmpty();
  }
}

 

'SpringBoot' 카테고리의 다른 글

Spring 6 의 HTTP Interface  (0) 2023.03.14
Retrying Feign Calls  (0) 2023.03.14
MongoDB _class 필드 제거하기  (0) 2019.07.09
restful json request response 패킷 로깅 필터  (1) 2019.07.04
404 NoHandlerFoundException 설정  (1) 2019.07.04
profile

somoly.tistory.com

@RxCats

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!