π μμΈ μ²λ¦¬ λ°©μ
νλ‘μ νΈλ₯Ό μ§ννλ©΄μ, λ΄κ° μ£Όλ‘ νλ μμΈμ²λ¦¬ λ°©λ²μ λ€μκ³Ό κ°λ€.
Enum νμ μ ErrorCode μ μ
@AllArgsConstructor
@Getter
public enum ErrorCode {
DUPLICATE_USERNAME(HttpStatus.CONFLICT, "μ΄λ―Έ μ‘΄μ¬νλ μ¬μ©μλͺ
μ
λλ€.");
private HttpStatus httpStatus;
private String message;
}
Http μν μ½λμ μλ¬ λ°μ μ μ λ¬ν λ©μΈμ§ μ λ³΄κ° λ΄κΈ΄ Enum νμ
μ ErrorCode
λ₯Ό μ μνλ€.
RuntimeException μ μμνλ AppExceptionμ μ μ
@Getter
public class AppException extends RuntimeException{
private ErrorCode errorCode;
public AppException(ErrorCode errorCode) {
this.errorCode = errorCode;
}
}
ErrorCode
λ₯Ό ν΅ν΄ μμΈ μμΈ μ 보λ₯Ό λ΄κ³ μλ€.
AppExceptionμ μ²λ¦¬νλ Handler μ μ
@RestControllerAdvice
public class ExceptionManager {
@ExceptionHandler(AppException.class)
public ResponseEntity<?> appExceptionHandler(AppException e) {
return ResponseEntity.status(e.getErrorCode().getHttpStatus())
.body(e.getErrorCode().getMessage());
}
}
μμ κ°μ΄ μ μνλ©΄, λ°νμ μ AppException
μ΄ λ°μνλ©΄ appExceptionHandler
λ©μλκ° λμνλ€.
β μ΄λ κ² μμΈ μ²λ¦¬λ₯Ό μ§ννμλλ°, μ΄λ€ μλ¦¬λ‘ μ΄λ£¨μ΄μ§λμ§ μ νν λͺ¨λ₯΄κ³ μ¬μ©ν κ² κ°μ μ°Ύμλ³΄κΈ°λ‘ κ²°μ νμλ€.
π μ΄λ Έν μ΄μ μ€λͺ
@RestControllerAdvice
@RestControllerAdvice
λ @ControllerAdvice
μ @ResponseBody
λ‘ κ΅¬μ±λμ΄μλ€.
@ControllerAdvice
λ μ μμ μΌλ‘ 컨νΈλ‘€λ¬μ μν₯μ λ―ΈμΉλ μν μ νλ€.
κ²½λ‘λ₯Ό λ°λ‘ μ§μ νμ§ μμΌλ©΄, νλ‘μ νΈμ λͺ¨λ ν¨ν€μ§μ μμΌλ©΄μ @Controller
μ΄λ
Έν
μ΄μ
μ΄ μλ ν΄λμ€μμ λ°μνλ μμΈμν©μ μ‘μ μ μλ€.
κ·Έλ¦¬κ³ @ControllerAdvice
κ° μ μ©λ ν΄λμ€μλ @ExceptionHandler
λ₯Ό μ μ©ν λ©μλλ₯Ό μ μνκ³ μ²λ¦¬ν μμΈλ₯Ό λͺ
μν΄μ£Όλ©΄, λ°μλ μμΈμ μΌμΉνλ λ©μλκ° μ€νλλ€.
π‘ λ³΄ν΅ μ»¨νΈλ‘€λ¬ λ¨μμ μλΉμ€ λ¨μ νΈμΆνκ³ , μλΉμ€ λ¨μμ λ¬Έμ κ° μλ κ²½μ°RuntimeException
μμΈλ₯Ό λμ§λ€.
κ²°κ΅μ μλΉμ€ λ¨μ νΈμΆν 컨νΈλ‘€λ¬μ μμΈλ₯Ό μ λ¬νκ³ , κ²°κ΅μ 컨νΈλ‘€λ¬μμ μμΈκ° λ°μνλ―λ‘@ControllerAdvice
μ μν΄ κ°μ§λλ κ²μ΄λ€.
@ExceptionHandler
μ μν ExceptionManager
ν΄λμ€μμ @ExceptionHandler(AppException.class)
μ κ°μ΄ μ¬μ©νλ€.
μμμ μ€λͺ
νλ―, AppException
μ΄ μ»¨νΈλ‘€λ¬μμ κ°μ§λλ©΄, @ExceptionHandler(AppException.class)
κ° μ μ©λ λ©μλ λ‘μ§μ΄ μ€νλλ κ²μ΄λ€.
νλ¦
μ μ 리λμ΄ μλ κ·Έλ¦Όμ λ°κ²¬νλ€.
μμ κ°μ λ°©μμΌλ‘, ν΄λΉ νλ‘μ νΈ λ΄μ μλ 컨νΈλ‘€λ¬μμ λ°μν μμΈλ₯Ό μ΄λ Έν μ΄μ μ ν΅ν΄μ νΉμ λ©μλλ₯Ό μ€νμν€λλ‘ ν¨μΌλ‘μ νλμ ν΄λμ€λ‘ κ΄λ¦¬ν μ μκ² λλ κ²μ΄λ€.
π DispatcherServlet μ νν΄μ³λ³΄μ
DispatcherServlet | doDispatch()
ν΄λΌμ΄μΈνΈμ μμ²μ λ°μ 컨νΈλ‘€λ¬μ λ©μλλ₯Ό μ€ννλ μν μ DispatcherServletμ΄ λ΄λΉνλ€.
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
...
try {
...
try {
...
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
...
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new ServletException("Handler dispatch failed: " + err, err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
...
}
doDispatch()
λ©μλλ₯Ό μ€ννλ©΄
ν΄λΌμ΄μΈνΈμ μμ²μ μ²λ¦¬ν μ μλ 컨νΈλ‘€λ¬λ₯Ό μ°Ύκ³ , νΈλ€λ¬λ₯Ό μ€νν μ΄λν°λ₯Ό μ°Ύμ 컨νΈλ‘€λ¬μ λ©μλλ₯Ό μ€νν λ€
ModelAndView
λΌλ κ°μ²΄μ κ²°κ³Όλ₯Ό 보κ΄νκ³
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException)
λ©μλλ₯Ό μ€ννλ€.
κ·Έλ λ€λ©΄, 컨νΈλ‘€λ¬μ λ©μλλ₯Ό μ€ννλ λΆλΆμΈ
mv = ha.handle(processedRequest, response, mappedHandler.getHandler())
λΆλΆμμ μμΈκ° λ°μνλ©΄
try-catch
λ¬Έμ μν΄ dispatchException = ex
κ° ν λΉλμ΄ processDispatchResult()
λ¬Έμ΄ μ€νλ κ²μΌλ‘ μμΈ‘λλ€.
DispatcherServlet | processDispatchResult()
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException mavDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = mavDefiningException.getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
...
}
λ©μλλ₯Ό νμΈν΄λ³΄λ, μμΈ μν©μ΄ λ°μνλ©΄ exception != null
μ΄λ―λ‘ μ‘°κ±΄λ¬Έ μμ λ‘μ§μ΄ μ€νλ κ²μ΄λ€.
μ¬κΈ°μ λ°μν exception
μ΄ ModelAndViewDefiningException
μ μνλ κ²½μ°μ μλ κ²½μ°λ‘ λλμ΄μ§λλ°
ModelAndViewDefiningException
λ Exception
ν΄λμ€μ νμ ν΄λμ€λ‘, μμΈ μν©μ ν΄λΌμ΄μΈνΈμκ² νλ©΄ ν
νλ¦ΏμΌλ‘μ 보μ¬μ£Όλ μν μ νλ€.
μ°λ¦¬κ° μ μν 컀μ€ν
μμΈλ RuntimeException
μ μμνμ¬ μ μνμΌλ―λ‘ μ΄ μ‘°κ±΄λ¬Έμ ν΄λΉνμ§ μμ κ²μ΄λ€.
λ°λΌμ mv = processHandlerException(request, response, handler, exception)
λ©μλκ° μ€νλ κ²μ΄λ€.
DispatcherServlet | processHandlerException()
@Nullable
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler, Exception ex) throws Exception {
...
if (this.handlerExceptionResolvers != null) {
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
...
}
μ¬κΈ°μ HandlerExceptionResolver
μΈν°νμ΄μ€μ resolveException
λ©μλλ‘ μλ¬κ° μ²λ¦¬λ¨μ μ μ μλ€.
AbstractHandlerExceptionResolver | resolveException()
@Override
@Nullable
public ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
if (shouldApplyTo(request, handler)) {
prepareResponse(ex, response);
ModelAndView result = doResolveException(request, response, handler, ex);
...
return result;
}
else {
return null;
}
}
κ²°κ³Όμ μΌλ‘ HandlerExceptionResolver
μ ꡬν체 μ€ νλμΈ AbstractHandlerExceptionResolver
μ resolveException()
μ νΈμΆνκ³
ν΄λΉ ν΄λμ€μμλ μΆμλ©μλλ‘ μ μλμ΄μλ, ν
νλ¦Ώ λ©μλ ν¨ν΄μΌλ‘ μ μ©λ doResolveException()
μ νΈμΆνλ€.
κ·Έλ¬λ©΄ AbstractHandlerExceptionResolver
ν΄λμ€λ₯Ό μμλ°λ AbstractHandlerMethodExceptionResolver
ν΄λμ€μ
doResolveException()
μ νΈμΆνλ€.
AbstractHandlerMethodExceptionResolver | doResolveException()
@Override
@Nullable
protected final ModelAndView doResolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
HandlerMethod handlerMethod = (handler instanceof HandlerMethod hm ? hm : null);
return doResolveHandlerMethodException(request, response, handlerMethod, ex);
}
AbstractHandlerMethodExceptionResolver
ν΄λμ€ μμ, ν
νλ¦Ώ λ©μλ ν¨ν΄ νμμΌλ‘ νμ ν΄λμ€μΈ
ExceptionHandlerExceptionResolver
μ doResolveHandlerMethodException()
λ©μλλ₯Ό νΈμΆνλ€.
ExceptionHandlerExceptionResolver | doResolveHandlerMethodException()
@Override
@Nullable
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
...
try {
...
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, arguments);
}
catch (Throwable invocationEx) {
...
}
...
}
κ·Έλ¬λ©΄ μ΄ λ©μλμμ getExceptionHandlerMethod()
λ©μλλ₯Ό νΈμΆνλ€.
getExceptionHandlerMethod()
λ©μλκ° μ λ§ ν΅μ¬ λΆλΆμ΄λ€.
ExceptionHandlerExceptionResolver | getExceptionHandlerMethod()
@Nullable
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(
@Nullable HandlerMethod handlerMethod, Exception exception) {
...
for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
ControllerAdviceBean advice = entry.getKey();
if (advice.isApplicableToBeanType(handlerType)) {
ExceptionHandlerMethodResolver resolver = entry.getValue();
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(advice.resolveBean(), method, this.applicationContext);
}
}
}
return null;
}
λ±λ‘λ λͺ¨λ ControllerAdvice
λΉμ κ²μ¬νκ³ μ°λ¦¬κ° doDispatch()
μμλΆν° μ λ¬νλ exception
μ λ°νμΌλ‘ μ²λ¦¬ν μ μλ @ExceptionHandler
μ΄λ
Έν
μ΄μ
κ³Ό μ΅μ
μΌλ‘ μ μν μμΈλ₯Ό μ²λ¦¬ν μ μλ λ©μλλ₯Ό μ°Ύμμ¨λ€.
κ·Έλ κ² μ°Ύμμ¨ ExceptionHandler
λ‘ exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, arguments)
λ©μλλ₯Ό ν΅ν΄ Reflection API λ₯Ό ν΅ν΄ ꡬν λ©μλλ₯Ό νΈμΆνμ¬ μμΈμ²λ¦¬λ₯Ό νκ² λλ€.
μ°Έκ³ ν λΈλ‘κ·Έ : https://mangkyu.tistory.com/24