일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- java
- CVAT
- PostgreSQL
- AWS
- Operating System
- aws s3
- zookeeper
- Kafka
- CSV
- OS
- helm
- kubernetes
- Spring
- Trino
- docker
- Vision
- jvm
- tcp
- EC2
- kubeadm
- kubectl
- Network
- Python
- JavaScript
- airflow
- ip
- grafana
- Packet
- log
- MAC address
- Today
- Total
JUST WRITE
@ControllerAdvice Exception 처리 본문
@ControllerAdvice
@ControllerAdvice를 통해 Application 내 모든 Controller에서 공유할 수 있는 Method를 정의할 수 있다.
@ExceptionHandler, @InitBinder, @ModelAttribute 등의 Annotation을 활용한 Method를 정의할 수 있다.
이번 글에서는 그중에서 @ControllerAdvice를 통해 Global하게 Exception을 처리하는 방법을 정리해보았다.
@ExceptionHandler
Spring 3.2부터 @ControllerAdvice로 @ExceptionHandler를 Global하게 사용하도록 지원해준다.
덕분에 흩어져 있던 @ExceptionHandler를 하나로 모을 수 있게 되었다.
추가적으로
- Status Code뿐만 아니라 Response의 Body까지 control 가능
- Exception 종류에 따라 1개의 Method로 Handle 가능
- RESTful ReponseEntity Response 사용하기 수월
이제부터 Code를 통해서 알아보려 한다.
먼저 사용자 정의 Exception을 만든다.
package com.jsw.app.exception;
import org.springframework.http.HttpStatus;
public class CustomException extends RuntimeException {
// Response HttpStatus
private HttpStatus status;
private String messageCode;
public CustomException (HttpStatus status, String messageCode) {
this.status = status;
this.messageCode = messageCode;
}
public HttpStatus getStatus () {
return this.status;
}
public void setStatus (HttpStatus status) {
this.status = status;
}
public String getMessageCode() {
return this.messageCode;
}
}
RuntimeException을 상속해서 정의해보았다.
여기에 상황에 따라 Response에 넣을 Status와 Body에 담을 Message를 field로 추가하였다.
Message는 Spring의 MessageSource를 활용해 propeties로 관리하였다.
(Message 관리에 대한 부분은 추후에 글로 따로 정리)
@RestControllerAdvice를 통해 ExceptionHandler를 만든다.
package com.jsw.app.handler;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import com.jsw.app.exception.CustomException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
@Autowired
private MessageSource messageSource;
@ExceptionHandler(value = { CustomException.class })
protected ResponseEntity<Map<String, String>> handleCustomException(CustomException e) {
String errMsg = messageSource.getMessage(e.getMessageCode(), null, Locale.getDefault());
log.error("Throw Exception!!! HTTPStatus: {}, Message: {}", e.getStatus(), errMsg);
Map<String, String> messageMap = new HashMap<>();
messageMap.put("message", errMsg);
return ResponseEntity.status(e.getStatus()).body(messageMap);
}
}
@RestControllerAdvice는 @ControllerAdvice에서 @ResponseBody를 추가한 Annotation이다.
동일하게 수행하면서 @ResponseBody를 통해 객체를 Return 할 수 있다.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ControllerAdvice
@ResponseBody
public @interface RestControllerAdvice {
...
...
...
}
위에 정의한 GlobalExceptionHandler를 다시 살펴본다.
@ExceptionHandler Annotation을 통해 CustomException이 발생하면 Method에서 처리하도록 정의하였다.
CustomException 내 field 값들로 ResponseEntity를 만들어 Return 한다.
Controller에서 따로 Exception 처리를 하지 않았다.
package com.jsw.app.controller;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import com.jsw.app.entity.Request;
import com.jsw.app.service.RequestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TaxiController {
@Autowired
private RequestService requestService;
@GetMapping("/taxi-requests")
public ResponseEntity<List<Request>> getCallList () {
return ResponseEntity.ok(requestService.getRequestList());
}
@PostMapping("/taxi-requests")
public ResponseEntity<Request> callTaxi (HttpServletRequest servletRequest, @RequestParam("address") String address) {
String header = servletRequest.getHeader("Authorization");
return ResponseEntity.ok(requestService.makeRequet(address, header));
}
@PostMapping("/taxi-requests/{taxiRequestId}/accept")
public ResponseEntity<Request> acceptCall (HttpServletRequest servletRequest, @PathVariable("taxiRequestId") Long taxiRequestId) {
String header = servletRequest.getHeader("Authorization");
return ResponseEntity.ok(requestService.acceptRequest(taxiRequestId, header));
}
}
CustomException 발생 부분을 살펴보면...
// 이미 다른 기사에 의해 바차 요청이 수락된 경우
if (request.getStatus().equals(RequestStatus.ACCEPT) && request.getDriverId() == null) {
throw new CustomException(HttpStatus.CONFLICT, "err.request.unacceptableRequest");
}
Member member = memberWrapper.get();
if (member.getUserType() != UserRole.DRIVER) {
throw new CustomException(HttpStatus.FORBIDDEN, "err.request.onlyAcceptDriver");
}
해당 CustomException을 발생시키면 위에서 정의한 GlobalExceptionHandler에서 처리한다.
@ControllerAdvice와 @ExceptionHandler를 통해 Global하게 Exception를 처리해 보았다.
@ControllerAdvice는 주로 이렇게 Exception 처리할 때 사용하지만 다른 방식으로도 사용한다.
추후에 다른 방식에 대해서도 정리해보려 한다.
'Programing > Spring' 카테고리의 다른 글
Spring Boot Configuration with Jasypt (0) | 2022.01.13 |
---|---|
HTTP/2.0 적용 (0) | 2022.01.11 |
BeanFactory vs ApplicationContext (0) | 2021.12.21 |
Bean Annotations (0) | 2021.12.20 |
@Autowired (0) | 2021.10.22 |