Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] 멱등성키 적용을 통한 중복결제 방지 #155

Merged
merged 9 commits into from
Aug 29, 2024

Conversation

Hyeon-Uk
Copy link
Contributor

@Hyeon-Uk Hyeon-Uk commented Aug 29, 2024

💡 다음 이슈를 해결했어요.

Issue Link - #118


💡 이슈를 처리하면서 추가된 코드가 있어요.

Idempotent.java

멱등성 키를 확인 후 응답할 수 있는 어노테이션

package camp.woowak.lab.infra.aop.idempotent;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {
boolean throwError() default false;
Class<? extends RuntimeException> throwable() default RuntimeException.class;
String exceptionMessage() default "";
}

IdempotentAop.java

@idempotent 가 추가된 메서드를 실행하기 전, 키를 확인 후 응답할 수 있는 aop

package camp.woowak.lab.infra.aop.idempotent;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.time.Duration;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import camp.woowak.lab.infra.aop.idempotent.exception.IdempotencyKeyNotExistsException;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Aspect
@Component
@RequiredArgsConstructor
@Slf4j
public class IdempotentAop {
private static final String REDIS_IDEMPOTENT_KEY = "IDEMPOTENT_KEY: ";
private final RedisTemplate<String, Object> redisTemplate;
@Around("@annotation(camp.woowak.lab.infra.aop.idempotent.Idempotent)")
public Object idempotentOperation(ProceedingJoinPoint joinPoint) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();
String idempotencyKey = request.getHeader("Idempotency-Key");
MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
Method method = methodSignature.getMethod();
Idempotent idempotent = method.getAnnotation(Idempotent.class);
if (idempotencyKey == null || idempotencyKey.isEmpty()) {
throw new IdempotencyKeyNotExistsException("Idempotency-Key is required");
}
if (Boolean.TRUE.equals(redisTemplate.hasKey(REDIS_IDEMPOTENT_KEY + idempotencyKey))) {
if (idempotent.throwError()) {
Class<? extends RuntimeException> throwable = idempotent.throwable();
String exceptionMessage = idempotent.exceptionMessage();
Constructor<? extends RuntimeException> constructor = throwable.getConstructor(String.class);
RuntimeException exceptions = constructor.newInstance(exceptionMessage);
throw exceptions;
}
return redisTemplate.opsForValue().get(REDIS_IDEMPOTENT_KEY + idempotencyKey);
}
Object proceed = joinPoint.proceed();
redisTemplate.opsForValue()
.setIfAbsent(REDIS_IDEMPOTENT_KEY + idempotencyKey, proceed, Duration.ofSeconds(60L));
return proceed;
}
}


💡 이런 고민을 했어요.

  • 네트워크가 지연되어 중복 요청이 성공 트랜잭션이 커밋되는 시점보다 느리게 도달하게 된다면, 중복 요청으로 인해 서버의 스레드 및 커넥션 풀 등 리소스를 낭비할 수 있다
  • 따라서 멱등성키를 이용해서 이미 처리된 응답이라면, 기존의 응답 혹은 중복 exception을 던지도록 구현했습니다.
  • 중복 요청은 실제 서비스 로직까지 들어가지 않아 스레드를 최대한 빠르게 반납할 수 있으며, 커넥션 풀의 점유를 방지할 수 있습니다.

✅ 셀프 체크리스트

  • 내 코드를 스스로 검토했습니다.
  • 필요한 테스트를 추가했습니다.
  • 모든 테스트를 통과합니다.
  • 브랜치 전략에 맞는 브랜치에 PR을 올리고 있습니다.
  • 커밋 메세지를 컨벤션에 맞추었습니다.
  • wiki를 수정했습니다.

@Hyeon-Uk Hyeon-Uk added ✨ Feature 기능 개발 💪 Improve 기능 고도화 & 개선 labels Aug 29, 2024
@Hyeon-Uk Hyeon-Uk added this to the 1차 고도화 milestone Aug 29, 2024
@Hyeon-Uk Hyeon-Uk self-assigned this Aug 29, 2024
Copy link
Member

@june-777 june-777 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다 👍

Copy link
Member

@kimhyun5u kimhyun5u left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수고하셨습니다!

@Hyeon-Uk Hyeon-Uk merged commit 4966cda into main Aug 29, 2024
1 check passed
@kimhyun5u kimhyun5u deleted the feature/118_Dr-KoKo_Hyeon-Uk_멱등성키 branch September 19, 2024 09:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
✨ Feature 기능 개발 💪 Improve 기능 고도화 & 개선
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

4 participants