API μ˜ˆμ™Έ 처리 (+HandlerExceptionResolver)

HTML νŽ˜μ΄μ§€μ˜ κ²½μš°λŠ” 4xx, 5xx와 같은 μ—λŸ¬ νŽ˜μ΄μ§€λ§Œ μ‘΄μž¬ν•˜λ©΄ ν•΄κ²°ν•  수 μžˆμ—ˆλ‹€.

μ—λŸ¬ νŽ˜μ΄μ§€λŠ” ν΄λΌμ΄μ–ΈνŠΈμ—κ²Œ ν™”λ©΄λ§Œ 보여주면 λ˜μ§€λ§Œ, APIλŠ” 상황에 λ§žλŠ” μ—λŸ¬ 응닡 μŠ€νŽ™μ„ ν΄λΌμ΄μ–ΈνŠΈμ™€ μ •ν•˜κ³  JSONκ³Ό 같은 ν˜•μ‹μœΌλ‘œ 데이터λ₯Ό 전달해야 ν•œλ‹€.

μ„œλΈ”λ¦Ώ 처리 - μ—λŸ¬ νŽ˜μ΄μ§€λ₯Ό JSON으둜

// [μˆ˜μ • μ „]
@Component
public class WebServerCustomizer 
		implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> {

   @Override
   public void customize(ConfigurableWebServerFactory factory) {
	ErrorPage errorpage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/custom-page");
	...
	...
	factory.addErrorPages(errorpage404, ...);
   } 
}

@RequestMapping("/error/page")
public String errorPage() {
	...
	return "error-page";
}

// [μˆ˜μ • ν›„]
@RequestMapping("/error/page", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> errorApi() {
	...
	return ResponseEntity<>(....);
}

μˆ˜μ • μ „ 상황이면, ν΄λΌμ΄μ–ΈνŠΈ-μ„œλ²„κ°€ JSON 톡신을 ν•˜λŠ” κ²½μš°μ—λ„ error pageλ₯Ό λ°˜ν™˜ν•΄λ²„λ¦°λ‹€. μ•½μ†ν–ˆλ˜ 데이터 ν˜•μ‹μ΄ μ•„λ‹ˆλ‹€.

ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ Accpet μš”μ²­ 헀더가 applicaiton/json 이라면, producesλ₯Ό κ°€μ§„ 것이 μš°μ„ μˆœμœ„λ₯Ό κ°€μ§€κΈ° λ•Œλ¬Έμ— errorApi()λ₯Ό ν˜ΈμΆœν•œλ‹€.

  • Acceptκ°€ β€œ/”과 같은 μ•„λ¬΄κ±°λ‚˜ λ“€μ–΄μ˜€λ©΄, html을 λ°˜ν™˜ν•΄μ£ΌλŠ” 것이 μš°μ„ μˆœμœ„κ°€ λœλ‹€.

컨트둀러 ~ WASκΉŒμ§€ λ™μž‘μ€ 변함이 μ—†μ§€λ§Œ, λ™μΌν•œ requestMapping이 2κ°œκ°€ μ‘΄μž¬ν•œλ‹€λ©΄, Accept둜 μ–΄λ–€ 것을 ν˜ΈμΆœν• μ§€ μ œμ–΄ν•  수 μžˆλ‹€.

HandlerExceptionResolver

μ˜ˆμ™Έκ°€ μ„œλΈ”λ¦Ώμ„ μ§€λ‚˜μ„œ μ„œλΈ”λ¦Ώ μ»¨ν…Œμ΄λ„ˆ WASκΉŒμ§€ μ˜ˆμ™Έκ°€ μ „λ‹¬λ˜λ©΄ μƒνƒœ μ½”λ“œκ°€ 500으둜 μ²˜λ¦¬λœλ‹€.

λ°œμƒν•˜λŠ” μ˜ˆμ™Έμ— λ”°λΌμ„œ 400, 404λ“± λ‹€λ₯Έ μƒνƒœ μ½”λ“œλ“€λ„ μ²˜λ¦¬ν•˜λ©΄μ„œ λ©”μ‹œμ§€ 등을 APIλ§ˆλ‹€ μ²˜λ¦¬ν•˜κ³  μ‹Άλ‹€.

ν΄λΌμ΄μ–ΈνŠΈκ°€ 잘λͺ»λœ μš”μ²­μ„ λ³΄λ‚΄μ„œ 4xx μƒνƒœ μ½”λ“œλ₯Ό μ „λ‹¬ν•˜κ³  μ‹Άμ§€λ§Œ 500 μƒνƒœ μ½”λ“œλ‘œ λ°œμƒν•˜κ²Œ λœλ‹€.

λ°”κΎΈλ €λ©΄ μ–΄λ–»κ²Œ ν•΄μ•Ό ν• κΉŒ?

μŠ€ν”„λ§ MVCλŠ” 컨트둀러 λ°–μœΌλ‘œ μ˜ˆμ™Έκ°€ μ „νŒŒλ˜λ©΄ 이λ₯Ό ν•΄κ²°ν•˜κ³  λ™μž‘μ„ μƒˆλ‘œ μ •μ˜ν•  수 μžˆλŠ” 방법을 μ œκ³΅ν•œλ‹€.

HandlerExceptionResolverλ₯Ό μ‚¬μš©ν•˜λ©΄ λœλ‹€. ExceptionResolver 라고 ν•œλ‹€.

ExceptionResolverλ₯Ό μ μš©ν•˜κΈ° μ „, μ˜ˆμ™Έκ°€ λ°œμƒν•˜λ©΄ λ‹€μŒκ³Ό 같이 λ™μž‘ν•œλ‹€.

  1. WASμ—μ„œ DispatcherServlet을 ν˜ΈμΆœν•œλ‹€.

    a. μΈν„°μ…‰ν„°μ˜ preHandle() 호좜

    b. ν•Έλ“€λŸ¬ μ–΄λŒ‘ν„° 호좜

    c. 컨트둀러 호좜 β†’ Exception λ°œμƒ

  2. μ»¨νŠΈλ‘€λŸ¬μ—μ„œ μ˜ˆμ™Έκ°€ μ „λ‹¬λ˜κ³  postHandle()이 ν˜ΈμΆœλ˜μ§€ μ•ŠλŠ”λ‹€.

  3. afterCompletion() 호좜

  4. WAS에 μ˜ˆμ™Έκ°€ μ „λ‹¬λ˜κ³  500 μƒνƒœ μ½”λ“œ λ°˜ν™˜

ExceptionResolverλ₯Ό μ μš©ν•˜λ©΄ μ μš©ν•˜κΈ° μ „μ˜ 2번 λ‹¨κ³„κΉŒμ§€λŠ” λ™μΌν•˜λ‹€.

DispatcherServlet에 μ˜ˆμ™Έκ°€ μ „λ‹¬λ˜κ³  ExceptionResolverλ₯Ό ν˜ΈμΆœν•΄μ„œ μ˜ˆμ™Έλ₯Ό ν•΄κ²°ν•˜λ €κ³  μ‹œλ„ν•œλ‹€.

  • ExceptionResolverλ₯Ό 톡해 μ˜ˆμ™Έλ₯Ό 해결해도 postHandle()은 ν˜ΈμΆœλ˜μ§€ μ•ŠλŠ”λ‹€.

HandlerExceptionResolver μΈν„°νŽ˜μ΄μŠ€

  • handler : 컨트둀러 정보

  • Exception : μ»¨νŠΈλ‘€λŸ¬μ—μ„œ λ°œμƒν•œ μ˜ˆμ™Έ

κ΅¬ν˜„μ²΄λ₯Ό λ§Œλ“€κ³  λ“±λ‘ν•˜κ²Œ 되면 response.sedError()λ₯Ό ν˜ΈμΆœν•˜λ©΄μ„œ WASλŠ” 400 μƒνƒœ μ½”λ“œλ‘œ μΈμ‹ν•˜κ²Œ λœλ‹€.

  • DispatcherServlet은 λ“±λ‘λœ ExceptionResolverλ₯Ό ν˜ΈμΆœν•˜κ²Œ λœλ‹€.

  • exception을 sendError()둜 λ³€ν™˜ ν•œ 것이닀.

[WebMvcConfigurer μˆ˜μ • - resolver 등둝] WebMvcConfigurerμ—λŠ” extendHandlerExceptionResolvers()와configureHandlerExceptionResolvers()κ°€ μ‘΄μž¬ν•˜λŠ”λ°, ν›„μžλ₯Ό μ‚¬μš©ν•˜κ²Œ 되면 μŠ€ν”„λ§μ΄ 기본으둜 μ œκ³΅ν•˜λŠ” ExceptionResolverκ°€ μ œκ±°λ˜λ―€λ‘œ μ •λ§λ‘œ 잘 μ»€μŠ€ν…€ ν•΄μ„œ μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” 것이라면, μ „μžλ₯Ό νƒν•˜λŠ” 것이 쒋을 것 κ°™λ‹€.

또, HandlerExceptionResolver의 λ°˜ν™˜ 값에 따라 λ‹€λ₯΄κ²Œ λ™μž‘ν•œλ‹€. (resolveException의 λ°˜ν™˜ κ°’)

  1. 빈 ModelAndView

    • return new ModelAndView();

    • μ΄λ ‡κ²Œ λ°˜ν™˜ν•˜λ©΄ λ·°λ₯Ό λ Œλ”λ§ ν•˜μ§€ μ•Šκ³  정상 νλ¦„μœΌλ‘œ μ„œλΈ”λ¦Ώμ΄ λ¦¬ν„΄λœλ‹€.

  2. ModelAndView μ§€μ •

    • View, Model의 정보λ₯Ό μ§€μ •ν•΄μ„œ λ°˜ν™˜ν•˜κ³ , λ·°λ₯Ό λ Œλ”λ§ ν•œλ‹€.

  3. null

    • μ§€μ •λœ λ‹€μŒ ExceptionResolverλ₯Ό μ°Ύμ•„μ„œ μ‹€ν–‰ν•œλ‹€. λ§Œμ•½ μ—†λ‹€λ©΄ μ˜ˆμ™Έ μ²˜λ¦¬κ°€ λ˜μ§€ μ•Šκ³ , 기쑴에 λ°œμƒν•œ μ˜ˆμ™Έλ₯Ό μ„œλΈ”λ¦Ώ λ°–μœΌλ‘œ μ „λ‹¬ν•˜κ²Œ λœλ‹€.

HandlerExceptionResolver ν™œμš©

  1. μ˜ˆμ™Έ μƒνƒœ μ½”λ“œ λ³€ν™˜

    • response.sendError()λ₯Ό ν˜ΈμΆœν•˜μ—¬ μƒνƒœ μ½”λ“œλ₯Ό λ³€κ²½ν•΄μ„œ μ„œλΈ”λ¦Ώμ— 전달 κ°€λŠ₯

  2. λ·° ν…œν”Œλ¦Ώ 처리

    • ModelAndView에 값을 μ±„μ›Œμ„œ μ˜ˆμ™Έμ— λ”°λ₯Έ λ·°λ₯Ό λ Œλ”λ§ ν•  수 μžˆλ‹€.

  3. API 응닡 처리

    • response.getWriter().println(”hello”)처럼 HTTP response body에 직접 데이터λ₯Ό λ„£μ–΄μ„œ JSONκ³Ό 같은 ν˜•μ‹μœΌλ‘œ 응닡할 수 μžˆλ‹€.

πŸ€” WebMvcConfigurerμ—μ„œ resolverλ₯Ό 등둝할 λ•Œ, 호좜 μˆœμ„œλŠ”?

DispatcherServlet은 μ˜ˆμ™Έ 처리기(resolver)λ₯Ό 등둝할 λ•Œ, λ“±λ‘λœ μˆœμ„œλ₯Ό μœ μ§€ν•˜κ³  κΈ°λ³Έ μˆœμ„œμ— 따라 μ •λ ¬ν•˜λ―€λ‘œ, λ¨Όμ € λ“±λ‘λœ μˆœμ„œλŒ€λ‘œ ν˜ΈμΆœν•œλ‹€.

호좜 μˆœμ„œλ₯Ό λ³€κ²½ν•˜κ³  μ‹Άλ‹€λ©΄?

DispatcherServlet은 OrderComparatorλ₯Ό μ‚¬μš©ν•΄μ„œ resolver의 μˆœμ„œλ₯Ό μ •λ ¬ν•œλ‹€.

HandlerExceptionResolver μΈν„°νŽ˜μ΄μŠ€λŠ” Ordered μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•  수 있으며 이λ₯Ό 톡해 μš°μ„ μˆœμœ„λ₯Ό μ§€μ •ν•  수 μžˆλ‹€. κ΅¬ν˜„ν•˜μ§€ μ•ŠμœΌλ©΄ κΈ°λ³Έ μš°μ„ μˆœμœ„κ°€ μ μš©λœλ‹€.

Summary

ExceptionResolverλ₯Ό μ‚¬μš©ν•˜λ©΄ μ»¨νŠΈλ‘€λŸ¬μ—μ„œ λ°œμƒν•œ μ˜ˆμ™Έλ₯Ό resolverμ—μ„œ μ²˜λ¦¬ν•œλ‹€.

κ·Έλž˜μ„œ μ˜ˆμ™Έκ°€ λ°œμƒν•΄λ„ μ„œλΈ”λ¦Ώ μ»¨ν…Œμ΄λ„ˆκΉŒμ§€ μ˜ˆμ™Έκ°€ μ „λ‹¬λ˜μ§€ μ•Šκ³  Spring MVCμ—μ„œ μ˜ˆμ™Έ μ²˜λ¦¬κ°€ λλ‚œλ‹€.

WAS μž…μž₯μ—μ„œλŠ” μ˜ˆμ™Έκ°€ 정상 처리된 것이닀. resolverλ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šκ³  μ„œλΈ”λ¦Ώ μ»¨ν…Œμ΄λ„ˆκΉŒμ§€ μ˜ˆμ™Έκ°€ μ „λ‹¬λ˜λ©΄ λ³΅μž‘ν•΄μ§€κ³  좔가적인 둜직이 μ‹€ν–‰λœλ‹€.

직접 ExceptionResolver듀을 κ΅¬ν˜„ν•˜λ €κ³  ν•˜λ©΄ 많이 λ³΅μž‘ν•΄μ§€κΈ° λ•Œλ¬Έμ—, μŠ€ν”„λ§μ΄ μ œκ³΅ν•˜λŠ” ExceptionResolver듀을 잘 ν™œμš©ν•΄μ•Ό ν•œλ‹€.

Last updated