안녕하세요. 저번 포스팅에서 좋아요 기능을 구현해 봤는데요. 생각해봤을때 화면 이동할때 마다 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
'Backend > 🌿Spring' 카테고리의 다른 글
[SpringBoot] N + 1 문제 해결 및 성능 개선 (0) | 2024.04.10 |
---|---|
[SpringBoot] Redis 캐싱을 통한 좋아요 조회 성능 비교 (0) | 2024.04.08 |
[SpringBoot] 좋아요 기능 구현 및 생각 (0) | 2024.03.27 |
[SpringBoot] 협업을 위한 Swagger 사용법 (0) | 2024.03.11 |
[SpringBoot] AWS S3 Bucket을 이용한 이미지 업로드 + CloudFront설정 (0) | 2024.03.06 |