스프링은 의도적으로 서블릿 웹 앱의 컨텍스트를 두 가지로 분리해놓았다. 웹 기술에서 완전히 독립적인 비즈니스 서비스 계층과 데이터 액세스 계층을 담은 루트 앱 컨텍스트와, 스프링 웹 기술을 기반으로 동작하는 웹 관련 빈을 담은 서블릿 앱 컨텍스트다. 이렇게 스프링 컨텍스트를 두 가지로 분리해둔 이유는 스프링 웹 서블릿 컨텍스트를 통째로 다른 기술로 대체할 수 있도록 하기 위해서다. 

 

1.1 스프링에서 사용되는 웹 프레임워크의 종류

스프링 서블릿/스프링 MVC

스프링이 직접 제공하는 서블릿 기반의 MVC프레임워크다. 스프링 서블릿 또는 스프링 MVC라고 부른다. 프론트 컨트롤러 역할을 하는 DispatcherServlet을 핵심엔진으로 사용한다. 스프링 서블릿은 다양한 종류의 컨트롤러를 동시에 사용할 수 있게 설계되어 있다. 스프링답게 MVC 프레임워크의 많은 기능을 자유롭게 확장할 수 있다. 스프링 서블릿이 제공해주는 공통 서비스와 전략들을 기반으로 해서 새로운 종류의 MVC프레임워크를 만드는 일도 어렵지 않다. 애노테이션 설정과 유언한 핸들러 메소드를 지원하는 스프링 @MVC가 가장 대표적으로 사용되는 스프링 서블릿 기반의 기술이다. 

 

 스프링 서블릿의 모든 컴포넌트는 스프링의 서블릿 ac의 빈으로 등록되어 동작한다. 따라서 간단히 루트 컨텍스트에 존재하는 서비스 계층의 빈을 사용할 수 있다.  스프링 서블릿의 구성요소와 전략,특징을 살펴보자.

 

 

SOAP(Simple Object Access Protocol)은 일반적으로 널리 알려진 HTTP, HTTPS, SMTP 등을 통해 XML 기반의 메시지를 컴퓨터 네트워크 상에서 교환하는 프로토콜이다. 

 

통신 프로토콜 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 통신 프로토콜 또는 통신 규약은 컴퓨터나 원거리 통신 장비 사이에서 메시지를 주고 받는 양식과 규칙의 체계이다. 통신 프로토콜은 신호 체계, 인증, 그리고

ko.wikipedia.org

 Spring Web Service

스프링 MVC와 유사한 방식으로 SOAP 기반의 웹 서비스 개발을 가능하게 해주는 프레임워크다. 강력한 오브젝트 매핑 기능과 XML 마샬링 기능을 제공. 스프링 보안을 비롯한 각종 스프링의 기능을 활용가능. 

 

스프링 기반으로 두지 않는 웹 프레임워크

JPS/Servlet

기존에 만들어뒀떤 모델 1 방식의 JSP나 서블릿을 스프링 앱의 웹 프레젠테이션 계층으로 사용할 수 있다. JSP나 서블릿에서 newXXXHelper()와 같은 방식으로 헬퍼 오브젝트를 만들어서 DB나 비즈니스 로직을 처리하고 있었다면, 이를 스프링으로 만들어둔 서비스계층이나 데이터액세스 계층의 빈을 가져와 사용하도록 만들 수 있다.

 

 JSP/서블릿 모델 1 방식이라면 스프링은 리스너로 등록하는 루트 ac만을 등록해뒀을 것이다. 루트 ac를 JSP/서블릿 안에서 가져오려면 WebApplicationContextUtils의 스태틱 메소드를 이용하면 된다. JSP나 서블릿에서 사용할 수 있는 HttpServiceRequest 타입 오브젝트를 이용해서 ServletContext를 가져와 getWebApplicationContext() 메서드의 파라미터로 넣어주면 루트 ac를 돌려준다. 

1
2
3
4
ApplicationContext context= WebApplicationContextUtils.
        getWebApplicationContext(request.getSession().getServletContext());
 
HelloService service = context.getBean(HelloService.class);
cs

1.2 스프링 MVC와 DispatcherServlet 전략

프레임워크 기술은 2가지 방향으로 발전하고 있다. 

 하나는 스프링과 같이 유연성과 확장성에 중점을 두고 어떤 종류의 시스템 개발이나 환경, 요구조건에 잘 들어맞도록 재구성할 수 있는 범용적 프레임워크. 이런 프레임워크는 각 계층과 기술 사이의 독립성이 중요. 따라서 계층과 기술이 서로의 내부를 잘 알고 강하게 결합되는 것을 극도로 꺼린다. 모든 계층에서 공유 가능한 도메인 오브젝트 정도를 제외하면 각 계층은 정해진 인터페이스 외에는 서로 알지못한다. 각 계층을 독립적으로 개발하고 테스트할 수 있으며 한 계층의 기술이나 구현은 다른 계층의 코드에 영향을 주지 않은 채로 자유롭게 변경하거나 교체할 수 있다. 같은 계층에서도 환경에 종속적인 로우레벨의 기술과 상위 레벨의 앱 코드는 서비스 추상화와 같은 방식으로 최대한 느슨하게 연결해서 의존성이 발생하지 않도록 만든다.  유연한 아키텍쳐를 가지고 장기적으로 많은 인원이 큰 규모의 시스템을 개발할 때 적합한 프레임워크다. 

 

 스프링을 사용하는 개발자는 스프링이 제공해준 MVC 프레임워크 위에 필요한 전략을 추가해서 사용할 수 있어야 한다.

스프링 MVC프레임워크를 이미 완성된 고정적인 프레임우커로 보지말고, 진행하려는 프로젝트의 특성에 맞게 빠르고 편리한 개발이 가능하도록 자신만의 웹 프레임워크를 만드는 데 쓸 수 있는 도구라고 생각할 필요가 있다. 이것이 스프링이 제공하는 가치를 누리며 스프링을 잘 사용할 수 있는 비결이다 .

 

 일단 먼저 스프링의 웹 기술이 제공하는 유연하고 확장성 뛰어난 기반구조를 확실히 이해하고 난 후에 이를 스프링이 어떻게 확장해서 웹 프레임워크를 만들었는지 살펴보고 이를 다시 확장해서 프로젝트에 맞는 편리하고 빠른 개발이 가능한 프레임워크를 만드는 게 좋을 것이다. 

 스프링 웹 기술의 핵심이자 기반이 되는 것은 DispatcherServlet이다. 이 서블릿은 스프링의 웹 기술을 구성하는 다양한 전략을 DI로 구성해서 확장하도록 만들어진 스프링/MVC의 엔진과 같은 역할을 한다. 

 

 

DispatcherServlet과 MVC 아키텍쳐

스프링의 웹 기술은 MVC 아키텍처를 근간으로 하고 있다. MVC는 프레젠테이션 계층의 구성요소를 정보로 담은 모델(M), 화면 출력 로직을 담은 뷰(V), 그리고 제어 로직을 담은 컨트롤러(C)로 분리하고 이 세 가지 요소가 서로 협력해서 하나의 웹 요청을 처리하고 응답을 만들어내는 구조다. 

 MVC아키텍쳐는 보통 프론트 컨트롤러 패턴과 함께 사용된다. 중앙집중형 컨트롤러를 프레젠테이션 계층의 제일 앞에 둬서 서버로 들어오는 모든 요청을 먼저 받아서 처리하게 만든다. 예외 발생 시 이를 처리하는 것도 프론트 컨트롤러의 역할.

 

(1) DispatcherServlet의 HTTP 요청 접수

 자바 서버의 서블릿 컨테이너는 HTTP 프로토콜을 통해 들어오는 요청이 스프링의 DispatcherServlet에 할당된 것이라면 HTTP요청정보를 DS에 전달해준다. web.xml에는 보통 다음과같이 DS이 전달받을 URL의 패턴이 정의됨.

<servlet-mapping>

  <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>

  <url-pattern>/app/*</url-pattern>

</servlet-mapping>

 

이 서블릿-매핑은 URL이 /app로 시작하는 모든 요청을 스프링의 프론트 컨트롤러인 DispatcherServlet에게 할당해주는 것이다. 위에서처럼 특정 폴더 아래의 내용을 매핑하는 방법도 가능하고, *.do와 같이 특정 확장자만을 매핑해주는 방법도 사용가능.

 

 HTTP요청은 HTTP명령에 따라서 GET과 POST로 구분된다. 어떤 명령을 사용하든 HTTP요청에는 해당 요청을 통해 사용자가 전달하려고 하는 정보가 전송된다. 이 정보는 URL의 "?"뒤에 이어지는 쿼리 스트링이나 폼 파라미터, 쿠키나 헤더 정보를 통해 전달될 수 있다. 

 

 DispatcherServlet은 모든 요청에 대해 공통적으로 진행해야 하는 전처리 작업이 등록된 것이 있다면 이를 먼저 수행한다. 공통적으로 이용가능한 보안이나 파라미터 조작, 한글 디코딩과 같은작업이 적용된다.

 

==========타이핑하면서 책보기가 힘들어서 글이 잘 정리된 

https://dhsim86.github.io/web/2018/12/15/toby_spring_12_web-post.html

님의 블로그를 참조하며 추가로 타이핑했다.

 

(2)DispatcherServlet에서 컨트롤러로 HTTP 요청 위임

  • DispatcherServlet은 URL이나 파라미터 정보, HTTP 메소드를 참고하여 어떤 컨트롤러로 작업을 위임할지 결정한다. 컨트롤러를 설정하는 것은 DispatcherServlet의 핸들러 매핑 전략을 이용한다. 사용자 요청 기준으로 어떤 핸들러에게 작업을 위임할지를 결정해주는 것을 핸들러 매핑 전략이라고 한다.
  • DispatcherServlet의 핸들러 매핑 전략은 DispatcherServlet 수정없이 DI를 통해 언제든지 확장이 가능하다. 어떤 URL이 들어오면 어떤 컨트롤러가 이를 처리할지를 매핑해주는 전략을 만들어서 제공해주면 된다.
  • 어떤 컨트롤러가 처리할지를 결정했다면, 해당 컨트롤러 오브젝트의 메소드를 호출하여 실제로 웹 요청을 위임한다. 그런데 실제로 실행하려면 컨트롤러 메소드를 어떻게 호출할지를 알아야 한다. 컨트롤러는 특정 인터페이스를 구현한다는 식의 제약없이 어떤 종류의 형태로도 정의할 수 있다.
  • DispatcherServlet은 각기 다른 메소드와 포맷을 가진 컨트롤러 오브젝트를 사용하기 위해 어댑터를 사용한다. 특정 컨트롤러를 호출할 때는 해당 컨트롤러 타입을 지원하는 어댑터를 중간에 끼워넣어 호출하는 것이다. 따라서 DispatcherServlet 입장에서는 항상 일정한 방식으로 컨트롤러를 호출하고 결과를 받을 수 있다.
  • DispatcherServlet은 컨트롤러가 어떠한 메소드를 가졌으며 어떤 인터페이스를 구현했는지 전혀 알지 못한다. 다만 컨트롤러에 따른 적절한 어댑터만을 사용할 뿐이다.
  • 각 어댑터는 자신이 담당하는 컨트롤러에 맞게 호출 방법을 이용하여 컨트롤러에 작업 요청을 보내고, 결과를 받아 다시 DispacherServlet으로 돌려준다. 스프링 서블릿 / MVC 확장 구조의 기본은 이 어댑터를 통한 컨트롤러 호출 방식이다.
  • DispatcherServlet이 어떤 어댑터를 사용할지는 핸들러 어댑터 전략을 통해 결정한다.
  • DispatcherServlet은 핸들러 어댑터로 웹 요청을 전달할 때는 모든 웹 요청 정보가 담긴 HttpServletRequest 타입의 오브젝트를 전달한다. 어댑터는 이를 받아 컨트롤러의 메소드가 받을 수 있는 파라미터로 변환해서 전달해준다.
  • DispatcherServlet은 HttpServletResponse도 같이 전달한다. 이는 컨트롤러에서 HttpServletResponse 오브젝트안에 직접 결과를 넣어줄 수 있기 때문이다.

 

(3) 컨트롤러의 모델 생성과 정보 등록

  • MVC 패턴의 장점은 정보를 담는 모델과 정보를 어떻게 뿌려줄지를 알고 있는 뷰가 분리된다는 점이다.
  • 컨트롤러의 작업은 먼저 사용자 요청을 해석한 후, 비즈니스 로직을 수행하도록 서비스 계층의 오브젝트로 작업을 위임, 결과를 받아 모델을 생성 후 어떤 뷰를 사용할지를 결정하는 네 가지로 분류할 수 있다.
  • 모델을 생성하고 모델에 정보를 넣어주는 것이 컨트롤러가 해야 할 마지막 중요한 두 가지 작업 중의 하나이다. 컨트롤러에서 DispatcherServlet으로 결과를 돌려주는 두 가지 정보가 모델과 뷰이다.

(4) 컨트롤러의 결과 리턴: 모델과 뷰

  • MVC의 모든 요소랑 마찬가지로 뷰도 하나의 오브젝트인데, 컨트롤러가 뷰 오브젝트를 직접 리턴할 수도 있지만 보통은 뷰의 논리적인 이름을 리턴하여 DispatcherServlet의 전략인 뷰 리졸버가 이를 이용해 뷰 오브젝트를 생성하도록 한다. 대표적으로 사용되는 뷰는 JSP/JSTL 뷰다.
  • 컨트롤러가 리턴하는 정보는 모델과 뷰로, 이를 표현하는 ModelAndView라는 이름의 오브젝트가 있다. 이 오브젝트가 DispatcherSevlet이 어댑터를 통해 컨트롤러로부터 돌려받는 오브젝트이다. 모델과 뷰를 넘기는 것으로 컨트롤러의 책임은 끝.

(5) DispatcherServlet의 뷰 호출과 (6) 모델 참조

  • DispatcherServlet은 컨트롤러로부터 모델과 뷰를 전달받은 후, 뷰 오브젝트에게 모델을 전달하여 최종 결과물을 생성해달라고 요청한다.
  • 뷰 오브젝트는 모델을 받아 적절한 최종 결과물을 생성한다. 보통 브라우저를 통해 사용자가 결과를 볼 테니 브라우저에서 나타날 HTML을 생성하는 일이 가장 흔한 뷰의 작업. 
  • 최종 결과물은 HttpServletResponse 오브젝트에 담긴다.

(7) HTTP 응답 돌려주기

  • 뷰 생성까지 모든 작업을 마친 후, DispatcherServlet은 등록된 후처리기가 있는지 확인 후 있다면 후처리기에서 후속 작업을 진행 후 뷰가 만들어준 HttpServletResponse에 담긴 결과를 서블릿 컨테이너로 돌려준다.
  • 서블릿 컨테이너는 HttpServletResponse에 담긴 정보를 HTTP 응답으로 만들어 사용자의 브라우저나 클라이언트로 전송하고 작업을 종료한다.

간단 요약.

1.HTTP요청 

2.컨트롤러에게 어댑터를 이용해서 HTTPservletrequst타입의 오브젝트를 전달. response도 함께 전달.

3. 사용자 요청 해석하고, 비즈니스로직수행하도록 서비스계층 오브젝트에 작업위임, 그리고 결과 받아서 모델 생성, 마지막으로 어떤 뷰를 사용할지 결정. 그리고 모델과 뷰 리턴.

4.뷰의 논리적인 이름 리턴해주면 뷰 리졸버가 이를 이용해 뷰 오브젝트생성하고 모델참조해 최종 결과물을 생성.

최종 결과물은 HTTpServletResponse 오브젝트안에 담긴다.

5. HTTP 응답 돌려주기. 최종결과를 서블릿 컨테이너에게 돌려주고 response에 담긴 정보를 HTTP응답으로 만들어 사용자 브라우저나 클라이언트에 전송하고 작업을 종료.

 

DispacherServlet의 DI 가능한 전략

DispatcherServlet은 DI로 확장 가능한 여러 전략들이 있다. 스프링 MVC는 자주 사용되는 전략을 디폴트로 설정해주고 있다. 따라서 필요한 전략만 확장해서 사용하고 나머지는 디폴트 전략을 사용해도 된다.

다음 전략들은 DispatcherServlet의 동작 방식을 확장하는 확장 포인트라고 할 수 있다. DispatcherServlet은 서블릿 컨테이너가 생성하고 관리하는 오브젝트이지, 스프링 컨텍스트에서 관리하는 빈 오브젝트가 아니다. 하지만 DispatcherServlet은 내부에 서블릿 웹 애플리케이션 컨텍스트를 가지고 있고 내부 컨텍스트로부터 전략이 담긴 빈 오브젝트를 찾아 사용한다.

DispatcherServlet에 적용할 전략을 선택하고, 필요에 따라 확장하거나 다른 방식으로 사용하는 것이 스프링 MVC를 바로 사용하는 첫 걸음이다.

 

HandlerMapping

 

URL과 요청 정보를 기준으로 어떤 핸들러 오브젝트, 즉 컨트롤러를 사용할 것인지를 결정하는 로직을 담당한다. HandlerMapping 인터페이스를 구현해서 만들 수도 있다.

DispatcherServlet은 하나 이상의 핸들러 매핑을 가질 수 있다. 디폴트는 BeanNameUrlHandlerMapping DefaultAnnotationHandlerMapping 두 가지가 설정되어 있다.

  • DefaultAnnotationHandlerMapping: @RequestMapping이라는 애노테이션을 컨트롤러 클래스나 메소드에 직접 부여하고 이를 이용해 매핑하는 전략이다. 스프링 3.1부터 deprecated되고, 대신에 RequestMappingHandlerMapping이 추가되었다.

RequestMappingHandlerMapping은 웹 요청을 핸들러 오브젝트가 아닌 핸들러 메소드(HandlerMethod)로 매핑한다.

 

HandlerAdapter

 

핸들러 매핑으로 선택한 컨트롤러 / 핸들러를 DispatcherServlet이 호출할 때 사용하는 어댑터이다. 컨트롤러 타입에는 제한이 없으며, 호출 방식은 타입에 따라 다르기 때문에 컨트롤러를 결정한다고 해서 DispatcherServlet이 바로 호출할 수 없다. 따라서 컨트롤러 타입을 지원하는 HandlerAdapter가 필요하다.

D

 

HandlerExceptionResolver

public interface HandlerExceptionResolver { @Nullable ModelAndView resolveException( HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex); }

예외가 발생했을 때, 이를 처리하는 로직을 가진다. 예외가 발생했을 때, 예외의 종류에 따라 에러 페이지를 표시한다던가 관리자에게 통보하는 작업은 개별 컨트롤러가 아닌 프론트 컨트롤러인 DispatcherServlet을 통해 처리되어야 한다.

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

디폴트는 AnnotationMethodHandlerExceptionResolver, ResponseStatusExceptionResolver, DefaultHandlerExceptionResolver 세 가지가 등록되어 있다. 단 3.1부터는 AnnotationMethodHandlerExceptionResolver 대신, ExceptionHandlerExceptionResolver를 사용한다.

 

ViewResolver

 

뷰 리졸버는 컨트롤러가 리턴한 뷰 이름을 참고해서 적절한 뷰 오브젝트를 찾는 로직을 가진다. 스프링이 지원하는 뷰의 종류는 다양하므로, 뷰의 종류에 따라 적절한 뷰 리졸버를 추가로 설정하면 된다.

컨트롤러가 작업을 마친 후 뷰 정보를 ModelAndView 타입 오브젝트를 DispatcherServlet으로 돌려주는데, 뷰 이름을 넣어주거나 View 타입의 오브젝트를 돌려주는 방법이 있다. 뷰 이름을 넣어주는 경우, 위의 뷰 리졸버가 필요하다.

 

LocaleResolver

지역 정보를 결정해주는 전략이다. 디폴트인 AcceptHeaderLocaleResolver는 HTTP 헤더의 정보를 보고 지역정보를 설정한다. 헤더말고도 세션이나 URL 파라미터, 쿠키 정보를 고려하도록 다른 전략을 통해 결정하게 할 수 있다.

 

ThemeResolver

테마를 가지고 이를 변경해서 사이트를 구성할 경우, 쓸 수 있는 테마 정보를 결정해주는 전략이다.

 

RequestToViewNameTranslator

컨트롤러에서 뷰 이름이나 뷰 오브젝트를 지정해주지 않았을 경우, URL과 같은 요청정보를 참고하여 자동으로 뷰 이름을 생성해주는 전략이다. 디폴트는 DefaultRequestToViewNameTranslator이다.

 

결론

DispatcherServlet을 프론트 컨트롤러로 사용하는 스프링 MVC의 가장 큰 특징은 매우 유연한 컨트롤러 호출 방식을 사용한다는 것이다. 컨트롤러 종류에 제약을 받지 않고, 적절한 어댑터만 제공해준다면 다양한 종류의 컨트롤러를 사용할 수 있다.

 

 

 

 

 

 

+ Recent posts