Redis 아는척 해보기

박우영·2023년 7월 18일
0

DB

목록 보기
3/6

개요

redis 에 대해 공부하며 정리합니다. 틀린 부분이나 궁금한 부분은 댓글 달아주시면 
답글 및 확인 후 수정하겠습니다.

jwt 를 구현하거나 선착순 쿠폰 발급등 이러한 이벤트 처리를 할때
Redis 를 많이 쓴다고 알고있다. 어떤 상황에서 알고있는지 어렴풋이 알고있기 때문에
왜 Redis 를 사용해야하고 redis 는 어떻게 동작하는지 알아보고자 합니다.

Redis 알아보기

먼저 Redis 는 Nosql 의 DB 고 Key-value 저장소 입니다.

NoSQL 특징 2가지만 정리해 보겠습니다.

  • 스키마 없이 동작하며, 구조에 대한 정의를 변경할 필요 없이 데이터베이스 레코드에 자유롭게 필드를 추가할 수 있다.
  • 수평적 확장(scale-out)에 용이하다.

특히 KV 인 Redis는 사용법도 간단하고 속도도 매우 빠름

속도는 왜 빠를까?

위 그림을 보면 L1, L2 cache 라는 게 보입니다.
이부분은 우리가 알고 있는 CPU 에서 동작하는 곳입니다.

Redis 는 In-Memory 방식으로 RAM 에서 동작합니다. 따라서 CPU보단 느리지만 일반 DB 보단 더 빠르다는걸 이론상 확인할 수 있습니다.

따라서 복잡하지 않은 쿼리나 조회같은 것이 빈번하게 일어나는 경우 빠르게 처리하기 위해서 Redis 를 사용한다는것을 이해할 수 있습니다.

단점은?

  • 스키마가 없이 동작하기 때문에 SQLDB 에서 보장하는 ACID 를 제공하지 않습니다.
  • 휘발성 데이터다.
    • 이부분은 Redis 의 persistence 에서 다루겠습니다.

Redis 는 In-Memory 방식으로 동작된다는 것을 알아봤는데

Redis persistence

먼저 Redis 의 Persistence의 종류에 대해 알아보겠습니다.

  • RDB (Redis Database): RDB persistence performs point-in-time snapshots of your dataset at specified intervals.
  • AOF (Append Only File): AOF persistence logs every write operation received by the server. These operations can then be replayed again at server startup, reconstructing the original dataset. Commands are logged using the same format as the Redis protocol itself.
  • No persistence: You can disable persistence completely. This is sometimes used when caching.
  • RDB + AOF: You can also combine both AOF and RDB in the same instance.

제가 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 설정

RedisConfig

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;
    }
}

RedisUtil

실질적인 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 시엔?

물론 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());
    }

참고


속도 사진(bytebytego)
Redis 홈페이지

2개의 댓글

comment-user-thumbnail
2023년 7월 18일

글이 잘 정리되어 있네요. 감사합니다.

1개의 답글