S E P H ' S

[Spring] 7. Servlet, Servlet Container, Spring MVC 본문

Programing & Coding/Spring

[Spring] 7. Servlet, Servlet Container, Spring MVC

yoseph0310 2023. 5. 29. 16:54

지난 PSA에서 나왔던 Spring MVC에 대해 자세히 알아보는 포스트이다. 기초적인 개념으로 Servlet에 대한 개념을 알아야 Spring MVC에 대한 이해가 가능하다. 먼저 Servlet에 대한 이해를 하고 Spring MVC에 대한 내용을 다루는 것으로 하겠다.

 


Servlet

Servlet은 클라이언트 요청을 처리하고 그 결과를 다시 클라이언트에게 전송하는 Servlet 클래스의 구현 규칙을 지킨 자바 프로그램이다.

이전의 웹 프로그램들은 클라이언트 요청에 대한 응답으로 만들어진 페이지를 넘겨줬지만 현재는 동적인 페이지를 가공하기 위해서 웹 서버가 다른 곳에 도움을 요청한 후 가공된 페이지를 넘겨 주게 된다. 이때 Servlet을 사용하게 되면 웹 페이지를 동적으로 생성하여 클라이언트에게 반환해 줄 수 있다.

 

Servlet의 주요 특징

1. 클라이언트의 요청에 대해 동적으로 작동하는 웹 어플리케이션 컴포넌트
2. 기존의 정적 웹 프로그램의 문제점을 보완하여 동적인 여러 가지 기능을 제공
3. JAVA의 스레드를 이용하여 동작
4. MVC 패턴에서 컨트롤러로 이용됨
5. 컨테이너에서 실행
6. 보안 기능을 적용하기 쉬움

 

Servlet의 예시

@WebServlet(name = "helloServlet", urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {
	
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    	// 애플리케이션 로직
    }
}
  • urlPatterns("/hello") 의 URL이 호출되면 서블릿 코드가 실행된다.
  • HttpServletRequest를 통해 HTTP 요청 정보를 사용할 수 있다.
  • HttpServletResponse를 통해 HTTP 응답 정보를 사요할 수 있다.

 

Servlet의 동작 방식

Servlet 동작 방식

  • 사용자가 URL을 입력하면 요청이 서블릿 컨테이너로 전송된다.
  • 요청을 전송받은 서블릿 컨테이너는 Http Request, HttpResponse 객체를 정의한다.
  • 사용자가 요청한 URL이 어느 서블릿에 대한 요청인지 찾는다. 그림에서는 HelloServlet을 찾게 된다.
  • 서블릿의 service() 메소드를 호출한 후 클라이언트의 GET, POST 여부에 따라 doGet(), doPost() 메소드를 호출한다.
  • 동적 페이지를 생성한 후 HttpServletResponse 객체에 응답을 보낸다.
  • 클라이언트에 최종 결과를 응답한 후 HttpServletRequest, HttpServletResponse 객체를 소멸한다.

 

Servlet의 생명 주기

Servlet 생명 주기

  • 클라이언트의 요청이 들어오면 서블릿 컨테이너는 서블릿이 메모리에 있는지 확인한다. 메모리에 없다면 init() 메소드를 호출하여 적재한다.
  • 클라이언트 요청에 따라서 service() 메소드를 통해 요청에 대한 응답이 doGet(), doPost()로 분기한다.
  • 서블릿 컨테이너가 서블릿에 종료 요청을 하면 destroy() 메소드가 호출된다. 종료 시 처리해야 하는 작업은 destroy() 메소드를 오버라이딩하여 구현하면 된다. destroy() 메소드가 끝난 서블릿 인스턴스는 GC에 의해 제거된다.

 

일반 자바 객체와 차이점

JVM에서 호출 방식은 서블릿과 일반 클래스 모두 같지만, 서블릿은 main() 메소드로 직접 호출되지 않고, 웹 컨테이너(Servlet Container)에 의해 실행된다. 컨테이너가 web.xml을 읽고, 서블릿 클래스를 클래스 로더에 등록하는 절차를 밟는다.

 

 

Servlet Container

서블릿 컨테이너는 구현되어 있는 Servlet 클래스의 규칙에 맞게 서블릿 객체를 생성, 초기화, 호출, 총료하는 생명주기를 관리한다. 서블릿 컨테이너는 클라이언트의 요청을 받고 응답할 수 있도록 웹 서버와 소켓으로 통신한다.

Tomcat은 웹 애플리케이션(WAS) 중 하나로, Servlet Container 기능을 제공하고 있다. 간혹 Tomcat이 서블릿 컨테이너라고 불려지기도 하지만 엄밀히 말하자면 웹 서버 등의 부가 기능도 제공하기 때문에 WAS라고 부르는 것이 좋다.

 

특징

1. 통신지원

 

서블릿과 웹서버가 통신할 수 있는 손쉬운 방법을 제공한다. 만약 해당 유저의 이름 값을 FORM을 통해 입력받는다고 가정하면 아래와 같은 수많은 작업이 필요하다.

 

  • 서버 TCP/IP 연결 대기, 소켓 연결
  • HTTP 요청 메시지를 파싱해서 읽기
  • POST 방식, /save URL 인지
  • Content-Type 확인
  • HTTP 메시지 바디 내용 파싱
    • username, age 데이터를 사용할 수 있게 파싱
  • 저장 프로세스 실행
  • 비즈니스 로직 실행
    • 데이터베이스에 저장 요청
  • HTTP 응답 메시지 생성 시작
    • HTTP 시작 라인 생성
    • Header 생성
    • 메시지 바디에 HTML 생성에서 입력
  • TCP/IP에 응답 전달, 소켓 종료

 

FORM 인증을 하면 HTTP 메시지가 전송되는데 그것을 읽어들이기 위해 여러가지 거쳐야 하고 응답하기 위해서도 많은 과정들을 거쳐야 한다. 서블릿 컨테이너는 개발자가 비즈니스 로직에 집중할 수 있도록 해당 과정을 모두 자동으로 수행한다. 우리는 단순히 HTTP 요청 메시지로 생성된 request를 읽어 비즈니스 로직을 수행하고 response를 반환하면 된다.

 

 

2. 서블릿 생명주기 관리

 

서블릿 클래스를 로딩해 인스턴스화하고 서블릿의 초기화 메소드를 호출, 요청이 들어오면 적절한 서블릿 메소드를 호출하는 작업을 수행한다. 서블릿의 사용이 끝난 시점에는 가비지 컬렉션이 진행되어 서블릿을 제거한다.

 

 

3. 멀티 스레딩 관리

 

서블릿 컨테이너는 요청이 올 때마다 새로운 자바 스레드를 하나 생성하여 다중 처리하고, 실행이 끝나면 자동으로 종료된다.

 

 

4. 선언적 보안관리

 

보안 관련 설정을 배포 서술자라는 xml문서를 활용하여 관리하므로 개발자가 보안 설정을 바꾸더라도 자바코드에 영향이 가지 않는다.

 

 

5. JSP 지원

 

JSP 코드를 자바 코드로 변환해준다.

 

 

웹 서버 & WAS 동작 과정 개요

  • 웹 브라우저에서 웹 서버에 HTTP 요청을 보내면, 웹 서버는 받은 HTTP 요청을 WAS의 Web Server로 전달한다.
  • WAS의 웹 서버는 HTTP 요청을 서블릿 컨테이너에 전달한다.
  • 서블릿 컨테이너는 HTTP 요청 처리에 필요한 서블릿 인스턴스가 힙 메모리 영역에 있는지 확인한다. 존재하지 않으면 서블릿 인스턴스를 생성하고 해당 서블릿 인스턴스의 init() 메소드를 호출하여 서블릿 인스턴스를 초기화한다.
  • 서블릿 컨테이너는 서블릿 인스턴스의 service() 메소드를 호출하여 HTTP 요청을 처리하고 WAS의 웹 서버에게 처리 결과를 전달한다.
  • WAS의 웹 서버는 HTTP 응답을 앞 단에 위치한 웹 서버에게 전달하고 앞 단의 웹 서버는 받은 HTTP 응답을 웹 브라우저에게 전달한다.

 

Spring Container

 

Bean을 관리하기 위해 IoC(의존성 주입)을 사용하며 BeanFactory가 바로 IoC 컨테이너(= DI Container)에 해당되며, 이 BeanFactory와 여러 부가 기능들을 상속한 ApplicationContext를 우리는 주로 스프링 컨테이너라고 한다는 것을 알고 있다.

Spring MVC 역시 서블릿 컨테이너가 관리하고 있는 거대한 서블릿이라고 생각하면 된다. 그래서 서블릿 없이 Spring MVC만 있으면 된다고 하는 것은 비즈니스 로직을 Spring을 통해 처리한다는 것이지 서블릿이 필요없다는 것이 절대 아니다.

그림을 보면, 스프링 컨테이너는 서블릿 컨테이너 안에 존재하는 것을 확인할 수 있다. 즉, 스프링 컨테이너는 서블릿 컨테이너와 독립존재가 아니며, 서블릿 컨테이너가 Spring Bean에 접근하려면 스프링 컨테이너를 거쳐야 한다.

 

 

Spring MVC

Spring MVC란 프론트 컨트롤러 패턴에 기초한 웹 MVC 프레임워크이다. Spring 프레임워크의 하위 모듈이며 Model, View, Controller를 명확하게 분리하여 매우 유연하고 확장성이 좋다.

 

Spring MVC가 없던 과거에는 URL 마다 서블릿을 생성하고 web.xml로 서블릿을 관리해야 했다. URL 마다 서블릿이 필요하다 보니, 매번 인스턴스를 만들어야 하고 각 서블릿마다 공통 기능을 하는 코드의 경우 중복이 발생하여 유지보수하기 어려웠다.

 

이를 프론트 컨트롤러 패턴을 사용하여 해결하였는데 모든 요청을 프론트 컨트롤러(하나의 서블릿)에게 보내고 프론트 컨트롤러는 각 요청에 맞는 컨트롤러를 찾아 호출하는 역할을 한다. 그래서 공통 기능은 프론트 컨트롤러에서 처리하고 서로 다른 코드들만 각 컨트롤러에서 처리하도록 할 수 있다.

 

DispatcherServlet

DispatcherServlet은 표현 계층 전면에서 HTTP 프로토콜을 통해 들어오는 모든 요청을 중앙 집중식으로 처리하는 프론트 컨트롤러이다. DispatcherServlet은 Spring MVC의 핵심 요소중의 하나로 클라이언트로부터 어떤 요청이 들어오면 서블릿 컨테이너가 요청을 받는다. 이후 공통 작업은 DispatcherServlet이 처리하고 이외 작업은 적절한 세부 컨트롤러로 위임한다.

Spring MVC 동작 흐름도

  1. DispatcherServlet으로 클라이언트의 요청이 들어온다.
  2. 웹 요청을 Handler Mapping에 위임하여 해당 요청을 처리할 Handler(Controller)를 탐색한다.
  3. 찾은 Handler를 실행할 수 있는 HandlerAdapter를 탐색한다.
  4. 찾은 HandlerAdapter를 사용하여 Handler(Controller)의 메소드를 실행한다.
  5. Handler의 반환 값은 Model과 View이다.
  6. View 이름을 View Resolver에게 전달하고, View Resolver는 View 객체를 전달한다.
  7. DispatcherServlet은 View에게 Model을 전달하고 화면 표시를 요청한다. 이때, Model이 null 이면 View를 그대로 사용하고, 그렇지 않으면 View에 Model 데이터를 렌더링한다.
  8. 최종적으로 DispatcherServlet은 View결과 (HttpServletResponse) 를 클라이언트에게 반환한다.

이 동작 흐름은 @Controller 어노테이션 기준이다. @RestController의 경우 6번과 7번이 생략된다. ViewResolver를 타지 않고 반환 값에 알맞는 MessageConverter를 찾아 응답 본문을 작성하게 된다.

 

Controller 1개는 어떻게 수십 만개의 요청을 처리할까?

톰캣은 기본값으로 200개의 작업 스레드가 존재한다. 톰캣은 하나의 프로세스에서 동작하고, 내부적으로 스레드 풀을 만들어 HTTP 요청이 오면 스레드 풀에서 스레드 하나를 가져온다. 따라서 여러 요청이 오면, 각각 스레드를 생성하여 하나의 컨트롤러에 요청을 한다. 그러면 컨트롤러는 요청에 맞게 로직을 수행하여 적절한 데이터를 반환하게 된다.

이때 컨트롤러 1개가 어떻게 수십 만개, 즉 여러개의 요청을 처리하는지 궁금증이 생긴다. 동기화를 배웠다면 컨트롤러 객체에 lock을 거는 방식으로 동시성 이슈를 막아야 한다고 생각할 수 있다. 하지만 일반적으로 컨트롤러는 싱글톤 빈으로 등록되고 (컴포넌트 스캔으로 의해 싱글톤으로 생성되어 스프링 빈으로 등록된다.) 무상태, 읽기 전용 상태, 혹은 동기화 처리된 상태로 설계가 된다. 그래서 여러 스레드가 동시에 하나의 컨트롤러에 접근해도 Thread-Safe하므로 상관이 없다.

하지만 컨트롤러가 프로토타입 빈이라면 다르다. 매번 새로운 인스턴스가 생성되므로, 이 때는 요청 당 하나의 컨트롤러 객체가 대응하게된다.

 


정리

서블릿부터 Spring MVC 까지 알아봤다. 서블릿의 개념과 동작 원리, 이를 활용한 Spring MVC 의 동작 원리를 이해하는 것이 핵심이었다. 

 

 

 

출처

 

[Spring] Servlet, Servlet Container, Spring MVC 정리

spring-study에서 스터디를 진행하고 있습니다. Servlet 서블릿은 클라이언트 요청을 처리하고, 그 결과를 다시 클라이언트에게 전송하는 Servlet 클래스의 구현 규칙을 지킨 자바 프로그램이다. 이전

steady-coding.tistory.com