-
Notifications
You must be signed in to change notification settings - Fork 171
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
[4기 - 김민희] SpringBoot Part3 WeeklyMission 제출합니다. #867
base: KimMinheee/week01
Are you sure you want to change the base?
Changes from all commits
c3ede23
58b7672
ac57590
3fc14eb
0994f2b
f372e31
ae7edeb
4ba6d1c
1d463a8
48125fd
3cd95ee
845c28c
b21a1c3
7d85b6c
7f990fe
6f1e443
503e7d7
a250699
f4fdb1f
98797c9
09b519e
bb5fd7e
5c5bd26
8597271
7d99b66
c9777fe
c1d69a3
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,19 @@ | ||
package org.programmers.VoucherManagement.global.entity; | ||
|
||
import org.springframework.stereotype.Component; | ||
|
||
import java.time.LocalDateTime; | ||
import java.time.format.DateTimeFormatter; | ||
|
||
@Component | ||
public class BaseTimeEntity { | ||
protected String createdAt; | ||
|
||
public BaseTimeEntity() { | ||
this.createdAt = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); | ||
} | ||
|
||
public String getCreatedAt() { | ||
return createdAt; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
package org.programmers.VoucherManagement.global.exception; | ||
|
||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.programmers.VoucherManagement.global.response.ErrorCode; | ||
import org.programmers.VoucherManagement.global.response.ErrorResponse; | ||
import org.programmers.VoucherManagement.member.exception.MemberException; | ||
import org.programmers.VoucherManagement.voucher.exception.VoucherException; | ||
import org.programmers.VoucherManagement.wallet.exception.WalletException; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.validation.BindException; | ||
import org.springframework.web.bind.MethodArgumentNotValidException; | ||
import org.springframework.web.bind.MissingServletRequestParameterException; | ||
import org.springframework.web.bind.annotation.ExceptionHandler; | ||
import org.springframework.web.bind.annotation.RestControllerAdvice; | ||
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; | ||
|
||
@RequiredArgsConstructor | ||
@RestControllerAdvice | ||
@Slf4j | ||
public class GlobalExceptionHandler { | ||
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. 이름좋은데요! |
||
/** | ||
* [Exception] 객체 혹은 파라미터의 데이터 값이 유효하지 않은 경우 | ||
*/ | ||
@ExceptionHandler(MethodArgumentNotValidException.class) | ||
protected ResponseEntity<ErrorResponse> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { | ||
log.error("Handle MothodArgumentNotValidException", e.getMessage()); | ||
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. TRACE, DEBUG, INFO, WARN, ERROR 등 |
||
final ErrorResponse response = ErrorResponse.of(ErrorCode.INVALID_METHOD_ERROR, e.getBindingResult()); | ||
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST); | ||
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. ResponseEntity.badRequest().body() 라는 정적 팩토리 메소드가 존재합니다. |
||
} | ||
|
||
/** | ||
* [Exception] 클라이언트에서 request로 '파라미터로' 데이터가 넘어오지 않았을 경우 | ||
* | ||
* @param ex MissingServletRequestParameterException | ||
* @return ResponseEntity<ErrorResponse> | ||
*/ | ||
@ExceptionHandler(MissingServletRequestParameterException.class) | ||
protected ResponseEntity<ErrorResponse> handleMissingRequestHeaderExceptionException( | ||
MissingServletRequestParameterException ex) { | ||
log.error("Handle MissingServletRequestParameterException", ex); | ||
final ErrorResponse response = ErrorResponse.of(ErrorCode.REQUEST_PARAM_MISSING_ERROR, ex.getMessage()); | ||
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST); | ||
} | ||
|
||
/** | ||
* [Exception] enum type 일치하지 않아 binding 못할 경우 | ||
* 주로 @RequestParam enum으로 binding 못했을 경우 발생 | ||
*/ | ||
@ExceptionHandler(MethodArgumentTypeMismatchException.class) | ||
protected ResponseEntity<ErrorResponse> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) { | ||
log.error("Handle MethodArgumentTypeMismatchException", e); | ||
final ErrorResponse response = ErrorResponse.of(ErrorCode.INVALID_INPUT_VALUE_ERROR, e.getMessage()); | ||
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST); | ||
} | ||
|
||
|
||
/** | ||
* [Exception] com.fasterxml.jackson.core 내에 Exception 발생하는 경우 | ||
* | ||
* @param ex JsonProcessingException | ||
* @return ResponseEntity<ErrorResponse> | ||
*/ | ||
@ExceptionHandler(JsonProcessingException.class) | ||
protected ResponseEntity<ErrorResponse> handleJsonProcessingException(JsonProcessingException ex) { | ||
log.error("handleJsonProcessingException", ex); | ||
final ErrorResponse response = ErrorResponse.of(ErrorCode.REQUEST_BODY_MISSING_ERROR, ex.getMessage()); | ||
return new ResponseEntity<>(response, HttpStatus.OK); | ||
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. ResponseEntity.ok() |
||
} | ||
|
||
/** | ||
* [Exception] @ModelAttribute 으로 binding error 발생할 경우 | ||
*/ | ||
@ExceptionHandler(BindException.class) | ||
protected ResponseEntity<ErrorResponse> handleBindException(BindException e) { | ||
log.error("Handle BindException : ", e); | ||
final ErrorResponse response = ErrorResponse.of(ErrorCode.INVALID_INPUT_VALUE_ERROR, e.getBindingResult()); | ||
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST); | ||
} | ||
|
||
/** | ||
* [Exception] 서버에 정의되지 않은 모든 예외 | ||
*/ | ||
@ExceptionHandler(Exception.class) | ||
public ResponseEntity<ErrorResponse> handleAllException(Exception e) { | ||
log.error("Handle Exception :", e); | ||
final ErrorResponse response = ErrorResponse.of(ErrorCode.INTERNAL_SERVER_ERROR, e.getMessage()); | ||
return new ResponseEntity<>(response, HttpStatus.OK); | ||
Comment on lines
+89
to
+90
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. Exception은 internal server error가 맞는 것 같아요. |
||
} | ||
|
||
/** | ||
* [Exception] 커스텀 예외 - MemberException | ||
*/ | ||
@ExceptionHandler(MemberException.class) | ||
public ResponseEntity<ErrorResponse> handleNotFoundException(MemberException e) { | ||
log.error("Handle NotFoundException :", e); | ||
final ErrorResponse response = ErrorResponse.of(e.getErrorCode(), e.getMessage()); | ||
return new ResponseEntity<>(response, HttpStatus.OK); | ||
} | ||
Comment on lines
+96
to
+101
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. 404 혹은 400이 맞는 에러코드인 것 같습니다. 멤버를 못찾는 요청이 잘못된 경우(400), 찾는 멤버가 없는 경우(404) |
||
|
||
/** | ||
* [Exception] 커스텀 예외 - VoucherException | ||
*/ | ||
@ExceptionHandler(VoucherException.class) | ||
public ResponseEntity<ErrorResponse> handlePermissionDeniedException(VoucherException e) { | ||
log.error("Handle PermissionDeniedException :", e); | ||
final ErrorResponse response = ErrorResponse.of(e.getErrorCode(), e.getMessage()); | ||
return new ResponseEntity<>(response, HttpStatus.OK); | ||
} | ||
Comment on lines
+106
to
+111
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. permission denied에 맞는 http status 를 알아보시면 좋을 것 같네요. 아래도 마찬가지고요. 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. 혹은 단순히 400으로 볼 수도 있고요. 순전히 요청이 잘못됐다고 서버가 판단해야하는 포괄적인 경우 http status 에 의해서 비즈니스가 노출될 수 있으니깐요. |
||
|
||
/** | ||
* [Exception] 커스텀 예외 - WalletException | ||
*/ | ||
@ExceptionHandler(WalletException.class) | ||
public ResponseEntity<ErrorResponse> handlePermissionDeniedException(WalletException e) { | ||
log.error("Handle PermissionDeniedException :", e); | ||
final ErrorResponse response = ErrorResponse.of(e.getErrorCode(), e.getMessage()); | ||
return new ResponseEntity<>(response, HttpStatus.OK); | ||
} | ||
Comment on lines
+116
to
+121
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. permission denied에 맞는 http status 를 알아보시면 좋을 것 같네요. 아래도 마찬가지고요. 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. 혹은 단순히 400으로 볼 수도 있고요. 순전히 요청이 잘못됐다고 서버가 판단해야하는 포괄적인 경우 http status 에 의해서 비즈니스가 노출될 수 있으니깐요. |
||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package org.programmers.VoucherManagement.global.response; | ||
|
||
import com.fasterxml.jackson.annotation.JsonInclude; | ||
import com.fasterxml.jackson.annotation.JsonPropertyOrder; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
|
||
import static org.programmers.VoucherManagement.global.response.SuccessCode.SUCCESS; | ||
|
||
@Getter | ||
@AllArgsConstructor | ||
@JsonPropertyOrder({"code", "message", "result"}) | ||
public class BaseResponse<T> { | ||
private final String message; | ||
private final String code; | ||
@JsonInclude(JsonInclude.Include.NON_NULL) | ||
private T result; | ||
|
||
Comment on lines
+16
to
+18
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. 이부분은 클라이언트와의 협업간에도 고민해볼 포인트 인것같아요. |
||
/** | ||
* 요청에 성공하고 반환값이 있는 경우 | ||
* | ||
* @param result | ||
*/ | ||
public BaseResponse(T result) { | ||
this.message = SUCCESS.getMessage(); | ||
this.code = SUCCESS.getCode(); | ||
this.result = result; | ||
} | ||
|
||
/** | ||
* 요청에 성공하고 반환값이 없는 경우 | ||
* | ||
* @param status | ||
*/ | ||
public BaseResponse(SuccessCode status) { | ||
this.message = status.getMessage(); | ||
this.code = status.getCode(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package org.programmers.VoucherManagement.global.response; | ||
|
||
public enum ErrorCode { | ||
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. LGTM Http와 관련되었으니 HttpErrorCode는 어떨까요? |
||
/** | ||
* 10000번 -> Global에서 발생하는 에러코드 관리 | ||
* [Http Status code] | ||
* 400 : Bad Request | ||
* 401 : Unauthorized | ||
* 403 : Forbidden | ||
* 404 : Not Found | ||
* 405 : Method Not Allowed | ||
* 500 : Internal Server Error | ||
*/ | ||
FAIL(500, "10000", "요청에 실패하였습니다."), | ||
INVALID_INPUT_VALUE_ERROR(400, "10001", "유효하지 않은 입력값입니다."), | ||
INVALID_METHOD_ERROR(405, "10002", "Method Argument가 적절하지 않습니다."), | ||
REQUEST_BODY_MISSING_ERROR(400, "10003", "RequestBody에 데이터가 존재하지 않습니다."), | ||
REQUEST_PARAM_MISSING_ERROR(400, "10004", "RequestParam에 데이터가 전달되지 않았습니다."), | ||
INVALID_TYPE_VALUE_ERROR(400, "10005", "타입이 유효하지 않습니다."), | ||
INTERNAL_SERVER_ERROR(500, "10006", "서버 오류 입니다."), | ||
|
||
/** | ||
* ========================================================================== | ||
* 20000번 -> Member에서 발생하는 에러코드 관리 | ||
* ========================================================================== | ||
*/ | ||
NOT_EXIST_MEMBER_STATUS(400, "M20000", "해당하는 회원 상태가 존재하지 않습니다."), | ||
NOT_FOUND_MEMBER(404, "M20001", "회원을 찾을 수 없습니다."), | ||
FAIL_TO_INSERT_MEMBER(500, "M20002", "데이터가 정상적으로 저장되지 않았습니다."), | ||
FAIL_TO_UPDATE_MEMBER(500, "M20003", "데이터가 정상적으로 수정되지 않았습니다."), | ||
FAIL_TO_DELETE_MEMBER(500, "M20004", "데이터가 정상적으로 삭제되지 않았습니다."), | ||
|
||
/** | ||
* ========================================================================== | ||
* 30000번 -> Voucher에서 발생하는 에러코드 관리 | ||
* ========================================================================== | ||
*/ | ||
NOT_INCLUDE_1_TO_100(400, "V30000", "할인율은 1부터 100사이의 값이여야 합니다."), | ||
AMOUNT_IS_NOT_NUMBER(400, "V30001", "숫자만 입력가능합니다."), | ||
NOT_EXIST_COMMAND(400, "V30002", "해당하는 Command가 존재하지 않습니다."), | ||
NOT_EXIST_DISCOUNT_TYPE(400, "V30003", "해당하는 유형의 바우처가 존재하지 않습니다."), | ||
NOT_FOUND_VOUCHER(404, "V30004", "바우처를 찾을 수 없습니다."), | ||
FAIL_TO_INSERT_VOUCHER(500, "V30005", "데이터가 정상적으로 저장되지 않았습니다."), | ||
FAIL_TO_UPDATE_VOUCHER(500, "V30006", "데이터가 정상적으로 수정되지 않았습니다."), | ||
FAIL_TO_DELETE_VOUCHER(500, "V30007", "데이터가 정상적으로 삭제되지 않았습니다."), | ||
|
||
/** | ||
* ========================================================================== | ||
* 40000번 -> Wallet에서 발생하는 에러코드 관리 | ||
* ========================================================================== | ||
*/ | ||
FAIL_TO_INSERT_WALLET(500, "W40000", "데이터가 정상적으로 저장되지 않았습니다."), | ||
FAIL_TO_DELETE_WALLET(500, "W40001", "데이터가 정상적으로 삭제되지 않았습니다."); | ||
|
||
Comment on lines
+42
to
+54
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. 클라이언트에게 500 응답을 줘서 서버에 문제가 있다 라는것을 노출 시킬지 여부를 고민 해보시면 좋을것같아요 |
||
|
||
private final int status; //코드 상태(Http) | ||
private final String divisionCode; //서버 내 코드 구분 값 | ||
private final String message; //에러 코드 메시지 | ||
|
||
ErrorCode(int status, String divisionCode, String message) { | ||
this.status = status; | ||
this.divisionCode = divisionCode; | ||
this.message = message; | ||
} | ||
|
||
public String getMessage() { | ||
return message; | ||
} | ||
|
||
public int getStatus() { | ||
return status; | ||
} | ||
|
||
public String getDivisionCode() { | ||
return divisionCode; | ||
} | ||
|
||
} |
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.
이 entity는 Component가 아닌것 같아요