_DoYun
_yunilog
_DoYun
전체 방문자
오늘
어제
  • 전체 (83)
    • spring boot main 프로젝트 해결 (2)
    • 회고 (0)
      • pre-project(stackoverflow) (0)
    • 지식창고 (25)
    • 후기 (1)
    • LINUX (2)
    • HTML&CSS (2)
    • SQL (2)
    • 기술 면접 질문지 (1)
      • Chapter1 (1)
      • Chapter2 (0)
    • JAVA (25)
      • JAVA 기초 문법 (1)
      • Collection (1)
      • Enum,Annotation,Stream,람다 (3)
      • 입출력, Thread, JVM (1)
      • Spring Framework (3)
      • Spring MVC (6)
      • JPA (1)
      • Test (3)
      • API 문서 (1)
      • 인증&보안 (2)
      • AWS (2)
    • 알고리즘 (19)
      • 프로그래머스_LEVEL_3 (6)
      • 백준 (0)
      • 프로그래머스_LEVEL_2 (13)
    • Comento (2)
    • Inflearn (2)
      • HTTP (2)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
_DoYun

_yunilog

JAVA/Spring MVC

자바 Spring MVC에서의 예외 처리 @ExceptionHandler, @RestControllerAdvice

2022. 6. 30. 15:35

이전 글에서 배운 DTO 클래스의 유효성 검증을 통해 위 동그라미 부분처럼 Email 형식을 지키지 않던가, 아무 값도 들어있지 않다면 그 아래처럼 에러 메시지가 전송된다는 것을 확인할 수 있었습니다. 

 

2022.06.28 - [CodeStates/Spring MVC] - [자바 Spring] DTO에 대해 배워보자

 

[자바 Spring] DTO에 대해 배워보자

DTO(Data Transfer Object)란 계층간 데이터 교환을 위해 사용하는 객체(Java Beans)입니다. DTO 중 T인 Transfer라는 의미에서 알 수 있듯이 데이터를 전송하기 위한 용도의 객체 정도로 생각할 수 있습니다.

jungdo8016.tistory.com

 

 

효과적인 예외처리를 위해 별도의 클래스 하나를 생성하겠습니다. 

@Getter
@AllArgsConstructor
public class ErrorResponse {
		// (1)
    private List<FieldError> fieldErrors;

    @Getter
    @AllArgsConstructor
    public static class FieldError {
        private String field;
        private Object rejectedValue;
        private String reason;
    }
}

 

먼저 FieldError 클래스를 ErrorResponse 클래스의 내부 클래스로 설정하였습니다.

 

이유는 예외처리 해야할 변수의 종류가 한 가지가 아니기 떄문입니다. 예를 들어 이전 자료를 보면 email과 name, phone 변수의 DTO 유효성 검증에서 세개 모두 유효성을 위반되는 것을 확인할 수 있었습니다. 

 

그럼 예외처리를 총 3번하여 결과를 클라이언트에게 응답하여야 합니다. 따라서 ErrorResponse 클래스의 필드인 fieldErrors 리스트의 각 요소는 하나하나는 하나의 예외처리라고 인지하면 될 것 같습니다. 

 

@RestController
@RequestMapping("/v7/members")
@Validated
@Slf4j
public class MemberController {
    ...
		...

    @PostMapping
    public ResponseEntity postMember(@Valid @RequestBody MemberPostDto memberDto) {
        Member member = mapper.memberPostDtoToMember(memberDto);

        Member response = memberService.createMember(member);

        return new ResponseEntity<>(mapper.memberToMemberResponseDto(response),
                HttpStatus.CREATED);
    }

		...
		...

    @ExceptionHandler
    public ResponseEntity handleException(MethodArgumentNotValidException e) {
				// (1)
        final List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();

				// (2)
        List<ErrorResponse.FieldError> errors =
                fieldErrors.stream()
                            .map(error -> new ErrorResponse.FieldError(
                                error.getField(),
                                error.getRejectedValue(),
                                error.getDefaultMessage()))
                            .collect(Collectors.toList());

        return new ResponseEntity<>(new ErrorResponse(errors), HttpStatus.BAD_REQUEST);
    }
}

위 코드의 @ExceptionHandler는 예외 처리 메소드라는 의미를 가지고 있습니다. handleException 메소드 위의 Controller 메소드 들에서 DTO가 유효성 검증에 실패했을 때, 자동으로 해당 메소드가 실행되어 (1)의 fieldErrors 리스트에 정보가 전달 되게 됩니다. 

 

그 정보들을 이전에 작성하였던 ErrorResponse 클래스 내부의 FieldError 클래스에 일부 필요한 정보만 전달하여 새로운 리스트를 생성합니다. 

 

마지막으로 ResponseEntity<>()를 통해 예외 처리 결과를 BAD_REQUEST 상태 메시지와 함께 반환하면 마치게 됩니다.

 

위처럼 어떤 부분에서 유효성 검증 실패가 일어났는지 이유와 항목이 반환되게 됩니다. 

 

# ExceptionHandler의 단점

1. 각각의 Controller 클래스에서 @ExceptionHandler 애너테이션을 사용하여 Request Body에 대한 유효성 검증 실패에 대한 에러 처리를 해야되므로 각 Controller 클래스마다 코드 중복이 발생합니다.

 

2. Controller에서 처리해야 되는 예외(Exception)가 유효성 검증 실패에 대한 예외 (MethodArgumentNotValidException)만 있는것이 아니기 때문에 하나의 Controller 클래스 내에서 @ExceptionHandler를 추가한 에러 처리 핸들러 메서드가 늘어납니다.

 

# RestControllerAdvice를 사용한 예외 처리 공통화

위 같이 @ExceptionHandler의 중복되는 단점들을 해결하기 위해 적용된 개념이 @RestControllerAdvice 입니다.

 

@RestControllerAdvice 애너테이션을 추가한 클래스를 이용하면 예외 처리를 공통화 할 수 있습니다.

 

@RestControllerAdvice
public class GlobalExceptionAdvice {
		// (1)
    @ExceptionHandler
    public ResponseEntity handleMethodArgumentNotValidException(
            MethodArgumentNotValidException e) {
        final List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();

        List<ErrorResponse.FieldError> errors =
                fieldErrors.stream()
                        .map(error -> new ErrorResponse.FieldError(
                                error.getField(),
                                error.getRejectedValue(),
                                error.getDefaultMessage()))
                        .collect(Collectors.toList());

        return new ResponseEntity<>(new ErrorResponse(errors), HttpStatus.BAD_REQUEST);
    }
 }

이전에 Controller 내부마다 존재했던 handleMethodArgumentNotValidException 메소드를 @RestControllerAdvice가 붙어있는 별도의 클래스에 새롭게 작성하였습니다. 

 

'JAVA > Spring MVC' 카테고리의 다른 글

[자바 intellij] @RequestParam? @ModelAttribute? @RequestBody는 또 뭐야??  (0) 2022.08.14
[Spring 자바] Mapper에 대해 알아보자  (1) 2022.06.29
[자바 Spring] DTO에 대해 배워보자  (1) 2022.06.28
[자바 Spring] HTTP Request 헤더(Header) 정보 얻기  (0) 2022.06.26
자바 Spring MVC의 개념에 대해 알아보자  (0) 2022.06.25
    'JAVA/Spring MVC' 카테고리의 다른 글
    • [자바 intellij] @RequestParam? @ModelAttribute? @RequestBody는 또 뭐야??
    • [Spring 자바] Mapper에 대해 알아보자
    • [자바 Spring] DTO에 대해 배워보자
    • [자바 Spring] HTTP Request 헤더(Header) 정보 얻기
    _DoYun
    _DoYun

    티스토리툴바