[Spring Redis] Redis

in_ho_·2023년 11월 12일
0

Spring Redis

목록 보기
1/1
post-thumbnail

1. Redis란?

  • Redis는 고성능, 인메모리 키-값 데이터 저장소입니다.
  • 빠른 성능과 유연한 데이터 구조로 데이터를 캐싱하는 등의 역할을 주로 담당합니다.
  • Redis는 인메모리 데이터베이스임에도 불구하고 데이터를 디스크에 저장하므로, 시스템 재시작 후에도 데이터를 복구할 수 있습니다.

2. Redis 설치

  • 해당 설치 방법은 docker를 이용하였습니다.
docker run --name redis -p 8379:6379 -d redis redis-server --requirepass yourpassword

3. Redis 설치 확인

docker exec -it 컨테이너명 /bin/bash

# redis shell 접근 후
redis-cli -p 6379 -a [password]

4. Spring 설정

4-1. yaml 파일 설정

spring:
	redis:
    	host: localhost
        port: 8379
        password: [password]

현재 Deprecated되어 이를 아래와 같이 수정해줍니다.(Spring Boot 3.X 버전)

  data:
    redis:
      host: localhost
      port: 8379
      password: [password]

4-2. Java Configuration

@Configuration
@EnableRedisRepositories
public class RedisConfiguration {
    private Logger log = LoggerFactory.getLogger(RedisConfiguration.class);
    @Value("${spring.data.redis.host}")
    private String host
    @Value("${spring.data.redis.port}")
    private int port;
    @Value("${spring.data.redis.password}")
    private String password;
    /**
     * Redis와 연결
     * @return RedisConnectionFactory
     */
    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        RedisStandaloneConfiguration redisConfiguration = new RedisStandaloneConfiguration();
        redisConfiguration.setHostName(host);
        redisConfiguration.setPort(port);
        redisConfiguration.setPassword(password);
        return new LettuceConnectionFactory(redisConfiguration);
    }
    /**
     * RedisConnection에서 넘겨준 byte값 객체 직렬화
     * @return RedisTemplate<?, ?>
     */
    @Primary
    @Bean
    public RedisTemplate<String, String> redisTemplate() {
        RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory());
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        return redisTemplate;
    }
}
  • redis를 Lettuce를 사용했습니다.
  • redisTemplate을 통해 redis를 사용할 수 있습니다.

    @Primary는 동일한 타입의 여러 빈이 존재할 경우 기본 Bean으로 정의할 것을 지정합니다.

5. 인터페이스 생성

  • RedisService라는 인터페이스를 생성합니다.
public interface RedisService {
    /**
     * Redis 연결 여부 확인
     * @return boolean
     */
    boolean isConnected();

    /**
     * 특정 데이터를 저장
     * @param key 키
     * @param value 값
     * @param duration 만료 시간
     */
    void setDataExpire(String key, String value, long duration);

    /**
     * 특정 키 값 삭제
     * @param key 키
     */
    void deleteData(String key);

    /**
     * 특정 키의 값을 가져옴
     * @param key 키
     * @return 값
     */
    String getData(String key);

    /**
     * 특정 키에 해당하는 값의 존재 유무
     * @param key 키
     * @return 값의 존재 유무
     */
    boolean existData(String key);
}

6. 구현체 생성

@RequiredArgsConstructor
@Service
public class RedisServiceImpl implements RedisService {

    private final RedisTemplate<String, String> redisTemplate;
    private final Logger log = LoggerFactory.getLogger(RedisService.class);

    @Override
    public boolean isConnected() {
        try {
            String pingResponse = redisTemplate.getConnectionFactory().getConnection().ping();

            return "PONG".equals(pingResponse);
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public void setDataExpire(String key, String value, long duration) {
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
        Duration expireDuration = Duration.ofSeconds(duration);
        valueOperations.set(key, value, expireDuration);
    }

    @Override
    public void deleteData(String key) {
        redisTemplate.delete(key);
    }

    @Override
    public String getData(String key) {
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
        return valueOperations.get(key);
    }

    @Override
    public boolean existData(String key) {
        return Boolean.TRUE.equals(redisTemplate.hasKey(key));
    }
}

7. Test Code 작성

  • RedisServiceImplTest 파일을 생성합니다.

    테스트 코드를 습관을 들이는 중이기 때문에 다소 부족할 수 있습니다.

@SpringBootTest
class RedisServiceImplTest {

    @Autowired
    RedisService redisService;

    Logger log = LoggerFactory.getLogger(RedisServiceImplTest.class);

    @Test
    @DisplayName("연결 확인 - isConnected")
    void isConnected() {
        boolean connected = redisService.isConnected();

        // 연결 확인
        assertTrue(connected, "Redis와 연결하지 못했습니다.");
    }

    @Test
    @DisplayName("만료기간 있는 데이터를 저장 - setDataExpire")
    void setDataExpire() throws InterruptedException {
        redisService.setDataExpire("TEST", "1234", 10);

        String redisData = redisService.getData("TEST");

        // 데이터 검증
        assertNotNull(redisData, "Redis에서 데이터를 검색하지 못했습니다.");
        assertEquals("1234", redisData, "검색된 데이터가 예상과 다릅니다.");

        // 만료 시간 후 데이터 검증
        Thread.sleep(10000); // 10초 대기
        redisData = redisService.getData("TEST");
        assertNull(redisData, "데이터가 만료 시간 후에도 여전히 존재합니다.");
    }

    @Test
    @DisplayName("특정 데이터를 삭제 - deleteData")
    void deleteData() {
        redisService.setDataExpire("TEST", "1234", 10);

        redisService.deleteData("TEST");

        // 데이터 검증
        String redisData = redisService.getData("TEST");
        assertNull(redisData, "Redis에서 데이터를 삭제하지 못했습니다.");
    }

    @Test
    @DisplayName("특정 데이터 조회 - getData")
    void getData() {
        redisService.setDataExpire("TEST", "1234", 10);
        String redisData = redisService.getData("TEST");

        assertNotNull(redisData, "Redis에서 데이터를 검색하지 못했습니다.");
    }

    @Test
    @DisplayName("특정 데이터가 존재하는지 여부 - existData")
    void existData() {
        redisService.setDataExpire("TEST", "1234", 10);
        boolean existData = redisService.existData("TEST");

        assertTrue(existData, "Redis에서 데이터를 검색하지 못했습니다.");
    }
}

0개의 댓글