-
[Spring Boot] Jedis를 활용하여 Redis에 대한 CRUD 작업을 테스트Spring Boot/기타 2024. 12. 11. 20:00반응형
목차
Jedis를 활용하여 Redis에 대한 CRUD 작업을 수행하는 방법을 설명하겠습니다.
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와 같은 GUI 도구와의 연결
참고
Docker Hub - redis/redis-stack
2. 의존성 추가build.gradle 파일에 Redis 및 Jedis와의 연동에 필요한 의존성을 추가합니다.
build.gradledependencies { // Redis implementation('org.springframework.boot:spring-boot-starter-data-redis') { exclude group: 'io.lettuce', module: 'lettuce-core' } // Jedis implementation 'redis.clients:jedis:5.1.0' }
참고
3. RedisConfig 설정
Redis 서버와의 연동을 위해 JedisPooled 객체를 빈으로 등록합니다.
RedisConfig.java
package com.example.jedis.config; import java.util.Objects; 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( Objects.requireNonNull(jedisConnectionFactory.getPoolConfig()), jedisConnectionFactory.getHostName(), jedisConnectionFactory.getPort()); } }
4. RedisComponent 구현
RedisComponent 클래스는 Jedis를 활용해 데이터를 저장하고, 조회하는 기능을 제공합니다.
RedisComponent에서 사용한 Jedis 메서드- set(String key, String value) : 주어진 키에 문자열 값을 저장합니다. 기존 값이 있다면 덮어씁니다.
- expire(String key, long seconds) : 특정 키에 대한 만료 시간을 설정합니다. 초 단위로 설정되며, 해당 시간이 지나면 키와 값이 삭제됩니다.
- get(String key) : 특정 키와 연관된 값을 조회합니다. 키가 존재하지 않으면 null을 반환합니다.
- jsonSetWithEscape(String key, Object t) : 객체를 JSON 형식으로 변환하고, 지정된 키에 저장합니다.
- jsonGet(String key) : JSON 형태로 저장된 데이터를 객체로 변환하여 가져옵니다. 저장된 데이터가 없는 경우 null을 반환합니다.
참고
RedisComponent의 메서드
RedisComponent는 Jedis 메서드들을 활용하여 Redis 서버와의 연동을 위해 필요한 메서드를 제공합니다.- setString(String key, String value, long ttl) : set 메서드를 사용하여 문자열 데이터를 Redis에 저장하고, expire 메서드를 이용해 키의 만료 시간을 설정합니다.
- getString(String key) : get 메서드를 사용하여 Redis에 저장된 문자열 값을 조회합니다. 키가 존재하지 않으면 null을 반환합니다.
- setJson(String key, T t, long ttl) : jsonSetWithEscape를 사용하여 객체를 JSON으로 변환한 후 저장하고, expire를 활용하여 키의 만료 시간을 설정합니다.
- getJsonObject(String key, Class<T> clazz) : jsonGet을 사용해 특정 키의 데이터를 가져오고, 지정된 클래스 타입(clazz)으로 변환하여 반환합니다.
RedisComponent.java
package com.example.jedis.component; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.type.TypeFactory; import java.util.Collections; import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.json.JSONArray; import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; import redis.clients.jedis.JedisPooled; import redis.clients.jedis.json.Path2; @Slf4j @RequiredArgsConstructor @Component public class RedisComponent { private final JedisPooled jedisPooled; private final ObjectMapper objectMapper; /** * 문자열을 저장하고, 지정된 TTL(만료 시간)을 설정 * * @param key 저장할 키 * @param value 저장할 문자열 * @param ttl 만료 시간(초 단위) */ public void setString(String key, String value, long ttl) { jedisPooled.set(key, value); jedisPooled.expire(key, ttl); } /** * 키를 사용하여 저장된 문자열을 조회 * * @param key 검색할 키 * @return 저장된 문자열을 반환. 키가 존재하지 않으면 null을 반환 */ public String getString(String key) { return jedisPooled.get(key); } /** * 객체를 JSON 형식으로 저장하고, 지정된 TTL(만료 시간)을 설정 * * @param key 저장할 키 * @param t 저장할 객체 * @param ttl 만료 시간(초 단위) * @param <T> 저장할 객체의 유형 */ public <T> void setJson(String key, T t, long ttl) { jedisPooled.jsonSetWithEscape(key, t); jedisPooled.expire(key, ttl); } /** * 키를 사용하여 저장된 객체를 조회 * * @param key 검색할 키 * @param clazz 검색할 객체의 클래스 유형 * @param <T> 검색할 객체의 유형 * @return 저장된 객체를 반환. JSON 파싱 실패 시 null을 반환 */ public <T> T getJsonObject(String key, Class<T> clazz) { try { Object object = jedisPooled.jsonGet(key); if (object != null && !ObjectUtils.isEmpty(object.toString())) { String jsonString = objectMapper.writeValueAsString(object); return objectMapper.readValue(jsonString, clazz); } } catch (JsonProcessingException e) { log.error("getJson key : {}", key, e); } return null; } }
5. 테스트 클래스 구현
JUnit을 사용하여 RedisComponent의 기능을 테스트하는 코드를 작성합니다. 이 코드는 문자열 저장 및 조회, 단일 사용자 저장 및 조회, 사용자 목록 저장 및 조회를 다룹니다.
메서드 설명
- testSetAndGetString() : Redis에 문자열 데이터를 저장하고(setString), 저장된 데이터를 다시 조회(getString)하여 유효성을 테스트합니다.
- testSetAndGetJsonUser() : 단일 사용자 객체를 Redis에 JSON 형식으로 저장(setJson)하고, 이를 객체(getJsonObject)로 다시 조회하여 유효성을 확인합니다.
- testSetAndGetJsonUserList() : 사용자 객체들의 리스트를 Redis에 JSON 형식으로 저장(setJson)하고, 이를 리스트 형태의 객체로 다시 조회하여 유효성을 테스트합니다.
JedisGuideTest.javapackage com.example.jedis.service; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import com.example.jedis.component.RedisComponent; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Arrays; import java.util.List; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; /** * Jedis를 활용하여 Redis에 대한 CRUD 작업을 테스트 * <p> * <a href="https://redis.io/docs/latest/develop/clients/jedis/">Jedis guide(Java)</a> */ @Slf4j @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @SpringBootTest class JedisGuideTest { private static final long TTL_SECONDS = 60 * 10; @Autowired RedisComponent redisComponent; @Autowired ObjectMapper objectMapper; @Order(1) @DisplayName("문자열 저장 및 조회") @Test void testSetAndGetString() { // Given String redisKey = "string:1"; String value = "Hello, Redis!"; redisComponent.setString(redisKey, value, TTL_SECONDS); // When String retrievedValue = redisComponent.getString(redisKey); log.debug("testSetAndGetString value : {}", retrievedValue); // Then assertAll( () -> assertNotNull(retrievedValue), () -> assertEquals(value, retrievedValue) ); } @Order(2) @DisplayName("단일 사용자 저장 및 조회") @Test void testSetAndGetJsonUser() { // Given String redisKey = "user:1"; UserDto.User user1 = UserDto.User.of("Paul John", "paul.john@example.com", 42, "London"); redisComponent.setJson(redisKey, user1, TTL_SECONDS); // When UserDto.User retrievedUser = redisComponent.getJsonObject(redisKey, UserDto.User.class); log.debug("testSetAndGetJsonObject user : {}", retrievedUser); // Then assertAll( () -> assertNotNull(retrievedUser), () -> assertEquals(user1.getName(), retrievedUser.getName()), () -> assertEquals(user1.getEmail(), retrievedUser.getEmail()), () -> assertEquals(user1.getAge(), retrievedUser.getAge()), () -> assertEquals(user1.getCity(), retrievedUser.getCity()) ); } @Order(3) @DisplayName("사용자 목록 저장 및 조회") @Test void testSetAndGetJsonUserList() throws Exception { // Given String redisKey = "user_list"; List<UserDto.User> userList = Arrays.asList( UserDto.User.of("Paul John", "paul.john@example.com", 42, "London"), UserDto.User.of("Eden Zamir", "eden.zamir@example.com", 29, "Tel Aviv"), UserDto.User.of("Paul Zamir", "paul.zamir@example.com", 35, "Tel Aviv") ); UserDto userDto = UserDto.builder().userList(userList).build(); redisComponent.setJson(redisKey, userDto, TTL_SECONDS); // When UserDto retrievedUser = redisComponent.getJsonObject(redisKey, UserDto.class); log.debug("testSetAndGetJsonList userDto : {}", objectMapper.writeValueAsString(retrievedUser)); // Then assertAll( () -> assertNotNull(retrievedUser), () -> assertNotNull(retrievedUser.getUserList()), () -> assertEquals(userList.size(), retrievedUser.getUserList().size()) ); } @Getter @Builder @AllArgsConstructor @NoArgsConstructor public static class UserDto { private List<User> userList; @Getter @Builder @AllArgsConstructor(staticName = "of") @NoArgsConstructor public static class User { private String name; private String email; private int age; private String city; } } }
자세한 소스 코드는 Github Repository를 참조하세요.
관련 글
반응형'Spring Boot > 기타' 카테고리의 다른 글
[Spring Boot] Apache HttpClient 5 기반 RestClient 구성하기 (0) 2024.12.29 [Spring Boot] Jedis를 활용하여 Redis에 JSON Path 기능을 테스트 (0) 2024.12.11 [Spring Boot] RestClient, HttpInterface를 활용한 HTTP 요청 (0) 2024.11.17 [Spring Boot] RestClient를 활용한 HTTP 요청 (0) 2024.11.10 [Spring Boot] RestTemplate를 활용한 HTTP 요청 (0) 2024.11.10