SpringBoot - Redis 키 설정

박찬규·2023년 7월 13일

GoodJobProject

목록 보기
8/9

컨트롤러 통합 테스트를 진행하면서 로그인 후 레디스에 jwt 토큰을 저장해주는 로직을 검증하는 과정에서 NullPointerException이 발생했다.

  • MemberControllerTest.java
	@Test
    @DisplayName("로그인 성공")
    void loginSuccess() throws Exception {
        // WHEN
        ResultActions resultActions = mockMvc
                .perform(post("/member/login")
                        .contentType(MediaType.MULTIPART_FORM_DATA)
                        .param("username", "test")
                        .param("password", "1234")
                )
                .andDo(print());

        // THEN
        MvcResult mvcResult = resultActions
                .andExpect(status().is3xxRedirection())
                .andExpect(handler().handlerType(MemberController.class))
                .andExpect(handler().methodName("login"))
                .andReturn();

        // 쿠키 검증
        MockHttpServletResponse response = mvcResult.getResponse();
        Cookie[] cookies = response.getCookies();
        assertThat(cookies).isNotNull();
        assertThat(cookies).hasSize(2);

        // 액세스 토큰 검증
        Cookie accessToken = getCookie(cookies, "accessToken");
        assertThat(accessToken).isNotNull();

        // 리프레시 토큰 검증
        Cookie refreshToken = getCookie(cookies, "refreshToken");
        assertThat(refreshToken).isNotNull();

        // 레디스 저장 확인 및 토큰 값 비교
        Member member = memberService.findByUsername("test").orElse(null);
        String value = redisUt.getValue(String.valueOf(member.getId()));

        assertThat(value).isEqualTo(refreshToken.getValue());
    }

위 코드 redis.getValue() 부분에서 오류가 발생했다.

        // 레디스 저장 확인 및 토큰 값 비교
        Member member = memberService.findByUsername("test").orElse(null);
        String value = redisUt.getValue(String.valueOf(member.getId()));

        assertThat(value).isEqualTo(refreshToken.getValue());

레디스에 키가 어떻게 저장되는지 확인한 결과, 아래와 같이 이스케이프 시퀀스로 값이 저장되는 걸 확인할 수 있었다.
이렇게 저장되니 getValue(Long id)로 찾으려 해도 찾을 수가 없었던 것!

문제의 원인은 크게 2가지 였다.

  1. 현재 프로젝트에서 레디스의 키 값으로 String타입의 email과 Long 타입의 id가 사용되기 때문에, id값을 넣을 때마다 형변환 해주는 것도 지저분해 보여서
    다양한 자료형의 키 값을 받아보고자 setValue(), getValue() 메서드의 파라미터로 제네릭 타입 T를 사용한 것

  2. Redis의 기본 Serializer가 JdkSerializationRedisSerializer이기 때문에 이스케이프 시퀀스 형태로 저장되는 것

  • RedisUt.java (기존)
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);
    }

기존의 RedisUt.java 값을 아래와 같이 바꿔주고,

  • RedisUt.java (변경)
public String getValue(String key) {
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
        return valueOperations.get(key);
    }

    public void setValue(String key, String value, long timeout) {
        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
        valueOperations.set(key, value, timeout, TimeUnit.MILLISECONDS);
    }

RedisConfig에서 Key serializer를 String으로 바꿔줌으로서 해결했다.

  • RedisRepositoryConfig
@Configuration
public class RedisRepositoryConfig {

    @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 RedisTemplate<String, String> redisTemplate() {
        RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory());
        redisTemplate.setEnableTransactionSupport(true);

        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new StringRedisSerializer());

        return redisTemplate;
    }
}

이제 정상적으로 값이 들어온다!

0개의 댓글