Backend/🌿Spring

[SpringBoot] Docker를 통한 EC2환경 Redis 설치 & 테스트 통과하기

발달중인 망고 2024. 4. 2. 21:21

한땀한땀 합성하기

안녕하세요. 저번 포스팅에서 좋아요 기능을 구현해 봤는데요. 생각해봤을때 화면 이동할때 마다 likeCount를 DB에 접근해서 불러오는게 매우 비효율적이라고 느껴져서 Redis를 한번 도입해 보기로 했습니다.

이번 포스팅은 좋아요 기능호출 이전단계인 설치 & Test 통과를 목표로 해보도록하겠습니다~

 

가이드 & 환경

  • SpringBoot3.2.2 Redis 설치
  • Ec2 인스턴스에 Docker를 이용한 Redis 설치
  • Ec2 인스턴스 보안 규칙 변경
  • Redis CRUD Test

 

Issue

오늘은 체크박스 2번인 구현 및 테스트까지 준비해봤습니다!!

 

 

Test  환경 조성

💡 gradle 의존성 및 YML 파일 설정

implementation 'org.springframework.boot:spring-boot-starter-data-redis'
spring:
  redis:
    host: {EC2퍼블릭 IPv4 주소}
    port: 6379

 

💡 SpringBoot Redis 기본 코드

@RequiredArgsConstructor
@Configuration
@EnableRedisRepositories
public class RedisConfig {

//    private static final Logger logger = LoggerFactory.getLogger(RedisConfig.class);

    @Value("${spring.redis.host}")
    private String redisHost;

    @Value("${spring.redis.port}")
    private int redisPort;

//    @PostConstruct
//    public void postConstruct() {
//        logger.info("Redis Host: {}", redisHost);
//        logger.info("Redis Port: {}", redisPort);
//    }

    // RedisProperties로 yaml에 저장한 host, post를 연결
    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory(redisHost, redisPort);
    }

    // serializer 설정으로 redis-cli를 통해 직접 데이터를 조회할 수 있도록 설정
    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        redisTemplate.setConnectionFactory(redisConnectionFactory());

        return redisTemplate;
    }
}

 

💡 RedisConfig 설정

@Value에 값이 잘 들어갔는지 확인해보고 싶으시면 주석을 지우시고 확인해보시면 됩니다!

@Slf4j
@Component
@RequiredArgsConstructor
public class RedisService {
    private final RedisTemplate<String, Object> redisTemplate;

    public void setValues(String key, String data) {
        ValueOperations<String, Object> values = redisTemplate.opsForValue();
        values.set(key, data);
    }

    public void setValues(String key, String data, Duration duration) {
        ValueOperations<String, Object> values = redisTemplate.opsForValue();
        values.set(key, data, duration);
    }

    @Transactional(readOnly = true)
    public String getValues(String key) {
        ValueOperations<String, Object> values = redisTemplate.opsForValue();
        if (values.get(key) == null) {
            return "false";
        }
        return (String) values.get(key);
    }

    public void deleteValues(String key) {
        redisTemplate.delete(key);
    }

    public void expireValues(String key, int timeout) {
        redisTemplate.expire(key, timeout, TimeUnit.MILLISECONDS);
    }

    public void setHashOps(String key, Map<String, String> data) {
        HashOperations<String, Object, Object> values = redisTemplate.opsForHash();
        values.putAll(key, data);
    }

    @Transactional(readOnly = true)
    public String getHashOps(String key, String hashKey) {
        HashOperations<String, Object, Object> values = redisTemplate.opsForHash();
        return Boolean.TRUE.equals(values.hasKey(key, hashKey)) ? (String) redisTemplate.opsForHash().get(key, hashKey) : "";
    }

    public void deleteHashOps(String key, String hashKey) {
        HashOperations<String, Object, Object> values = redisTemplate.opsForHash();
        values.delete(key, hashKey);
    }

    public boolean checkExistsValue(String value) {
        return !value.equals("false");
    }
}

 

EC2 환경 설정

💡 redis 이미지 파일 다운

docker pull redis 

💡 redis 실행

docker run --name {컨테이너 이름} -p 6379:6379 -d redis

💡 redis 실행 확인

docker exec -it {컨테이너 이름} redis-cli

💡 EC2 보안그룹 설정

EC2의 인바운드 규칙과 아웃바운드 규칙의 6379port 설정을 0.0.0.0/0으로 열어줍니다.

 

Redis TestCode

@Slf4j
@SpringBootTest
class RedisCrudTest {
    final String KEY = "key";
    final String VALUE = "value";
    final Duration DURATION = Duration.ofMillis(5000);
    @Autowired
    private RedisService redisService;

    @BeforeEach
    void shutDown() {
        redisService.setValues(KEY, VALUE, DURATION);
    }

    @AfterEach
    void tearDown() {
        redisService.deleteValues(KEY);
    }

    @Test
    @DisplayName("Redis에 데이터를 저장하면 정상적으로 조회된다.")
    void saveAndFindTest() throws Exception {
        // when
        String findValue = redisService.getValues(KEY);

        // then
        assertThat(VALUE).isEqualTo(findValue);
    }

    @Test
    @DisplayName("Redis에 저장된 데이터를 수정할 수 있다.")
    void updateTest() throws Exception {
        // given
        String updateValue = "updateValue";
        redisService.setValues(KEY, updateValue, DURATION);

        // when
        String findValue = redisService.getValues(KEY);

        // then
        assertThat(updateValue).isEqualTo(findValue);
        assertThat(VALUE).isNotEqualTo(findValue);
    }

    @Test
    @DisplayName("Redis에 저장된 데이터를 삭제할 수 있다.")
    void deleteTest() throws Exception {
        // when
        redisService.deleteValues(KEY);
        String findValue = redisService.getValues(KEY);

        // then
        assertThat(findValue).isEqualTo("false");
    }

    @Test
    @DisplayName("Redis에 저장된 데이터는 만료시간이 지나면 삭제된다.")
    void expiredTest() throws Exception {
        // when
        String findValue = redisService.getValues(KEY);
        await().pollDelay(Duration.ofMillis(6000)).untilAsserted(
                () -> {
                    String expiredValue = redisService.getValues(KEY);
                    assertThat(expiredValue).isNotEqualTo(findValue);
                    assertThat(expiredValue).isEqualTo("false");
                }
        );
    }
}

이렇게 해서 Test를 돌려보면~

 

Test 코드는 개발하는 콩님의 블로그에서 참고&사용하였습니다.
항상 좋은 레퍼런스 감사합니다.
읽어주셔서 감사합니다!
https://green-bin.tistory.com/69