2020년 4월 19일 일요일

예외처리 핸들러 테스트

예외처리 핸들러 테스트

  • 토이프로젝트의 예외처리를 어떻게 할까 고민하다 검색과 백기선님 웹MVC강의에 예외처리 부분을 복습하였다. (회사소스에 존재하는 ExceptionHandler에는 로그만 남기도록 되어있었다.)
  • MVC에서 요청을 처리하다가 에러가 발생하거나 자바에서 지원하는 예외가 발생했을 때 정의한 핸들러로 그 예외를 어떻게 처리할지?, 어떤응답을 만들지를 정의할 수 있다.
      @ExceptionHandler
      public String RuntimeExceptionHandler(RuntimeException e, Model model){
        model.addAttribute("message", "runtime error");
        return "error"
      }
    
    • 예외처리를 핸들러를 전역 컨트롤러에 선언할 수 있다. (모든 컨트롤러에 적용됨)
      // 모든 컨트롤러 적용
      @ControllerAdvice("com.edu.controller")
      public class CommonExceptionHandler {
        ...
      }
    
    
      // 다수의 특정 컨트롤러 지정 가능
      @ControllerAdvice(assignableTypes = {EventController.class, EventApi.class})
      public class CommonExceptionHandler {
        ...
      }
    
    
      // 패키지 지정 가능
      @ControllerAdvice("com.edu.controller")
      public class CommonExceptionHandler {
        ...
      }
    
  • @RestControllerAdvice 는 @ResponseBody 가 붙은거 라고 생각하면될듯!

내가 테스트해본 것

아쉬운 점 : 그냥 잘못된 값을 던지고 해당 메서드에서 exception이 발생하였을 때 처리하는 테스트를 하였는데, 테스트 시 예외를 강제로 줘서 테스트를 하고 싶었는데 못하였음 ㅠㅠ(의미가 없으려나??)
  • CommonExceptionHandler.java
@ControllerAdvice
public class CommonExceptionHandler {
    private static final Logger logger = LoggerFactory.getLogger(CommonExceptionHandler.class);

    public static final String DEFAULT_ERROR_VIEW = "exception";

    @ExceptionHandler(value = RuntimeException.class)
    public ModelAndView defaultRuntimeErrorHandler(HttpServletRequest req, RuntimeException e) {
        logger.debug("RuntimeException: " + e);
        logger.debug("url: " + req.getRequestURL());

        ModelAndView mav = new ModelAndView();
        mav.addObject("exception", e);
        mav.addObject("url", req.getRequestURL());
        mav.setViewName(DEFAULT_ERROR_VIEW);

        return mav;

    }
}
  • LectureControllerTest.java
public class LectureControllerTest extends BaseControllerTest {

/* 전역 Exception Handler 설정
CommonExceptionHandler 에서 처리하기 위한 셋팅: standaloneSetup, setControllerAdvice 지정

지정 파라미터를 다른 값으로 변경하면 CommonExceptionHandler에 안들어온다.
하지만 아예 이 부분을 제거해도 CommonExceptionHandler에 옴.
 */
    @Before
    public void setUp() {
        this.mockMvc = MockMvcBuilders
                .standaloneSetup(lectureController)
                .setControllerAdvice(new CommonExceptionHandler())
                .build();
    }

  ...


    @Test
    @Description("Exception 발생 테스트")
    public void exceptionTest() throws Exception{

        // Given
        String userId = "admin2";

        // When (실패함. 어떻게 사용하는 지 더 찾아볼 것)
        //when(lectureController.checkedLecture(anyString(),anyString(),anyString())).thenThrow(new Exception("테스트입니다."));

        // Then
        mockMvc.perform(post("/lecture/checkedLecture")
                .param("userId", userId)

        ).andDo(print())
                .andExpect(status().is2xxSuccessful())
                .andExpect(view().name("exception"))
                .andExpect(model().attributeExists("exception"))
        ;

    }
}

Error, Exception 참고

  • Error : 개발자가 미리 예측하여 처리할 수 없는 경우 (JVM 자원 부족 등의 문제?)
    • 관례적으로 Error에 대해서는 Custom Class를 만들지 않는다
  • Exception : 개발자가 구현한 로직에서 발생하기 때문에 미리 예측하여 처리할 수 있음
    • RuntimeException
      • 처리를 강제하지 않음(동작하다 exception 발생)
      • 하위 Exception: NullPoint, IllegalArgument, IndexOutOfBound, System)
      • Custom UnChecked Exception 을 만들 경우 RuntimeException 클래스를 상속하여 만든다.
    • CheckedException
      • 반드시 예외처리를 해야함(처리안하면 컴파일 오류)
      • RuntimeException을 제외한 모든 클래스
      • Custom checked Exception을 만들 경우 Exception 클래스를 상속하여 만든다.
  • 예외처리 팁
    • 아무것도안하는 에러처리는 피하기 : catch 영역에 아무 동작도 안하면 예외 발생 시 추적하기 힘들어짐. 처리를 못한다면 로그라도 남길 것
    • exception.printStackTrace()는 쓰지 말 것 : 에러가 발생한 원인에 대한 Stack Trace를 추적하여 개발자에게 디버깅할 수 있는 힌트를 제공해주지만, 운영 시 성능을 생각한다면 지양해야한다. java reflection을 사용하여 trace를 추적하기 때문에 오버헤드가 발생한다고한다.
    • 반복문 내에서 Checked Exception에 대한 처리는 지양할 것 : 반복문 내에서 Checked Exception에 대한 예외처리 구문이 들어가게 되면 성능은 2배 3배 떨어지게 된다.