redis 에 대해 공부하며 정리합니다. 틀린 부분이나 궁금한 부분은 댓글 달아주시면
답글 및 확인 후 수정하겠습니다.
jwt 를 구현하거나 선착순 쿠폰 발급등 이러한 이벤트 처리를 할때
Redis 를 많이 쓴다고 알고있다. 어떤 상황에서 알고있는지 어렴풋이 알고있기 때문에
왜 Redis 를 사용해야하고 redis 는 어떻게 동작하는지 알아보고자 합니다.
먼저 Redis 는 Nosql 의 DB 고 Key-value 저장소 입니다.
NoSQL 특징 2가지만 정리해 보겠습니다.
특히 KV 인 Redis는 사용법도 간단하고 속도도 매우 빠름
속도는 왜 빠를까?
위 그림을 보면 L1, L2 cache 라는 게 보입니다.
이부분은 우리가 알고 있는 CPU 에서 동작하는 곳입니다.
Redis 는 In-Memory 방식으로 RAM 에서 동작합니다. 따라서 CPU보단 느리지만 일반 DB 보단 더 빠르다는걸 이론상 확인할 수 있습니다.
따라서 복잡하지 않은 쿼리나 조회같은 것이 빈번하게 일어나는 경우 빠르게 처리하기 위해서 Redis 를 사용한다는것을 이해할 수 있습니다.
단점은?
Redis 는 In-Memory 방식으로 동작된다는 것을 알아봤는데
먼저 Redis 의 Persistence의 종류에 대해 알아보겠습니다.
제가 Redis 의 단점을 정리할때 휘발성이라고 말한것은 Redis 의 Cache 처리를 할때 Persistence가 없어 저장되지않고 날아가기때문입니다.
Persistence를 정의하면 저장하고 백업도가능합니다. 하지만 no persistence 상태에서 캐시처리를 할때만큼의 성능이 나오진않기때문에 trade off 가 발생하죠
이러한 점들을 잘 이용하여 상황에 맞게 설계하는것이 중요한 것 같습니다.
앞서 말했던것처럼 redis 는 빠른속도 와 데이터의 스키마를 따로 정의 할 필요없이 유연한 사용이 가능합니다.
또한 여러개의 was가 존재한다면 분산 환경에서도 유용하게 사용할 수 있습니다.
그래서 이번엔 MemberService 의 서버에서 회원 가입, 로그인 후 jwt 를 발급하여 이 token을 redis에 저장하고 다른 Microservice에서 회원의 정보를 조회할때, Access Token 만료시 Refresh Token 을 활용하여 Access Token을 재 발급하는 용도로 사용하고자 합니다.
발급받은 jwt토큰은 Persistence를 정의하지않고도 휘발된다 하더라도 큰 문제가 이루어질것이라고 생각했습니다. (재 로그인을 하면 되기때문)
redis 에 대한 설정은 다음과 같이 하였습니다.
@Configuration
public class RedisConfig {
@Value("${spring.data.redis.host}")
private String host;
@Value("${spring.data.redis.port}")
private int port;
// lettuce
@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(host, port);
}
@Bean
public <T> RedisTemplate<T, String> redisTemplate() {
RedisTemplate<T, String> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
redisTemplate.setEnableTransactionSupport(true);
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new StringRedisSerializer());
return redisTemplate;
}
}
실질적인 redis의 서비스가 이루어지는 곳 입니다.
@Service
public class RedisUtil {
@Autowired
private RedisTemplate redisTemplate;
public <T> long getExpire(T key) {
return redisTemplate.getExpire(key);
}
public <T> boolean hasValue(T key) {
ValueOperations<T, String> valueOperations = redisTemplate.opsForValue();
String refreshToken = valueOperations.get(key);
if (refreshToken == null) {
return false;
}
return true;
}
public <T> String getValue(T key) {
ValueOperations<T, String> valueOperations = redisTemplate.opsForValue();
return valueOperations.get(key);
}
public <T> void setValue(T key, String value, long timeout) {
ValueOperations<T, String> valueOperations = redisTemplate.opsForValue();
valueOperations.set(key, value, timeout, TimeUnit.MILLISECONDS);
}
public <T> void delete(T key) {
redisTemplate.delete(key);
}
}
물론 test 환경에서도 redis를 설치하여 직접 테스트 할 순있겠지만 Service 의 단위 테스트이기 때문에 다음과 같이 MockBean으로 처리를 하였습니다.
따라서 MockBean으로 등록하여 제가 의도한 응답이 나오도록 설정했습니다.
@MockBean
RedisUtil redisUtil;
@MockBean
JwtUtil jwtUtil;
@BeforeEach
void setUp() {
// redisUtil
when(redisUtil.getValue(any())).thenReturn("role_admin");
// jwtUtil
}
@Test
@DisplayName("룰생성")
void createRuleTests() {
// Rule 생성
RuleForm ruleForm = new RuleForm("이름", "소개", "3", "1", "BOJ", "GOLD");
Long ruleId = ruleService.create(ruleForm, 1L, "role_admin");
Rule rule = ruleService.getRule(ruleId).getData();
assertThat(rule.getId()).isEqualTo(ruleId);
assertThat(rule.getName()).isEqualTo(ruleForm.getName());
}
글이 잘 정리되어 있네요. 감사합니다.