-
Notifications
You must be signed in to change notification settings - Fork 1
[배포] 토큰 만료 처리 #346
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
[배포] 토큰 만료 처리 #346
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the ✨ Finishing Touches🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
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.
Summary of Changes
Hello @wlgns12370, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
이 PR은 JWT 토큰 만료 및 유효성 검사 오류 처리를 개선합니다. 토큰 만료 시 ExpiredJwtException을 명시적으로 발생시키고, 이를 전역 예외 필터에서 상세한 JSON 응답으로 처리하도록 변경하여 클라이언트에게 더 명확한 오류 정보를 제공합니다. 또한, 다양한 토큰 관련 예외에 대한 응답 형식을 표준화합니다.
Highlights
- JWT 토큰 만료 처리 개선: JwtTokenProvider에서 토큰 만료 시 CustomRuntimeException 대신 ExpiredJwtException을 직접 발생시키도록 변경했습니다.
- 전역 예외 필터 강화: TokenExceptionFilter에서 ExpiredJwtException을 포함한 다양한 예외(CustomRuntimeException, 일반 Exception)를 캐치하여, HTTP 상태 코드와 함께 상세한 JSON 형식의 오류 응답을 클라이언트에게 반환하도록 구현했습니다.
- 토큰 필터 예외 전파: TokenFilter에서 ExpiredJwtException을 명시적으로 캐치하고 재발생시켜 TokenExceptionFilter에서 중앙 집중적으로 처리할 수 있도록 변경했습니다.
- 리프레시 토큰 오류 코드 변경: 리프레시 토큰 재발급 불가 시 발생하는 오류 코드를 EXPIRED_REFRESH_TOKEN에서 NOT_FOUND_TOKEN으로 변경했습니다.
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.
| Feature | Command | Description |
|---|---|---|
| Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
| Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
| Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments. |
| Help | /gemini help |
Displays a list of available commands. |
Customization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.
Limitations & Feedback
Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
Footnotes
-
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩
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.
Code Review
이 PR은 토큰 만료 처리를 개선하고 관련 예외 핸들링을 중앙 필터로 옮기는 좋은 변경을 포함하고 있습니다. JwtTokenProvider에서 ExpiredJwtException을 직접 던지도록 변경하고, TokenFilter에서 이를 적절히 처리하여 TokenExceptionFilter로 전달하는 구조는 명확하고 확장성이 좋습니다. TokenExceptionFilter에서 몇 가지 개선점을 발견하여 리뷰 코멘트를 남겼습니다. 중복 코드를 줄이고, HTTP 상태 코드를 표준에 맞게 사용하도록 수정하면 더 완성도 높은 코드가 될 것 같습니다.
| .code(ex.getCode()) | ||
| .build(); | ||
|
|
||
| response.setStatus(HttpStatus.UNAUTHORIZED.value()); |
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.
CustomRuntimeException을 처리할 때 응답 본문의 status 필드에는 ex.getStatus().value()를 사용하면서, 실제 HTTP 응답 상태 코드는 HttpStatus.UNAUTHORIZED.value()로 고정하고 있습니다. 이 두 값은 일치해야 합니다. response.setStatus() 호출 시에도 ex.getStatus().value()를 사용하여 예외에 정의된 정확한 상태 코드가 반환되도록 수정해야 합니다.
| response.setStatus(HttpStatus.UNAUTHORIZED.value()); | |
| response.setStatus(ex.getStatus().value()); |
| ErrorResponse errorResponse = ErrorResponse.builder() | ||
| .status(HttpStatus.NOT_ACCEPTABLE.value()) | ||
| .message("토큰이 만료되었습니다.") | ||
| .code("TOKEN_EXPIRED") | ||
| .build(); | ||
|
|
||
| response.setStatus(HttpStatus.NOT_ACCEPTABLE.value()); | ||
| response.setContentType(MediaType.APPLICATION_JSON_VALUE); | ||
| response.setCharacterEncoding("UTF-8"); | ||
| response.sendError(exception.getStatus().value(), exception.getMessage()); | ||
|
|
||
| objectMapper.writeValue(response.getWriter(), errorResponse); |
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.
만료된 토큰에 대해 HttpStatus.NOT_ACCEPTABLE (406)을 사용하고 있는데, 이는 일반적으로 Accept 헤더와 서버가 생성할 수 있는 컨텐츠 타입이 맞지 않을 때 사용됩니다. 토큰이 만료된 경우에는 인증이 필요하다는 의미이므로 HttpStatus.UNAUTHORIZED (401)을 사용하는 것이 더 표준에 부합하고 일반적인 REST API 설계 관례에 맞습니다. 클라이언트는 401 응답을 받으면 토큰을 재발급 받거나 다시 로그인하도록 처리할 수 있습니다.
| ErrorResponse errorResponse = ErrorResponse.builder() | |
| .status(HttpStatus.NOT_ACCEPTABLE.value()) | |
| .message("토큰이 만료되었습니다.") | |
| .code("TOKEN_EXPIRED") | |
| .build(); | |
| response.setStatus(HttpStatus.NOT_ACCEPTABLE.value()); | |
| response.setContentType(MediaType.APPLICATION_JSON_VALUE); | |
| response.setCharacterEncoding("UTF-8"); | |
| response.sendError(exception.getStatus().value(), exception.getMessage()); | |
| objectMapper.writeValue(response.getWriter(), errorResponse); | |
| ErrorResponse errorResponse = ErrorResponse.builder() | |
| .status(HttpStatus.UNAUTHORIZED.value()) | |
| .message("토큰이 만료되었습니다.") | |
| .code("TOKEN_EXPIRED") | |
| .build(); | |
| response.setStatus(HttpStatus.UNAUTHORIZED.value()); | |
| response.setContentType(MediaType.APPLICATION_JSON_VALUE); | |
| response.setCharacterEncoding("UTF-8"); | |
| objectMapper.writeValue(response.getWriter(), errorResponse); |
| } catch (CustomRuntimeException ex) { | ||
|
|
||
| ErrorResponse errorResponse = ErrorResponse.builder() | ||
| .status(ex.getStatus().value()) | ||
| .message(ex.getMessage()) | ||
| .code(ex.getCode()) | ||
| .build(); | ||
|
|
||
| response.setStatus(HttpStatus.UNAUTHORIZED.value()); | ||
| response.setContentType(MediaType.APPLICATION_JSON_VALUE); | ||
| response.setCharacterEncoding("UTF-8"); | ||
|
|
||
| objectMapper.writeValue(response.getWriter(), errorResponse); | ||
| } catch (ExpiredJwtException ex) { | ||
| ErrorResponse errorResponse = ErrorResponse.builder() | ||
| .status(HttpStatus.NOT_ACCEPTABLE.value()) | ||
| .message("토큰이 만료되었습니다.") | ||
| .code("TOKEN_EXPIRED") | ||
| .build(); | ||
|
|
||
| response.setStatus(HttpStatus.NOT_ACCEPTABLE.value()); | ||
| response.setContentType(MediaType.APPLICATION_JSON_VALUE); | ||
| response.setCharacterEncoding("UTF-8"); | ||
| response.sendError(exception.getStatus().value(), exception.getMessage()); | ||
|
|
||
| objectMapper.writeValue(response.getWriter(), errorResponse); | ||
| } catch (Exception ex) { | ||
| ErrorResponse errorResponse = ErrorResponse.builder() | ||
| .status(HttpStatus.INTERNAL_SERVER_ERROR.value()) | ||
| .message("서버 오류가 발생했습니다.") | ||
| .code("INTERNAL_SERVER_ERROR") | ||
| .build(); | ||
|
|
||
| response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); | ||
| response.setContentType(MediaType.APPLICATION_JSON_VALUE); | ||
| response.setCharacterEncoding("UTF-8"); | ||
|
|
||
| objectMapper.writeValue(response.getWriter(), errorResponse); | ||
| } |
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.
각 catch 블록에서 HttpServletResponse에 에러 응답을 설정하는 코드가 중복되고 있습니다. 이 로직을 별도의 private 메서드로 추출하여 코드 중복을 줄이고 가독성과 유지보수성을 높이는 것을 제안합니다. 예를 들어, 다음과 같은 헬퍼 메서드를 만들 수 있습니다:
private void sendErrorResponse(HttpServletResponse response, ErrorResponse errorResponse) throws IOException {
response.setStatus(errorResponse.getStatus());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding("UTF-8");
objectMapper.writeValue(response.getWriter(), errorResponse);
}이렇게 하면 각 catch 블록이 더 간결해집니다.
No description provided.