본문 바로가기

BOOK/토비의스프링

[2권] 토비의 스프링 7일차

HandlerExceptionResolver 전략은 예외가 발상했을때 이를 처리하는 로직을 갖고 있다.

DispatcherServlet은 등록된 HandlerExceptionResolver중에서 발생한 예외에 적합한 것을 찾아서 예외처리를 위임한다.

디폴트 전략은 AnnotationMethodHandlerExceptionResolver, ResponseStatusExceptionResolver, DefaultHandlerExceptionResolver 세 가지가 등록되어 있다.

 

 

ViewResolver

- 컨트롤러가 리턴한 뷰 이름을 참고해서 적절한 뷰 오브젝트를 찾아주는 로직을 가진 전략 오브젝트다.

 

 

 

DispatcherServlet은 간 전략의 디폴트 설정을 DispatcherServlet.properties라는 전략 설정 파일로부터 가져와서 초기화한다.

DispatcherServlet은 서블릿 컨테이너가 생성하고 관리하는 오브젝트이고 스프링의 컨텍스트에서 관리하는 빈오브젝트가 아니다.

 

 

 

DispatcherServlet을 등록할때에는 담당할 HTTP 요청의 URL 패턴을 지정해줘야 한다.

1. 확장자로 구분하는 방식.

2. URL폴더를 기준으로 매핑하는 방식.

 

이제 DispatcherServlet의 디폴트 전략과 코드를 살펴보자.

- 핸들러 어뎁터 : SimpleControllerHandlerAdapter : 서블릿과 요청과 응답 정보를 가진 HttpServletRequest와 HttpServletResponse를 그대로 전달받는다.

컨트롤러의 작업 결과는 모델과 뷰를 동시에 담을 수 있는 ModelAndView 타입 오브젝트로 리턴해주면 된다.

 

 

핸들러 매핑 : BeanNameUrlHandlerMapping

컨트롤러를 빈으로 등록할때, 빈의 이름에 매핑할 URL을 넣어준다.

이후 등록된 빈과 URL을 찾아서 일치하는 빈을 찾아 DispatcherServlet에게 전달해준다.

 

 

뷰리졸버 : InternalResourceViewResolver

컨트롤러가 리턴한 뷰 이름을 참고해서 JSP와 서블릿을 템플릿으로 사용하는 InternalResourceView를 돌려준다.

InternalResourceView는 뷰 이름에 나와 있는 hello.jsp를 실행해서 브라우저에게 돌려줄 HTML을 완성한다.

 

 

/WEB-INF/ 아래에 JSP 파일을 둔 이유 : 모델 1 방식에서처럼 사용자가 직접 URL을 이용해서 실행시키는 용도가 아니다. 따라서 실수로라도 JSP파일을 직접 실행시키지 못하도록 직접적인 접근이 불가능한 웹루트 아래에 두는 것이다.

만약 일치되는 url패턴이 존재한다면, prefix, suffiex프로퍼티를 설정해두면 편리하다.

 

 

테스트

스프링은 서블릿을 직접 테스트할 때 사용할 수 있는 각종 서블릿 기술의 오브젝트들을 손쉽게 만들 수 있는 방법을 제공해준다.  스프링 테스트 모듈에 포함된 Mock 오브젝트들을 활용하면 된다.

 

 

 

컨트롤러

1. 사용자의 요청 파악 : 클라이언트 호스트, 포트, URI, 쿼리 스트링, 폼 파라미터, 쿠키, 헤더, 세션을 비롯해서 서블릿 컨테이너가 요청 애트리뷰트로 전달해주는 것까지 다양한 정보를 참고해야 한다.

2. 사용자 요청정보 점검 : 필수값이 들어왔는지, DB에 정보를 넣을때 발생할수 있는 오류는 없는지.

3. 검증절차까지 마친 후 비로서 서비스 계층의 비즈니스 로직을 담당하는 메소드를 호출하야 요청에 따른 작업 수행. 이 과정에서 서비스가 필요로 하는 파라미터로 변경하는 절차까지.

4. 서비스 계층의 메소드가 돌려준 결과를 보고 어떤 뷰를 보여줘야 하는지 결정.

5. 뷰 선택이 끝난 후, 모델에 값을 넣어줘야 한다. 상태를 세션에 저장하고, 필요없어진 정보를 세션에서 삭제하는 과정도 컨트롤러의 역할이다.

 

-> 이런 모든 작업을 단순하게 컨트롤러 메소드 하나에 모두 담아두는건 비효율적이며 객체지향적이라고 보기도 힘들다. 애플리케이션 성격상 컨트롤러의 역할이 크다면 책임의 성격과 특징, 변경 사유등을 기준으로 세분화해줄 필요가 있다.

 

 

 

컨트롤러의 종류와 핸들러 어뎁터

1. 표준 서블릿을 사용하는 Servlet과 SimpleServletHandlerAdapter

2. Controller와 SimpleControllerHandlerAdapter : Interface Controller를 구현해서 만든다. 하지만, 컨트롤러로써 필수 기능이 구현되어 있는 AbstractController를 상속해서 컨트롤러를 만드는 게 편리하기 때문이다.

3. AnnotationMethodHandlerAdapter, AnnotationHandlerAdapter : 지원하는 컨트롤러의 타입이 정해져 있지 않다. 클래스와 메소드에 붙은 몇 가지 애노테이션의 정보와 메소드 이름, 파라미터, 리턴 타입에 대한 규칙등을 종합적으로 분석해서 컨트롤러를 선별하고 호출 방식을 결정한다.

또한, 컨트롤러가 하나 이상의 Url에 매핑될수 있다.

메서드 방식의 URL기능을 제공하므로, Annotation을 필요로 하며 한 컨트롤러 안에서 다양한 URL등을 처리할수 있다.

 

 

 

 

핸들러 매핑

HTTP 요청정보를 이용해서 이를 처리할 핸들러 오브젝트, 즉 컨트롤러를 찾아주는 기능을 가진 DispatcherServlet의 전략이다.

BeanNameUrlHandlerMapping -> 디폴러 매핑의 하나로, 빈의 이름에 들어 있는 URL을 HTTP요청의 URL과 비교해서 일치하는 빈을 찾아준다.

 

1. DefaultAnnotationHandlerMapping : @RequestMapping이라는 애노테이션을 컨트롤러 클래스나 메소드에 직접 부여하고 이를 이용해 매핑하는 전략이다.

@RequestMapping은 메소드 단위로 URL을 매핑해줄 수 있어서 컨트롤러의 개수를 획기적으로 줄일 수 있다는 장점이 있다.

또한 HTTP 메소드, 심지어는 파라미터와 HTTP 헤더 정보까지 매핑에 활용할 수 있다.

 

기타 공통 설정정보

- order : 두 개 이상의 핸들러 매핑을 적용했을때, 중복되는 URL매핑정보에 대해서 우선순위를 지정할수 있다. 이는 Ordered라는 인터페이스를 구현함으로써 가능한 결과이다.

 

 

핸들러 인터셉터

- 핸들러 맵핑의 중요한 한가지 기능은 바로 핸들러 인터셉터를 적용해주는 것이다.

핸들러 맵핑은 DispatcherServlet으로부터 매핑 작업을 요청받으면, 그 결과로 핸들러 실행 체인을 돌려준다.

이 핸들러 실행 체인은 하나 이상의 핸들러 인터셉트를 거쳐서 컨트롤러가 실행될수 있도록 구성되어 있다.

 

HandlerInterceptor

- HandlerInterceptor 인터페이스를 구현해서 만든다. 이 인터페이스 안에는 다음과 같은 세 개의 메소드가 포함되어 있다.

1) preHandler : 메소드 컨트롤러가 호출되기 전에 실행된다. 파라미터중에 handler가 존재하는데 이는 핸들러 매핑이 찾아준 컨트롤러 빈 오브젝트다.

1.1) 언제 사용하나 ? : 컨트롤러 실행 이전에 처리해야 할 작업이 있다거나, 요청정보를 가공하거나 추가하는 경우에 사용할 수 있다. true일 경우, 핸들러 실행 체인의 다음단계로 진행된다.

2)postHandler : 컨트롤러를 실행하고 난 후에 실행된다.

3)afterCompletion : 모든 뷰에서 최종 결과를 생성하는 일을 포함한 모든 작업이 다 완료된 후에 실행된다.

 

핸들러 인터셉터는 서블릿 필터와 기능이나 용도가 비슷하다.

모든 종류의 컨트롤러에 동일한 핸들러 인터셉터를 적용하려면 차라리, 핸들러 인터셉터가 더 나은 선택이다.

 

view는 MVC아키텍처에서 모델이 가진 정보를 어떻게 표현해야 하는지에 대한 로직을 갖고 있는 컴포넌트이다.