-
Notifications
You must be signed in to change notification settings - Fork 2
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
[BE] feat: 중복 요청 방지 기능 구현 #781
Conversation
backend/src/main/java/reviewme/global/DuplicateRequestInterceptor.java
Outdated
Show resolved
Hide resolved
backend/src/main/java/reviewme/global/DuplicateRequestInterceptor.java
Outdated
Show resolved
Hide resolved
@@ -50,6 +51,11 @@ public ProblemDetail handleDataConsistencyException(DataInconsistencyException e | |||
return ProblemDetail.forStatusAndDetail(HttpStatus.INTERNAL_SERVER_ERROR, ex.getErrorMessage()); | |||
} | |||
|
|||
@ExceptionHandler(TooManyDuplicateRequestException.class) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(네이밍) Duplicate
는 없어도 될 듯합니다! TooManyRequestException
으로도 충분하다고 생각해요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TooManyRequestException
은 '요청 자체가 너무 많아서' 서버가 처리할 수 없다는 뉘앙스인 것 같아요,
이 예외가 발생하는 상황은 그런게 아니니까 구분하기 위해서라도 Duplicate를 붙이고 싶어요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
다른 단어를 선택하는 건 어떨까요? duplicate
는 정말 완벽히 같은 복제본의 느낌인데, 본문이 다르더라도 많은 요청이 막힐 수 있는 부분이라서요. 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Many
와 Duplicate
의 의미가 중복돼 클래스 이름이 어색해서 남긴 리뷰였습니다~
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
backend/src/main/java/reviewme/global/exception/TooManyDuplicateRequestException.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
산따최!!
어려워보이는데 구현해냈고나🚀🚀
backend/src/main/java/reviewme/global/exception/TooManyDuplicateRequestException.java
Outdated
Show resolved
Hide resolved
backend/src/main/java/reviewme/global/DuplicateRequestInterceptor.java
Outdated
Show resolved
Hide resolved
private final RedisTemplate<String, Object> redisTemplate; | ||
|
||
@Override | ||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
value로는 무조건 숫자가 오는 것 같은데 int 나 long이 아닌 object를 사용한 이유가 궁금해요~
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@skylar1220 object는 override 메서드라 못바꾸지 않나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
앗 설명이 부족했네요
RedisTemplate<String, Object> redisTemplate;
부분을 말합니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
레디스의 '사용 확장성'을 생각했어요!
레디스에 반드시 Integer 이나 Long 과 같은 값을 담을게 아니라,
"세션의 정보"와 같은 것들도 담을 수 있게 될 것 같아요.
(무중단 배포로 sticky session 을 사용하지 않게 되면 곧 우리가 해야 할 일이기도 하고요😅)
그런데 지금 숫자로 value 를 한정하면, 나중에 다른 형태의 값을 저장할 때 기존 기능에 영향을 주게 돼요.
그래서 Object 를 저장하게 해준 것입니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
추가))
저는 어플리케이션에 하나의 RedisTemplate 을 추가할 수 있을거라 생각했는데, 알고보니 아니었네요🫨 세션같은 정보를 저장할때는 그에 맞는 형식의 RedisTemplate 을 추가하도록 하고, 중복 요청정보를 저장할 때는 RedisTemplate<String, Long>
로 저장하도록 하겠습니다!
그리고 여러개의 RedisTemplate을 사용하더라도 저장을 할 때는 하나의 레디스에 key 와 value 모두 String 으로 직렬화해서 사용해서 상관 없다고 합니다😊 커비가 짚어준 덕분에 더 효율적으로 코드를 작성하게 되었어요!!!! 커비 최고최고😍
backend/src/test/java/reviewme/global/DuplicateRequestInterceptorTest.java
Outdated
Show resolved
Hide resolved
backend/src/main/java/reviewme/global/DuplicateRequestInterceptor.java
Outdated
Show resolved
Hide resolved
backend/src/main/java/reviewme/global/DuplicateRequestInterceptor.java
Outdated
Show resolved
Hide resolved
backend/src/main/java/reviewme/global/DuplicateRequestInterceptor.java
Outdated
Show resolved
Hide resolved
backend/src/main/java/reviewme/global/DuplicateRequestInterceptor.java
Outdated
Show resolved
Hide resolved
backend/src/main/java/reviewme/global/DuplicateRequestInterceptor.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
산초 커밋과 테스트를 읽으니 깔끔하게 이해가 잘 되네요👍👍
고생했습니다 코멘트만 확인 부탁해요~
private final RedisTemplate<String, Object> redisTemplate; | ||
|
||
@Override | ||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@skylar1220 object는 override 메서드라 못바꾸지 않나요?
backend/src/main/java/reviewme/global/exception/TooManyDuplicateRequestException.java
Outdated
Show resolved
Hide resolved
backend/src/main/java/reviewme/global/DuplicateRequestInterceptor.java
Outdated
Show resolved
Hide resolved
backend/src/main/java/reviewme/global/DuplicateRequestInterceptor.java
Outdated
Show resolved
Hide resolved
backend/src/main/java/reviewme/global/DuplicateRequestInterceptor.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
신초 고생했읍니다. 빨리 적용하고 중복요청 보내보고 싶어요 👍👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
따닥 미워
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
어렵다.. 산초 최고🚀🚀🚀🚀
- URI, IP, UserAgent 를 기준으로 1초에 3개보다 많은 요청이 오는 경우, 429를 응답한다. 즉, 4번째 요청부터 거부된다.
- RedisTemplate<String, Object> -> RedisTemplate<String, Long>
- info -> warn
- if null 분기 제거
- 명시적으로 null 인 경우 1로 초기화했던 이전 코드와 달리, setIfAbsent를 통해서 값이 초기화하는 지금은 '1로 초기화되었는지' 검증하기가 어렵다.
- increment 가 증가한 결과를 바로 반환한다. 이를 사용하도록 수정했다.
- RedisTemplate과 ValueOperations를 모킹한다. - 구체적인 인자를 지정하지 않은 stub 문을 수정한다.
- 테드 의견 반영
0b48f39
to
cf7e628
Compare
* build: 의존성 추가 * chore: 개발 환경 redis 설정 * feat: 레디스 설정을 빈으로 등록 * feat: 중복 요청 처리 인터셉터 생성 - URI, IP, UserAgent 를 기준으로 1초에 3개보다 많은 요청이 오는 경우, 429를 응답한다. 즉, 4번째 요청부터 거부된다. * feat: 중복 요청 처리 인터셉터 등록 * feat: 예외 핸들러 추가 * test: 중복 예외 처리 인터셉터 테스트 작성 * refactor: value를 업데이트할 때 만료 시간을 다시 설정하도록 수정 * style: 가독성을 위한 개행 수정 * style: 파일 끝 개행 * refactor: ConfigurationProperties 적용 * refactor: 변수명 변경 * refactor: 기존 상수 적용 * refactor: RedisTemplate 타입과 중복 요청 감지 로직 변경 - RedisTemplate<String, Object> -> RedisTemplate<String, Long> * refactor: 로그 메세지 수정 * chore: 사용하지 않는 예외 삭제 * refactor: 로그 레벨 수정 - info -> warn * refactor: 중복 요청 검증 로직 개션 - if null 분기 제거 * test: 깨진 테스트 봉합 * test: 의미 없어진 테스트 삭제 - 명시적으로 null 인 경우 1로 초기화했던 이전 코드와 달리, setIfAbsent를 통해서 값이 초기화하는 지금은 '1로 초기화되었는지' 검증하기가 어렵다. * refactor: 불필요한 지역변수 할당 삭제 - increment 가 증가한 결과를 바로 반환한다. 이를 사용하도록 수정했다. * test: redisTemplate 등록으로 깨지는 테스트 봉합 - RedisTemplate과 ValueOperations를 모킹한다. - 구체적인 인자를 지정하지 않은 stub 문을 수정한다. * refactor: 예외 이름 변경 * refactor: 요청 제한 설정을 properties로 이동 * refactor: 예외 메세지 수정 - 테드 의견 반영 * chore: 안쓰는 import 문 제거 * test: 테스트 데이터 경계값으로 변경 * refactor: 제한 만료 시간 설정 로직 변경 * refactor: frequency -> requestCount 완전 대체
🚀 어떤 기능을 구현했나요 ?
🔥 어떻게 해결했나요 ?
⬆️ 위 사진은 Jmeter (반복 요청을 보낼 수 있는 도구) 를 사용해서 1초동안 여러번의 요청을 보내본 결과입니다.
📝 어떤 부분에 집중해서 리뷰해야 할까요?
🔮 미래 계획
1) dev-ec2 에 redis 환경 만들어주기
필요한 것은 "작동하는 redis" 와 "java application 과 redis 의 연결 설정" 인데요,
전자는 docker 를 사용해서 이미 갖춰두었고,
이 PR 머지하기 전에 application-dev.yml 에 아래 코드 추가할 예정입니다.
2) prod-ec2-a / prod-ec2-b 에 redis 환경 만들어주기
redis 만을 위한 ec2 를 만들어준 다음, (역시나 도커로 편하게 redis 다운 &. 실행시킬 생각입니다)
application-prod.yml 에 아래 코드를 추가해줍니다.
보안그룹은 이미 만들어져있는 project-cache 를 새로 만들 ec2 에 달아주면 될 것 같습니다
로컬에서 실행하는 방법
Redis 를 다운받고, 실행한다. (Redis 다운받는 법은 인터넷에 많음)
아래 명령어를 입력했을 때, 버전이 나오면 다운로드 된 것이다.
이제 실행한다.
redis 를 실행하지 않은 상태에서 local Application을 실행하면 오류가 뜨며 실행되지 않는다🙀