diff --git a/build.gradle b/build.gradle index 23e302fd7..1e83a0521 100644 --- a/build.gradle +++ b/build.gradle @@ -37,6 +37,8 @@ dependencies { // swagger implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2' + // redis + implementation 'org.springframework.boot:spring-boot-starter-data-redis' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-web' diff --git a/src/main/java/balancetalk/global/config/RedisConfig.java b/src/main/java/balancetalk/global/config/RedisConfig.java new file mode 100644 index 000000000..1e028999f --- /dev/null +++ b/src/main/java/balancetalk/global/config/RedisConfig.java @@ -0,0 +1,35 @@ +package balancetalk.global.config; + +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.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +@Configuration +@EnableRedisRepositories +public class RedisConfig { + + @Value("${spring.data.redis.port}") + private int port; + + @Value("${spring.data.redis.host}") + private String host; + + @Bean + public RedisConnectionFactory redisConnectionFactory() { + return new LettuceConnectionFactory(host, port); + } + + @Bean + public RedisTemplate redisTemplate() { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new StringRedisSerializer()); + redisTemplate.setConnectionFactory(redisConnectionFactory()); + return redisTemplate; + } +} diff --git a/src/main/java/balancetalk/global/redis/application/RedisService.java b/src/main/java/balancetalk/global/redis/application/RedisService.java new file mode 100644 index 000000000..b102cf319 --- /dev/null +++ b/src/main/java/balancetalk/global/redis/application/RedisService.java @@ -0,0 +1,70 @@ +package balancetalk.global.redis.application; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +@Slf4j +@Component +@RequiredArgsConstructor +public class RedisService { + + private final RedisTemplate redisTemplate; + + public void setValues(String key, String data) { + ValueOperations values = redisTemplate.opsForValue(); + values.set(key, data); + } + + /* + key와 data, 만료 시간을 Redis에 저장 + */ + public void setValues(String key, String data, Duration duration) { + ValueOperations values = redisTemplate.opsForValue(); + values.set(key, data, duration); + } + + @Transactional(readOnly = true) + public String getValues(String key) { + ValueOperations 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 String getHashOps(String key, String hashKey) { + HashOperations values = redisTemplate.opsForHash(); + boolean exists = Boolean.TRUE.equals(values.hasKey(key, hashKey)); + if (exists) { + return (String) redisTemplate.opsForHash().get(key, hashKey); + } + return ""; // 빈 문자열 반환 + } + + public void deleteHashOps(String key, String hashKey) { + HashOperations values = redisTemplate.opsForHash(); + values.delete(key, hashKey); + } + + public boolean checkExistsValue(String value) { + return !value.equals("false"); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index b5452bdbb..7d5dbd195 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -24,5 +24,10 @@ spring: hibernate: format_sql: true + data: + redis: + host: 127.0.0.1 + port: 6379 + logging.level: org.hibernate.SQL: debug diff --git a/src/test/java/balancetalk/global/redis/application/RedisServiceTest.java b/src/test/java/balancetalk/global/redis/application/RedisServiceTest.java new file mode 100644 index 000000000..82e2e86af --- /dev/null +++ b/src/test/java/balancetalk/global/redis/application/RedisServiceTest.java @@ -0,0 +1,77 @@ +package balancetalk.global.redis.application; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import java.time.Duration; +import static org.assertj.core.api.Assertions.*; +import static org.awaitility.Awaitility.await; + +@SpringBootTest +class RedisCrudTEST { + + final String KEY = "key"; + final String VALUE = "value"; + final Duration DURATION = Duration.ofMillis(5000); + + @Autowired + private RedisService redisService; + + @BeforeEach + void setUp() { + redisService.setValues(KEY, VALUE, DURATION); + } + @Test + @DisplayName("Redis 데이터 저장후 조회 - 성공") + void Save_Find_Success() { + // when + String findValue = redisService.getValues(KEY); + + // then + assertThat(findValue).isEqualTo(VALUE); + } + + @Test + @DisplayName("Redis에 저장된 데이터 수정 - 성공") + void Update_Success() { + // given + String updatedValue = "updatedValue"; + redisService.setValues(KEY, updatedValue, DURATION); + + // when + String findValue = redisService.getValues(KEY); + + // then + assertThat(findValue).isEqualTo(updatedValue); + assertThat(findValue).isNotEqualTo(VALUE); + } + + @Test + @DisplayName("Redis에 저장된 데이터 삭제 - 성공") + void Delete_Success() { + // when + redisService.deleteValues(KEY); + + // then + assertThat(redisService.getValues(KEY)).isEqualTo("false"); + } + + @Test + @DisplayName("만료시간이 지간 데이터 삭제 - 성공") + void Expired_Success() { + // when + String findValue = redisService.getValues(KEY); + + // then + await().pollDelay(Duration.ofMillis(6000)).untilAsserted( + () -> { + String expiredValue = redisService.getValues(KEY); + assertThat(expiredValue).isNotEqualTo(findValue); + assertThat(expiredValue).isEqualTo("false"); + } + ); + } + +} \ No newline at end of file diff --git a/src/test/java/balancetalk/module/comment/application/CommentServiceTest.java b/src/test/java/balancetalk/module/comment/application/CommentServiceTest.java index fccff9b17..70b455699 100644 --- a/src/test/java/balancetalk/module/comment/application/CommentServiceTest.java +++ b/src/test/java/balancetalk/module/comment/application/CommentServiceTest.java @@ -17,6 +17,7 @@ import balancetalk.module.member.domain.Member; import balancetalk.module.member.domain.MemberRepository; +import balancetalk.module.post.domain.BalanceOption; import balancetalk.module.post.domain.Post; import balancetalk.module.post.domain.PostRepository; import balancetalk.module.vote.domain.Vote;