Spring - RequiredArgsConstructor, ExceptionHandler, ControllerAdvice

annotation 알고 쓰기

Posted by Yan on November 16, 2023

annotation

1. RequiredArgsConstructor

속성에 final을 적용하고 롬복의 @RequiredArgsConstructor 애너테이션을 적용하면 해당 속성을 필요로하는 생성자가 롬복에 의해 자동으로 생성된다. (※ final이 없는 속성은 생성자에 포함되지 않는다.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import lombok.Getter;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Getter
// @Setter
public class HelloLombok {

    private final String hello;
    private final int lombok;

    public static void main(String[] args) {
        HelloLombok helloLombok = new HelloLombok("헬로", 5);
        System.out.println(helloLombok.getHello());
        System.out.println(helloLombok.getLombok());
    }
}

final을 적용했기 때문에 @Setter는 의미가 없으며 Setter 메서드들도 사용할수 없다. final은 한번 설정한 값을 변경할수 없게 하는 키워드이다.

@ExceptionHandler

Spring MVC에서 에러를 처리하는 방식 2가지 : HandlerExceptionResolver, @ExceptionHandler

@ExceptionHandler는 Controller레벨에서만 작동

  • Controller 계층에서 발생하는 에러를 잡아서 메소드로 처리해주는 기능
  • Base Controller 클래스를 상속한 Controller만 해당됨.
  • Service, Repository에서 발생하는 에러는 제외함

HandlerExceptionResolver는 통일된 예외 처리 메커니즘을 구현할 수 있다.

  • ExceptionHandlerExceptionResolver
  • DefaultHandlerExceptionResolver
    • 응답의 상태 코드를 적절하게 설정한다.
    • 제한 사항: 응답 본문에 아무 것도 설정하지 않는다는 것 (애플리케이션이 실패에 대한 추가 정보를 제공할 수 있도록 응답에도 본문이 있어야 한다.)
  • ResponseStatusExceptionResolver
    • 사용자 정의 예외에 사용할 수 있는 @ResponseStatus 주석을 사용 하고 이러한 예외를 HTTP 상태 코드에 매핑
    • 응답 본문을 처리하는 방식이 제한이 있음 (응답에 상태코드를 매핑하지만 본문은 null)
  • 사용자 정의 HandlerExceptionResolver
    • 장점 : spring RESTful 서비스에 오류 처리 메커니즘 생성
    • 단점 : 응답 본문을 제어할 수 없음

@ControllerAdvice

  • @ExceptionHandler@ControllerAdvice을 붙이면 전역으로 사용 가능
  • @Controller와 handler에서 발생하는 에러들을 모두 잡아준다.
  • 흩어져 있던 여러 @ExceptionHandler 를 단일 전역 오류 처리 구성 요소로 통합 할 수 있다
  • 응답 본문과 상태 코드를 완전히 제어할 수 있다.
  • 함께 처리되도록 동일한 메서드에 대한 여러 예외 매핑을 제공한다.
  • 최신 RESTful ResposeEntity 응답을 효과적으로 활용한다. (ResponseEntity는 HttpEntity를 상속 받았다. HttpEnitiy는 HttpMessageConverter로 컨버팅이 된다.)
1
2
3
4
5
6
7
8
9
10
11
12
13
@ControllerAdvice
public class RestResponseEntityExceptionHandler 
  extends ResponseEntityExceptionHandler {

    @ExceptionHandler(value 
      = { IllegalArgumentException.class, IllegalStateException.class })
    protected ResponseEntity<Object> handleConflict(
      RuntimeException ex, WebRequest request) {
        String bodyOfResponse = "This should be application specific";
        return handleExceptionInternal(ex, bodyOfResponse, 
          new HttpHeaders(), HttpStatus.CONFLICT, request);
    }
}
주의할 것
  • @ExceptionHandler 로 선언된 예외를 메서드의 인수로 사용되는 예외와 일치시켜야 한다.

ResponseStatusException (Spring 5 이상)

  • HttpStatus 를 제공 하고 선택적으로 이유 와 원인을 제공하는 인스턴스를 만들 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
@GetMapping(value = "/{id}")
public Foo findById(@PathVariable("id") Long id, HttpServletResponse response) {
    try {
        Foo resourceById = RestPreconditions.checkFound(service.findOne(id));

        eventPublisher.publishEvent(new SingleResourceRetrievedEvent(this, response));
        return resourceById;
     }
    catch (MyResourceNotFoundException exc) {
         throw new ResponseStatusException(
           HttpStatus.NOT_FOUND, "Foo Not Found", exc);
    }
}
reference

점프 투 스프링 부트
Error Handling for REST with Spring