-
Notifications
You must be signed in to change notification settings - Fork 0
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
[#34] ExceptionHandler 추가하고 Custom Exception으로 변환 #35
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.dnd.dndtravel.auth.exception; | ||
|
||
public class AppleTokenDecodingException extends RuntimeException { | ||
private static final String MESSAGE = "Apple 토큰 payload 해독실패"; | ||
|
||
public AppleTokenDecodingException(Exception e) { | ||
super(MESSAGE, e); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.dnd.dndtravel.auth.exception; | ||
|
||
import io.jsonwebtoken.JwtException; | ||
|
||
public class JwtTokenDecodingException extends RuntimeException { | ||
private static final String MESSAGE = "Jwt 토큰 해독 실패"; | ||
|
||
public JwtTokenDecodingException(JwtException e) { | ||
super(MESSAGE, e); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.dnd.dndtravel.auth.exception; | ||
|
||
import io.jsonwebtoken.ExpiredJwtException; | ||
|
||
public class JwtTokenExpiredException extends RuntimeException { | ||
private static final String MESSAGE = "Jwt 토큰이 만료되었음"; | ||
|
||
public JwtTokenExpiredException(ExpiredJwtException e) { | ||
super(MESSAGE, e); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.dnd.dndtravel.auth.exception; | ||
|
||
public class RefreshTokenInvalidException extends RuntimeException { | ||
private static final String MESSAGE = "유효하지 않은 RefreshToken 토큰 [refreshToken=%s]"; | ||
|
||
public RefreshTokenInvalidException(String refreshToken) { | ||
super(String.format(MESSAGE, refreshToken)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
package com.dnd.dndtravel.common; | ||
|
||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.MethodArgumentNotValidException; | ||
import org.springframework.web.bind.annotation.ExceptionHandler; | ||
import org.springframework.web.bind.annotation.RestControllerAdvice; | ||
|
||
import com.dnd.dndtravel.auth.exception.AppleTokenDecodingException; | ||
import com.dnd.dndtravel.auth.exception.JwtTokenDecodingException; | ||
import com.dnd.dndtravel.auth.exception.JwtTokenExpiredException; | ||
import com.dnd.dndtravel.auth.exception.RefreshTokenInvalidException; | ||
import com.dnd.dndtravel.map.exception.MemberAttractionNotFoundException; | ||
import com.dnd.dndtravel.map.exception.MemberNotFoundException; | ||
import com.dnd.dndtravel.map.exception.PhotoDeleteFailException; | ||
import com.dnd.dndtravel.map.exception.PhotoEmptyException; | ||
import com.dnd.dndtravel.map.exception.PhotoInvalidException; | ||
import com.dnd.dndtravel.map.exception.PhotoUploadFailException; | ||
import com.dnd.dndtravel.map.exception.RegionNotFoundException; | ||
//todo 예외클래스가 많아지면 해당클래스가 길어질것으로 예상, 개선필요해보이고 보안 때문에 상태코드별로 애매하게 동일한 메시지를 전달해주고, 스웨거 문서로 상세 오류를 전달해주는데 이 구조가 적절한건지 고민해봐야한다. | ||
@RestControllerAdvice | ||
public class CommonExceptionHandler { | ||
|
||
private static final String INTERNAL_SERVER_ERROR_MESSAGE = "Internal Server Error"; | ||
private static final String BAD_REQUEST_MESSAGE = "잘못된 요청입니다"; | ||
|
||
@ExceptionHandler(MethodArgumentNotValidException.class) | ||
public ResponseEntity<String> runtimeException() { | ||
return ResponseEntity | ||
.status(HttpStatus.BAD_REQUEST) | ||
.body(BAD_REQUEST_MESSAGE); | ||
} | ||
|
||
@ExceptionHandler(MemberNotFoundException.class) | ||
public ResponseEntity<String> runtimeException(MemberNotFoundException e) { | ||
return ResponseEntity | ||
.status(HttpStatus.BAD_REQUEST) | ||
.body(BAD_REQUEST_MESSAGE); | ||
} | ||
|
||
@ExceptionHandler(MemberAttractionNotFoundException.class) | ||
public ResponseEntity<String> runtimeException(MemberAttractionNotFoundException e) { | ||
return ResponseEntity | ||
.status(HttpStatus.BAD_REQUEST) | ||
.body(BAD_REQUEST_MESSAGE); | ||
} | ||
|
||
@ExceptionHandler(RegionNotFoundException.class) | ||
public ResponseEntity<String> runtimeException(RegionNotFoundException e) { | ||
return ResponseEntity | ||
.status(HttpStatus.BAD_REQUEST) | ||
.body(BAD_REQUEST_MESSAGE); | ||
} | ||
|
||
@ExceptionHandler(AppleTokenDecodingException.class) | ||
public ResponseEntity<String> runtimeException(AppleTokenDecodingException e) { | ||
return ResponseEntity | ||
.status(HttpStatus.UNAUTHORIZED) | ||
.body("토큰 인증에 실패했습니다"); | ||
} | ||
|
||
@ExceptionHandler(JwtTokenExpiredException.class) | ||
public ResponseEntity<String> runtimeException(JwtTokenExpiredException e) { | ||
return ResponseEntity | ||
.status(HttpStatus.UNAUTHORIZED) | ||
.body("토큰 인증에 실패했습니다"); | ||
} | ||
|
||
@ExceptionHandler(JwtTokenDecodingException.class) | ||
public ResponseEntity<String> runtimeException(JwtTokenDecodingException e) { | ||
return ResponseEntity | ||
.status(HttpStatus.UNAUTHORIZED) | ||
.body("토큰 인증에 실패했습니다"); | ||
} | ||
Comment on lines
+55
to
+74
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 음 .. 이 부분은 중복이 크게 띄니까 handleTokenExceptions 같은 이름으로 합치는 것이 어떨까요 ? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. @ExceptionHandler(handleTokenExceptions.class)
public ResponseEntity<String> runtimeException(handleTokenExceptions e) {
return ResponseEntity
.status(HttpStatus.UNAUTHORIZED)
.body("토큰 인증에 실패했습니다");
} 이런 느낌으로용 ! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. handleTokenExceptions 추상클래스 를 만들어서 위 3개의 예외를 상속하는 방식 말씀하시는건가요?? 위 3개의 예외의 응답이 완전 똑같다는 확신이 생기면 어떻게든 줄일수 있을것같은데 클라랑 조금더 얘기해보고 변경해봐도 괜찮을것 같아요 |
||
|
||
@ExceptionHandler(RefreshTokenInvalidException.class) | ||
public ResponseEntity<String> runtimeException(RefreshTokenInvalidException e) { | ||
return ResponseEntity | ||
.status(HttpStatus.BAD_REQUEST) | ||
.body(BAD_REQUEST_MESSAGE); | ||
} | ||
|
||
@ExceptionHandler(PhotoEmptyException.class) | ||
public ResponseEntity<String> runtimeException(PhotoEmptyException e) { | ||
return ResponseEntity | ||
.status(HttpStatus.BAD_REQUEST) | ||
.body(BAD_REQUEST_MESSAGE); | ||
} | ||
|
||
@ExceptionHandler(PhotoDeleteFailException.class) | ||
public ResponseEntity<String> runtimeException(PhotoDeleteFailException e) { | ||
return ResponseEntity | ||
.status(HttpStatus.INTERNAL_SERVER_ERROR) | ||
.body(INTERNAL_SERVER_ERROR_MESSAGE); | ||
} | ||
|
||
@ExceptionHandler(PhotoUploadFailException.class) | ||
public ResponseEntity<String> runtimeException(PhotoUploadFailException e) { | ||
return ResponseEntity | ||
.status(HttpStatus.INTERNAL_SERVER_ERROR) | ||
.body(INTERNAL_SERVER_ERROR_MESSAGE); | ||
} | ||
|
||
@ExceptionHandler(PhotoInvalidException.class) | ||
public ResponseEntity<String> runtimeException(PhotoInvalidException e) { | ||
return ResponseEntity | ||
.status(HttpStatus.BAD_REQUEST) | ||
.body(BAD_REQUEST_MESSAGE); | ||
} | ||
|
||
|
||
@ExceptionHandler(RuntimeException.class) | ||
public ResponseEntity<String> runtimeException(RuntimeException e) { | ||
return ResponseEntity | ||
.status(HttpStatus.INTERNAL_SERVER_ERROR) | ||
.body(INTERNAL_SERVER_ERROR_MESSAGE); | ||
} | ||
|
||
@ExceptionHandler(Exception.class) | ||
public ResponseEntity<String> runtimeException(Exception e) { | ||
return ResponseEntity | ||
.status(HttpStatus.INTERNAL_SERVER_ERROR) | ||
.body(INTERNAL_SERVER_ERROR_MESSAGE); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.dnd.dndtravel.map.exception; | ||
|
||
public class MemberAttractionNotFoundException extends RuntimeException { | ||
private static final String MESSAGE = "존재하지 않는 방문기록 [memberAttractionId=%s]"; | ||
|
||
public MemberAttractionNotFoundException(long memberAttractionId) { | ||
super(String.format(MESSAGE, memberAttractionId)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.dnd.dndtravel.map.exception; | ||
|
||
public class MemberNotFoundException extends RuntimeException { | ||
private static final String MESSAGE = "존재하지 않는 유저 [memberId=%s]"; | ||
|
||
public MemberNotFoundException(long memberId) { | ||
super(String.format(MESSAGE, memberId)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.dnd.dndtravel.map.exception; | ||
|
||
import com.amazonaws.SdkClientException; | ||
|
||
public class PhotoDeleteFailException extends RuntimeException{ | ||
private static final String MESSAGE = "s3 이미지 삭제 실패 [imagePath=%s]"; | ||
|
||
public PhotoDeleteFailException(String existingFileName, SdkClientException e) { | ||
super(String.format(MESSAGE,existingFileName), e); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.dnd.dndtravel.map.exception; | ||
|
||
public class PhotoEmptyException extends RuntimeException{ | ||
private static final String MESSAGE = "이미지가 존재하지 않음"; | ||
|
||
public PhotoEmptyException() { | ||
super(MESSAGE); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package com.dnd.dndtravel.map.exception; | ||
|
||
public class PhotoInvalidException extends RuntimeException { | ||
|
||
public PhotoInvalidException(String message) { | ||
super(message); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package com.dnd.dndtravel.map.exception; | ||
|
||
import java.io.IOException; | ||
|
||
import org.springframework.web.multipart.MultipartFile; | ||
|
||
import com.amazonaws.SdkClientException; | ||
|
||
public class PhotoUploadFailException extends RuntimeException { | ||
private static final String MESSAGE = "s3 이미지 업로드 실패 [image=%s]"; | ||
|
||
public PhotoUploadFailException(MultipartFile image, IOException e) { | ||
super(String.format(MESSAGE, image), e); | ||
} | ||
|
||
public PhotoUploadFailException(String image, SdkClientException e) { | ||
super(String.format(MESSAGE, image), e); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.dnd.dndtravel.map.exception; | ||
|
||
public class RegionNotFoundException extends RuntimeException { | ||
private static final String MESSAGE = "존재하지 않는 지역 [region=%s]"; | ||
|
||
public RegionNotFoundException(String region) { | ||
super(String.format(MESSAGE, region)); | ||
} | ||
} |
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.
사용자의 방문기록을 저장하는 API에서는 클라이언트 입장에서 상태코드 400을 받는 경우의수가 엄청 다양해요.
(방문기록 자체의 검증실패, 유저나 지역이 존재하지 않는경우, 방문기록자체가 없는경우, 요청 헤더에 토큰 형식이 잘못된 경우 등..)
HTTP 바디에는 일반적인 메시지가 전달되고 이는 스웨거에서도 그렇게 표기됩니다. 주석으로 적어놨던 스웨거 문서로 상세오류를 전달한다는건 description 같은 부가설명을 적는부분이에요 스웨거 한번보시면 이해하실듯 해요