Redis로 중복요청(따닥) 막기

조성현·2023년 8월 17일
0

0. 중복요청(Duplicate Request)은 왜 막아야 할까

0-1. 일상에서 만나게 된 중복요청

얼마전 밥을 먹고나서 결제하던 중에 겪게 된 일이다.

  • 명확하게 중복요청이다! 라고 단정지을 순 없지만, 개인적으로 금액이 같고, 6초 차이로 연달아 온 요청 정도는 막아줄만 하지 않았나? 생각도 들었다.
  • 이중 결제를 보자마자 이거 혹시 중복요청 key값에 분 단위를 넣나?라는 생각부터 했다는게 킬링포인트였다. (이정도면 뇌가 개발에 절여져가고 있나..)

0-2. GPT에게 중복요청을 막아야하는 이유를 물어보았다.

  • 개인적으로 ChatGPT가 거짓말 하는 것을 너무 많이봐서, 무조건적 신뢰를 하지 않고 항상 의심하며 사용하고 있다.
  • 어느정도 배경지식이나 판단할 능력을 갖춘 상태에서 의심하는 자세로 사용하는 ChatGPT는 매우 좋은 학습수단이라고 생각한다.
    (구글링 키워드 뽑기, 대략적으로 감 잡기 등)

1. 계획.

1. 어떻게 막을 것인가.

  • API 요청이 들어왔을 때, 해당 요청을 특정할 수 있는 key를 저장하도록 하고,
    저장하기 이전에 해당 key가 이미 저장되어있는지를 확인하도록 한다.
  • key에 유효시간을 두어, 진짜로 한번 더 구매하는 경우를 막지 않도록 설계한다.

2. key 저장소는 역시 레디스!

  • 빠르고, 유효시간을 설정하여 알아서 삭제되도록 할 수 있다.

3. key는 어떻게 구성할 것인가.

  • methodName + userId를 기본으로 두고, 경우에 따라 조금 더 추가할 수 있도록 한다.

2. [구현]Redis를 활용하여 중복요청 막기

2-1. 로직 설명

사실 로직 자체는 간단하다.

  • 이전 글에서 설명했던 분산락 로직에 중복요청 방지를 한스푼 끼얹은 느낌.
  • line 25에서 중복요청 방지메서드가 호출된다.
  • 인자로 넘겨주는 dupulicateCheckKey
    methodName + userId + ProductIds로 이루어져 있다.

    line 38에서 redisDAO.setIfAbsent()를 호출하는데 이 메서드는

    1. 이미 해당 키값이 존재한다면, set하지 않고 False를 리턴한다.
    2. 해당 키값이 존재하지 않는다면, set하고 True를 리턴한다.
      • 유효시간 10초로 설정되어 있는데, 이 부분은 정책적인 부분이므로 내부적으로 정한 값을 사용하면 된다.
  • 만약 중복된 요청이라면 line 39에서 DuplicateRequestException을 던져주게 된다.

2-2. 분산락 획득 이전에 중복요청 체크를 하는 이유

  • 분산락 경쟁자를 하나라도 줄이려는 소박한 목적을 위해...

3. 사실 중요한 것은 '어디서 막을 것인가'이다.

3-1. 모든 요청에 대해서 중복 요청을 막는다.

  • 필터나 인터셉터에서 막아주면 되지 않을까 싶다.
  • 현재 프로젝트 기준으로는 인증객체 생성 이후에 필터를 끼워넣고,
    UserDetails에서 userId / HttpServletRequest에서 URI와 HTTP 메서드를 가져와 dupulicateCheckKey로 사용하면 될 것 같다.
  • [필터, 인터셉터, AOP에 대해 정리한 글 ]

3-2. 일부 요청에 대해서 중복 요청을 막는다.

  • 간단한 조회와 같이 리소스소모도 크지 않고, 멱등성이 보장되는 API라면 내부적인 정책에 따라 안막을 수도 있다.
  • AOP를 활용하거나, 컨트롤러에서 로직으로
    원하는 API들만 중복 요청을 막아주면 된다.

4. 새롭게 알게된 사실들

  • Redis의 MULTI, EXEC 명령어를 통해 구현한 Redis Transaction 내부에서 조회한 값으로 어떤 작업을 하려 한다면, 그 자리에는 null이 들어가게 된다.
  • 트랜잭션 커밋 이전에 조회 -> 조회한 값 사용이 안된다는 얘기.
profile
맛있는 음식과 여행을 좋아하는 당당한 뚱땡이

0개의 댓글