g-market / b-shop-backend

0 stars 0 forks source link

feat: Redis 장바구니 구현 및 리프레시 토큰 리팩토링 #27

Closed halucinor closed 1 year ago

halucinor commented 1 year ago

In GitLab by @haaeee on Feb 16, 2023, 10:43


name: Feature Template assignees: 'jaime'

🤷 구현할 기능

Redis 트랜잭션과 함께 비즈니스 로직을 구현

🔨 상세 작업 내용

📄 참고 사항

Bug

소스 코드

public class RefreshTokenRepositoryImpl implements RefreshTokenRepository {

    private static final String REFRESH_TOKEN_PREFIX = "refreshToken-";

    private final RedisTemplate<String, String> redisTemplate;
    private final StringRedisTemplate stringRedisTemplate;
    private final RedisValueSupport redisValueSupport;

    @Value("${token.refresh-expired-time}")
    private long expiredTimeMillis;

    @Override
    @Transactional
    public RefreshToken save(final RefreshToken refreshToken) {
        final String key = getKey(refreshToken.refreshToken());
        if (hasSameRefreshToken(key)) {
            throw new UnAuthorizedRefreshTokenException(REFRESH_TOKEN_DUPLICATED_SAVED_EXCEPTION);
        }
        final String value = redisValueSupport.writeValueAsString(refreshToken);
        stringRedisTemplate.opsForValue().set(key, value);
        stringRedisTemplate.expire(key, expiredTimeMillis, TimeUnit.MILLISECONDS);
        return refreshToken;
    }

    private boolean hasSameRefreshToken(final String key) {
        // key가 없을 떼 있을 때 둘다 null로
        return stringRedisTemplate.hasKey(key) != null;
    }

        private String getKey(final String refreshToken) {
        return REFRESH_TOKEN_PREFIX + refreshToken;
    }
        private String getValue(final String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }

테스트 코드

@Test
@DisplayName("같은 리프레시 토큰 Key가 Redis에 존재하면 예외를 던진다.")
void given_savedRefreshToken_when_save_savedRefreshToken_then_throw_UnAuthorizedRefreshTokenException() {
    // given
    final RefreshToken refreshToken = RefreshToken.builder()
            .refreshToken(tokenValue)
            .expiredAt(LocalDateTime.now())
            .memberId(memberId)
            .build();
    final RefreshToken savedRefreshToken = refreshTokenRepository.save(refreshToken);

    // when & then
    final RefreshToken otherKey = refreshTokenRepository.save(savedRefreshToken);
    assertThatThrownBy(() -> refreshTokenRepository.save(savedRefreshToken))
        .isInstanceOf(UnAuthorizedRefreshTokenException.class);
}
  1. 처음 저장할 때는 key로 저장된 것이 없기에 Boolean.False 혹은 null이 올 것이라고 예상했으며 null로 리턴되었습니다.
  1. 반면 2번째로 똑같은 값을 저장할 때 redis에서는 조회가 되지만, spring 에서는 key가 존재하더라도 null로 리턴되었습니다.

해결 방법

이에 따라 getValue를 통해 value가 존재하면 그 때 EXCEPTION으로 처리하여 중복을 처리하였습니다.

@Override
    @Transactional
    public RefreshToken save(final RefreshToken refreshToken) {
        final String key = getKey(refreshToken.refreshToken());
        final String foundValue = getValue(key);
        if (foundValue != null) {
            throw new UnAuthorizedRefreshTokenException(REFRESH_TOKEN_DUPLICATED_SAVED_EXCEPTION);
        }
        final String value = redisValueSupport.writeValueAsString(refreshToken);
        stringRedisTemplate.opsForValue().set(key, value);
        stringRedisTemplate.expire(key, expiredTimeMillis, TimeUnit.MILLISECONDS);
        return refreshToken;
    }

private String getValue(final String key) {
        return redisTemplate.opsForValue().get(key);
    }

⏰ 예상 소요 기간

2023/02/16 ~ 2023/02/16

halucinor commented 1 year ago

In GitLab by @haaeee on Feb 16, 2023, 10:52

added 1 design

halucinor commented 1 year ago

In GitLab by @haaeee on Feb 19, 2023, 16:20

marked the checklist item 장바구니 Redis 리팩토링 as completed

halucinor commented 1 year ago

In GitLab by @haaeee on Feb 19, 2023, 16:20

marked the checklist item refreshToken Redis 리팩토링 as completed

halucinor commented 1 year ago

In GitLab by @haaeee on Feb 23, 2023, 07:29

Animation