[✨ Feat] 7주차-API 응답 통일 & 에러 핸들러 #8
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
#️⃣ 연관된 이슈
📝 미션 번호
7주차 Misson
📋 구현 사항
API 응답통일
global 패키지 안에 apiPayload 패키지를 만들어 응답 통일 코드를 작성하였다.
status 패키지를 만들어 에러나 성공 메세지에 대한 enum을 둔 파일을 넣어 분리하였다.
ReasonDTO와 ErrorReasonDTO는 각각 성공와 실패시 응답에 대한 dto이다. 컨트롤러나 예외 처리에서 일관된 응답을 위한 dto이다.
JavaBeans 규칙상 boolean 필드는 기본 게터가
isXxx()가 된다.Lombok이 이렇게 게터를 만들면 Jackson은
is를 접두사로 보고 제거해, 결과적으로 속성 이름을success로 해석하게 된다. (필드명이isSuccess여도 JSON 키가success로 나갈 수 있음)이를 방지하기 위해 게터 명을
getIsSuccess()로 두거나@JsonProperty("isSuccess")로 키 이름을 고정하는 방식을 쓴다. 나는 여기서 게터명을 설정해주었다.BaseCode는 성공 응답 코드를 표준화할대 구현하도록 하는 인터페이스 이다.
getReason과 getReasonHttpStatus 두 메서드 모두 ReasonDTO를 반환하지만 http상태를 포함하냐 안하냐에 따라 구분한 것이다.
해당 메서드들은 status 코드들에 오버라이딩 하여 구현
이제 이 값들을 가지고 ApiResponse를 채우고 공통 응답을 보내게 된다.
예외가 발생하였을 때 successstatus나 errorstatus가 주는 DTO를 꺼내 ApiResponse로 만들어 반환하게 된더ㅏ.
@JsonProperty("isSuccess")와@JsonPropertyOrder로 키 이름과 순서를 보장@JsonInclude(NON_NULL)로result가 없을 때 필드를 아예 생략해 응답을 간결하게 만듦ExceptionAdvice
validation()ConstraintViolationException는 제약 조건 위반 예외로,@Valid가 아닌 메서드 파라미터 수준(@RequestParam, @PathVariable, @RequestHeader등)에서 검증 실패시 발생한다.즉, 컨트롤러 메서드의 파라미터나 Pathvariable에 @NotNull, @SiZe 등을 붙혔을 때, 유효하지 않은 값이 들어오면 이 예외가 던져진다.
handleMethodArgumentNotValid()이 메서드는
MethodArgumentNotValidException를 처리하는 메서드 이다.MethodArgumentNotValidException는@Valid또는@Validated가 붙은 RequestBody 필드 검증에서 실패할 때 발생하는 예외이다.→ 결과적으로
ApiResponse.onFailure("_BAD_REQUEST", errors, null)같은 형식으로 통일된 에러 응답 포맷을 생성ResponseEntityExceptionHandler 메서드
ResponseEntityExceptionHandler는 스프링이 제공하는 예외 처리용 추상 클래스이다. 사진 말고도 엄청 많은 메서드가 이씀..
@ControllerAdvice에서 상속하면 스프링 MVC가 발생시키는 표준 예외들을 미리 정의된 메서드로 잡아서 처리할 수 있게한다.handleHttpRequestMethodNotSupported: 지원하지 않는 HTTP 메서드(ex) put 요청 불가)handleHttpMediaTypeNotSupported: 지원하지 않는 Content-TypehandleMissingServletRequestParameter: 필수 요청 파라미터 누락handleTypeMismatch: 파라미터 타입 불일치handleMethodArgumentNotValid:@Valid로 DTO 검증 실패 시 발생handleBindException: GET 파라미터 바인딩 실패handleMissingPathVariable: PathVariable 누락handleServletRequestBindingException: 헤더, 쿠키 바인딩 실패handleExceptionInternal:ResponseEntity만들 때 내부적으로 사용됨exception()위의 핸들러들에서 잡지 못하는 모든 에러들을 처리하는 핸들러 이다.
NullPointerException,ArrayIndexOutOfBoundsException등 개발자가 예측하지 못한 런타임 에러들..서버에러
_INTERNAL_SERVER_ERROR (HTTP 500)상태를 기반으로 표준 응답(ResponseEntity)을 생성하여 반환한다. ⇒handleExceptionInternalFalse호출onThrowException이 메서드는 개발자가 직접 던진 커스텀 예외를 처리하는 코드이다.
비지니스 로직에서 발생하는 예외를 잡아서 표준 응답 형식으로 바꿔주는 역할 !
예외 발생시 ErrorReasonDTO → ApiResponse → ResponseEntity 로 변환 ⇒
handleExceptionInternal호출handleExceptionInternalExceptionAdvice 안에서 반복적으로 처리되는 “예외 → ApiResponse → ResponseEntity 변환” 과정을 한 곳에 모아 재사용하도록 하는 헬퍼 메소드 이다.
data 필드는 null로 설정
👉 핸들러(
onThrowException)는 언제!! 예외를 처리할지 정함👉 헬퍼(
handleExceptionInternal)는 어떻게!! 응답을 만들지 정함handleExceptionInternalFalseexception()메서드에서 호출하는 메서드로 일반적인 예외가 발생했을때 일관된 형태(ApiResponse)로 감싸서 ResponseEntity로 만들어주는 헬퍼 메서드이다.여기서는 data 필드에 어디서 에러 메세지를 담는 errorpoint 를 담눈다.
handleExceptionInternalArgsMethodArgumentNotValidException (DTO 유효성 검사) 처리를 위한 표준 응답 ResponseEntity를 생성하는 헬퍼메소드 이다.
handleExceptionInternalConstraintConstraintViolationException (파라미터 유효성 검사) 처리를 위한 표준 응답 ResponseEntity를 생성하는 헬퍼메소드이다.
예외 처리 로직
🚨 예외 발생 ->
throw new ErrorHandler(ErrorStatus.___)GeneralException을 상속받은ErrorHandler생성ExceptionAdvice (@RestControllerAdvice)에서 잡힘handleExceptionInternal( )호출ApiResponse.onFailure( )로 통일된 실패 응답 생성ResponseEntity(ApiResponse)로 클라이언트에게 JSON 반환WebRequest
ExceptionAdvice코드에서WebRequest가 계속 보이길래HttpServletRequest을 안쓰고 왜 이걸 쓰는지 궁금해서 찾아봤다. WebRequst는 SpringMVC에서 HTTP 요청 정보를 추상화한 인터페이스이다.HttpServletRequest는 java servlet 에서 제공하는 인터페이스이고, 이WebRequest은 Spring Framework에서 제공하는 인터페이스이다.HttpServletRequest보다 높은 수준의 추상화를 제공하고, spring mvc를 사용할때 spring의 다양한 기능과 통합할 수 있는 장점이 있다고 한다.컨트롤러단에서는
HttpServletRequest를 자주 사용하고, 예외 처리나 핸들러에서는WebRequest를 사용하는 것이 더 일반적이고 범용성이 높다고 함.>!>>!@RestControllerAdvice
여기서 볼 수 있듯이 RestControllerAdvice는 ControllerAdvice와 ResponseBody를 합친 것이다. 전역 예외를 처리하되, 그 결과를 json으로 변환할 수 있도록 한다.
@RestControllerAdvice가 등록되어 있다면, 스프링이 이 예외를 대신 처리할 수 있는 핸들러가 있는지 찾음@ExceptionHandler메서드 실행ResponseEntity형태(JSON 응답)로 클라이언트에게 반환장점
전역 예외 처리 - 한곳에서 모든 예외를 처리 ⇒ 코드 중복 제거
모든 컨트롤러에서 발생한 예외를 하나의 클래스에서 일괄처리할 수 있따. 이로서, 컨트롤러마다 try-catch문을 반복해서 작성할 필요가 없고, 예외나 로깅/응답 형식 통일이 가능하다. 따라서 유지보수가 쉽다는 장점이 있다. 모든 컨트롤러마다 예외 처리 코드를 적어야한다면......끔찍....
응답 형식 통일 - ApiResponse 구조로 일관된 에러 응답 제공
모든 오류응답이 같은 json 형태로 제공된다. 이건 프론트엔드와의 협업에서도 굉장히 중요한 부분이기에 꼭 필요하다. 만약 응답 형식이 통일되지 않으면 컨트롤러마다 구조가 달라져버리기때문에 api 명세 유지가 어렵고, 프론트엔드에서 일관된 처리가 불가능하다.
예외 종류별 세밀한 분기 처리 가능
GeneralException→ 비즈니스 예외ConstraintViolationException→ 요청 파라미터 유효성 실패MethodArgumentNotValidException→ @Valid DTO 검증 실패Exception→ 그 외 모든 예외시니어 미션 - 500 에러 발생 시 알림 전송 기능 구현 (디스코드)
일단 결과 사진만 첨부할게요...
✅ 체크리스트
🤔 질문 & 고민