Spring boot/Spring 개념

Servlet과 Front Controller

eunnnn 2023. 5. 1. 15:54

Servlet

서블릿이란 Dynamic Web Page를 만들 때 사용되는 자바 기반의 웹 애플리케이션 프로그래밍 기술을 말한다.

쉽게 말해 클라이언트의 요청에 맞춰 동적인 결과를 만들어 주는 자바 웹 프로그래밍 기술이라고 할 수 있는 것인데, WAS내의 서블릿 컨테이너에서 동작하며, 요청(Request)을 받으면 요청에 맞는 로직을 실행하고 클라이언트에게 HTTP 형식으로 응답(Response)하게 된다. 

 

클라이언트가 웹 서버에 요청하면 웹 서버는 그 요청을 톰캣과 같은 WAS에 위임한다. 그러면 WAS는 각 요청에 해당하는 서블릿을 실행한다. 그리고 서블릿은 요청에 대한 기능을 수행한 후 결과를 반환하여 클라이언트에 전송한다.

1. 클라이언트 요청
2. HttpServletRequest, HttpServletResponse 객체 생성
3. Web.xml이 어느 서블릿에 대해 요청한 것인지 탐색
4. 해당하는 서블릿에서 service() 메소드 호출 
5. doGet() 또는 doPost() 호출 
6. 동적 페이지 생성 후 ServletResponse 객체에 응답 전송
7. HttpServletRequest, HttpServletResponse 객체 소멸

※ web.xml : URL 경로와 해당 경로의 요청을 처리하는 서블릿 사이의 매핑을 정의

 

Servlet Container

 

 

 

서블릿 컨테이너는 서블릿을 담고 관리해주는 컨테이너이다. 서블릿 컨테이너는 구현되어 있는 서블릿 클래스의 규칙에 맞게 서블릿을 관리하며 클라이언트의 요청을 받으면 HttpServletRequest와 HttpServletResponse 객체를 생성하여 post, get 여부에 따라 동적인 페이지를 생성하여 응답한다.

 

서블릿 컨테이너는 다음과 같은 기능을 한다.

1. 서블릿 생명주기 관리

서블릿 컨테이너는 서블릿의 탄생과 죽음을 관리한다. 서블릿 클래스를 로딩하여 인스턴스화하고, 초기화 메서드를 호출하고, 요청이 들어오면 적절한 서블릿 메서드를 찾아서 동작한다. 또한 서블릿의 생명이 다하면 가비지 컬렉션(Garbage Collection)을 통해 메모리에서 제거한다.

2. 통신 지원

서블릿 컨테이너는 웹 서버와 소켓을 만들어서 클라이언트의 요청을 받고 응답할 수 있는 통신을 지원해준다. 통신을 하기 위한 listen, accept 등의 과정을 API로 제공하여 복잡한 과정을 생략해주기 때문에 개발자가 비즈니스 로직 개발에 집중할 수 있게 도와준다. 

3. 멀티쓰레드 지원 및 관리

서블릿 컨테이너는 클라이언트의 요청을 받을 때마다 새로운 자바 스레드를 생성한다. 따라서 동시에 여러 요청이 들어와도 멀티쓰레딩 환경에서 동시다발적인 작업을 관리할 수 있다.

4. 선언적인 보안 관리

서블릿 컨테이너는 보안 관련 기능을 제공하기 때문에 개발자는 서블릿에 보안 관련 메서드를 구현하지 않아도 된다. 

 

Front Controller

Front Controller 패턴이란 REST API를 구현할 때, 한개의 URL에 하나의 서블릿을 대응해야만 했던 구조에서 코드의 중복을 줄이기 위해 고안된 패턴이다.

따라서 프론트 컨트롤러를 여러 서블릿 컨트롤러들의 앞단에 두어 일종의 ‘수문장’ 역할을 하게 둔다.

프론트 컨트롤러 도입 전
프론트 컨트롤러 도입

위와 같이 프론트 컨트롤러 서블릿 하나에 클라이언트의 모든 요청이 들어오고, 공통 작업을 처리한 후 필요한 요청을 수행할 컨트롤러를 찾아서 호출하는 방식으로 동작한다.

  1. 프론트 컨트롤러에 요청이 온다.
    → 프론트 컨트롤러는 HttpServlet을 상속 받는다.
  2. 프론트 컨트롤러는 컨트롤러들의 URL 관련 정보들을 갖고 있는데 여기서 조회를 한다.
    → interface에 컨트롤러들을 Map으로 갖고 있다.
  3. 매핑된 컨트롤러를 호출한다.
  4. 매핑된 컨트롤러는 공통 이외의 본인의 역할을 수행한다.

 

DispatcherServlet

DispatcherServlet은 Spring에 HTTP 프로토콜로 들어오는 모든 요청을 가장 먼저 받아 적합한 컨트롤러에 위임해주는 프론트 컨트롤러(Front Controller)이다. DispatcherServlet은 클라이언트의 요청을 먼저 받아 필요한 처리를 한 뒤, 개발자가 구현한 요청에 맞는 핸들러에게 요청을 Dispatch하고 해당 핸들러의 실행 결과를 Response형태로 만드는 역할을 한다.

DispatcherServlet이 등장하기 전에는 모든 서블릿을 url 매핑을 위해 web.xml에 등록해주어야 했지만, DispatcherServlet이 해당 어플리케이션으로 들어오는 모든 요청을 핸들링해주고 공통 작업을 처리하면서 개발자가 컨트롤러를 구현 해두기만 하면 DispatcherServlet이 알아서 적합한 컨트롤러로 위임을 해주는 구조가 되었다.

 

DispatcherServlet의 동작 과정

DispatcherServlet의 동작 과정은 다음과 같다.

1. 클라이언트의 요청을 디스패처 서블릿이 받음

서블릿 컨텍스트(웹 컨텍스트)에서 필터들을 지나 스프링 컨텍스트에서 디스패처 서블릿이 가장 먼저 요청을 받게된다.

 

2. 요청 정보를 통해 요청을 위임할 컨트롤러를 찾음

디스패처 서블릿은 요청 정보를 바탕으로, 요청을 처리할 컨트롤러를 찾고 해당 메소드를 호출해야 한다.

RequestMappingHandlerMapping 객체는 내부에서 HashMap으로 (요청 정보, 처리 대상)을 관리하고 있어서, 요청 정보를 Key로 사용하여 요청을 처리할 대상을 찾아온다.

이때 요청을 처리할 대상은 컨트롤러가 아닌 컨트롤러를 가지고 있는 HandlerMethod 객체다. HandlerMethod를 갖는 이유는 컨트롤러와 컨트롤러의 메소드 등을 포함해 부가적인 정보들이 더욱 필요하기 때문이다.그리고 찾아온 HandlerMethod를 HandlerMethodExecutionChain으로 감싸서 반환화는데, 그 이유는 컨트롤러로 요청을 넘겨주기 전에 처리해야 하는 인터셉터 등을 포함하기 위해서다.

 

3. 요청을 컨트롤러로 위임할 핸들러 어댑터를 찾아서 전달함

디스패처 서블릿은 컨트롤러로 요청을 직접 위임하는 것이 아니라 HandlerAdapter를 통해 어댑터 패턴을 적용해 컨트롤러로 요청을 위임한다. 

이때 어댑터 인터페이스를 통해 컨트롤러를 호출하는 이유는 컨트롤러의 구현 방식이 다양하기 때문입니다. 다양한 핸들러의 return 타입은 String, ModelAndView 등 다양하며, Parameter로 들어오는 값도 HttpServletRequest로 받거나 HttpServletResponse로 받는 등 다양한 형식으로 받을 수 있다. 이처럼 다양한 핸들러 구현 방식에 맞춰 개발을 하는 것은 쉽지 않다.따라서 이러한 요구 사항을 충족하기 위해 중간에 Adapter를 두는 어댑터 패턴을 사용하는 것이다.

 

4. 핸들러 어댑터가 컨트롤러로 요청을 위임함

핸들러 어댑터가 컨트롤러로 요청을 넘기기 전에 공통적인 전처리 과정이 필요하다. 요청에 매칭되는 인터셉터들도 실행을 시키고, @RequestParam, @RequestBody 등으로 파라미터를 준비하는 ArgumentResolver도 실행하는 등의 다양한 공통 작업들이 수행됩니다. 이러한 전처리 작업들이 완료되면 파라미터 값들과 함께 컨트롤러로 요청을 위임한다.

 

5. 비지니스 로직을 처리함

이후에 컨트롤러는 서비스를 호출하고 우리가 작성한 비지니스 로직들이 진행된다.

 

6. 컨트롤러가 반환값을 반환함

비지니스 로직이 처리된 후에는 컨트롤러가 반환값을 반환한다. 응답 데이터를 사용하는 경우에는 주로 ResponseEntity를 반환하게 되고, 응답 페이지를 보여주는 경우라면 String으로 View의 이름을 반환할 수도 있다.

 

7. 핸들러 어댑터가 반환값을 처리함

핸들러 어댑터는 컨트롤러로부터 받은 반환값을 응답 처리기인 ReturnValueHandler가 후처리한 후에 디스패처 서블릿으로 돌려준다. 만약 컨트롤러가 ResponseEntity를 반환하면 HttpEntityMethodProcessor가 MessageConverter를 사용해 응답 객체를 직렬화하고 응답 상태(HttpStatus)를 설정한다. 만약 컨트롤러가 View 이름을 반환하면 View를 반환하기 위한 준비 작업을 처리합니다.

 

8. 서버의 응답을 클라이언트로 반환함

디스패처 서블릿을 통해 반환되는 응답은 다시 필터들을 거쳐 클라이언트에게 반환된다. 이때 응답이 데이터라면 그대로 반환되지만, 응답이 화면이라면 View의 이름에 맞는 View를 찾아서 반환해주는 ViewResolver가 적절한 화면을 내려준다.

 

 

출처

더보기