본문 바로가기

서버 심화 스터디

서블릿 컨테이너

<서블릿(Servlet)이란?>

서블릿은 자바 언어로 작성된 서버 측 웹 프로그램으로, 클라이언트의 HTTP 요청을 받아 처리하고
그에 대한 응답(HTML, JSON 등)을 생성 해서 보내는 역할을 한다.

-> 사용자가 웹사이트에서 어떤 요청(예: 로그인, 검색 등)을 보냈을 때 그 요청을 받아 처리하고, 그에 맞는 결과를 만들어서 응답해주는 프로그램

 

<HTTP 요청과 응답>

 

<서블릿이 왜 필요할까????>

1.  문자열을 일일이 처리하는 건 비효율적
 -만약 서블릿 없이 개발은 하다면 요청 메세지를 직접 문자열로 파싱해서 -> GET인지 POST인지, 어떤 데이터가 들어있는지 직접 분석을 해야됨 + 응답도 직접 문자열로 만들어서 -> 헤더까지 다 손수 작성해야함

2. 서블릿은 이런 과정을 자동으로 도와주는 도구
-요청이 들어왔을 때 : 
      -요청 메시지는 자동으로 HttpServletRequest 객체로 제공되고
      -응답을 만들 때는 HttpServletResponse 객체를 이용해 쉽게 작성 가능

3. 개발자는 비즈니스 로직에만 집중하면 됨
-서블릿이 HTTP 메시지를 파싱하고 포맷팅하는 번거로운 작업은 다 해주기 때

 

< 서블릿 없이 Request/Response를 처리하는 경우>

1. 소켓 열기 - 포트 80 또는 8080 같은 포트를 열고 클라이언트 연결 기다림

2. 요청 수신(HTTP 요청 메시지 받기)

    -텍스트 형태의 요청 메시지를 문자열로 받음

3. 직접 파싱

    -위 문자열을 분석해서 어떤 URL인지, GET인지 POST인지, 어떤 파라미터인지 추출

4. 비즈니스 로직 처리

5. HTTP 응답 메시지 작성

6. 응답전송

    -소켓을 통해 클라이언트로 위 메시지를 다시 보냄

--> 개발자가 모든 요청/응답을 문자열로 처리해야 하니 매우 귀찮고, 실수도 많고, 시간도 오래 걸림

<서블릿을 사용하여 Request/Response를 처리하는 경우>

서블릿 컨테이너(Tomcat 등)가 대부분의 과정을 처리

1. 사용자가 브라우저에 요청을 보냄

2. 웹서버가 요청을 받아 서블릿 컨테이너에 전달

3. 서블릿 컨테이너가 요청 메시지를 자동으로 파싱해서 HttpServletRequest 객체 생성

4. 서블릿 개발자는 그냥 이 객체이서 필요한 정보만 꺼냄

    String id = request.getParameter("id");

5. 처리 결과를 HttpServletResponse 객체에 작성하면 알아서 응답 포맷팅:

    response.setContentType("text/html");
    response.getWriter().write("<h1>Hello</h1>");

6. 나머지 소켓 통신, 응답 전송 등은 컨테이너가 다 처리

 

항목 직접 구현 (서블릿 없음) 서블릿 사용 시 Spring Boot 사용
요청 수신 소켓 직접 열고 수동 수신 컨테이너가 자동 수신 내장 톰캣이 자동 수신
요청 파싱 문자열 직접 분석  HttpServletRequest 객체로 제공 자동 파라미터 바인딩
@RequestParam, @PathVariable
응답 작성 직접 문자열로 작성 HttpServletResponse 객체에 작성 Java 객체 리턴 -> 자동 JSON 변환(@RestController)
응답 전송 소켓에 직접 씀 컨테이너가 자동 전송 Spring이 자동 전송 및 상태코드 처리
개발자가 집중할 부분 전 과정 비즈니스 로직에만 집중 가능 비즈니스 로직에만 집중 가능

 


<서블릿 컨테이너란?> 

-서블릿 (Servlet) 은 혼자서 실행되지 않으며, 반드시 서블릿 컨테이너(Servlet Container) 내에서 실행되어야 한다.

서블릿 컨테이너는 서블릿의 생명주기 관리, 요청 처리, 멀티스레드 관리, 보안 설정 등을 담당하며, 대표적으로 Tomcat, Jetty 같은 서버가 해당 역할을 수행한다.

 

 

<1> 웹 서버와의 통신 지원

서블릿은 혼자서 네트워크로부터 요청을 직접 받을 수 없다 --> 웹서버와 협력해야된다.

1. 사용자가 브라우저에 요청 : GET/ longin HTTP /1.1
2. 요청은 웹서버(ex. Tomcat)에 도착
3. 웹 서버는 이 요청을 서블릿컨테이너에게 넘김
4. 서블릿 컨테이너는 이 요청에 대응되는 서블릿을 찾아서 호출
5. 서블릿이 응답 데이터 생성 -> 서블릿 컨테이너가 웹서버로 전달
6. 웹서버가 사용자(브라우저)에게 응답 전송

 

<2> 서블릿 생명주기 관리

서블릿이 "언제 생성되고", "언제 초기화되고", "언제 제거되는지"를 자동으로 관리해줘야 안정적인 동작이 가능

[서버 시작]
        |
서블릿 클래스 로딩
        |
서블랫 객체 생성
        |
init()호출 -> 초기설정
        |
[요청 발생 시]
        |
service() / doGet() / doPost() 등 호출
        |
[서버 종료 시]
        |
destory()호출 -> 자원정리

 

<3> 멀티스레드 지원 및 관리

웹 서버는 동시에 수많은 사용자의 요청을 처리해야 하니까 각 요청을 동시에 처리할 수 있어야 한다.

<작동방식>
1. 사용자가 여러 명 동시에 요청
2. 컨테이너는 요청당 스레드(Thread)를 하나씩 생성하거나 재사용
3. 이 스레드들이 하나의 서블릿 인스턴스를 공유하며 각각의 요청을 처리
4. 요청 처리 후 스레드는 풀에 반납

<스레드 풀??>
스레드를 매번 새로 만들지 않고 미리 만들어둔 스레드를 재사용 -> 성능 향상, 자원 절약

<주의점>
서블릿 인스턴스는 하나 -> 여러 스레드가 동시에 접근 -> 공유자원(ex. 인스턴스 변수)은 스레드 간 충돌 가능 -> 
synchronized등으로 동기화 필요

 

<서블릿 컨테이너 동작과정> 

1. 클라이언트 요청 브라우저에서 HTTP 요청 전송
2. 요청/응답 객체 생성 컨테이너가 HttpServletRequest, HttpServletResponse 생성
3. 서블릿 매핑 URL에 따라 어떤 서블릿이 처리할지 결정
4. service() 호출 요청 타입에 따라 적절한 메서드 선택
5. doGet()/doPost() 처리 비즈니스 로직 수행, 응답 생성
6. 응답 전송 클라이언트에게 최종 결과 전송

 

서블릿 컨테이너가 모든 저수준 작업(요청 파싱, 응답 전송 등)을 처리해주기 때문에
→ 개발자는 doGet()이나 doPost()에서 로직에만 집중할 수 있다.

 


스프링부트 안에도 서블릿이 숨어있음!!

-우리는 컨드롤러(@RestController)를 만들지만, 실제로는 이 컨트롤러들이 HTTP 요청/응답을 직접 처리하는게 아님

-요청/응답의 실제 입출구는? DispatcherServlet 이라는 서블릿(Servlet)이 처리!!

 

<Dispatcher Servlet이란?>

-스프링에서는 DispatcherServlet이 서블릿의 역할을 담당한다.

-클라이언트의 요청을 받아서 서블릿 컨테이너에서 관리되는 DispatcherServlet이 요청을 처리하고 반환하는 것이다.

 

<DispatcherServlet의 동작>

1. 클라이언트 요청
클라이언트에서 요청(HttpServletRequest)이 들어오면, 서블릿 컨테이너는 DispatcherServlet으로 이를 전달한다.
2. Handler 조회
DispatcherServlet은 핸들러 매핑(HandlerMapping)을 통해 요청 URI에 매핑된 핸들러(Controller)를 탐색한다.

3. Handler Adapter 조회

조회한 핸들러를 실행할 수 있는 핸들러 어댑터를 조회한다.
4. Handler Adapter 실행
핸들러 어댑터(HandlerAdapter)를 통해 핸들러(Controller)를 호출한다.
5. Handler(Controller) 실행
핸들러(Controller)를 실행하여 컨트롤러에서 요청을 처리하고, 응답을 다시 핸들러 어댑터로 반환한다.
6. ModelAndView 반환
핸들러 어댑터는 이 응답을 ModelAndView로 가공하여 반환한다.
7-1. @Controller 사용 시 
     1) View Resolver를 찾고 실행한다.
     2) View Resolver는 View의 논리 이름을 물리 이름으로 바꾸고, 랜더링 역할을 담당하는 View 객체를 반환한다.
     3) View를 랜더링 하여 클라이언트에 반환한다.
7-2. @RestController 사용 시 
    1) View와 ViewResolver를 거치지 않는다.
    2) Controller로 부터 반환 받은 데이터를 MessageConverter를 거쳐서 Json 형식으로 변환한다.
    3) Json을 ResponseBody로 응답한다.
1. 클라이언트 요청
   사용자가 브라우저 또는 앱으로 HTTP 요청을 보냄
2. DispatcherServlet 진입
   Spring Boot의 중앙 서블릿(Front Controller) 모든 요청은 DispatcherServlet으로 들어옴
3. 핸들러 매핑 (HandlerMapping)
   이 요청을 어떤 컨트롤러 메서드가 처리할지 찾음 @GetMapping("/hello"), @PostMapping("/submit") 
4. RestController 실행
   찾은 컨트롤러 메서드를 실행함
5. MessageConverter 작동
   컨트롤러가 리턴한 Java 객체(User 등)를 클라이언트가 이해할 수 있는 형태로 변환
Spring Boot에서 @RestController는 직접 응답을 만들지 않아도, Spring이 내부적으로
DispatcherServlet + HandlerMapping + MessageConverter 를 자동으로 사용해서
요청 → 실행 → JSON 응답까지 전부 처리해줍니다.

'서버 심화 스터디' 카테고리의 다른 글

서블릿 필터와 리스너  (0) 2025.05.02
서블릿 컨테이너 구현  (0) 2025.05.02
스레드 풀(thread pool)  (0) 2025.05.01
기본 소켓 프로그래밍 실습  (0) 2025.04.11
HTTP 프로토콜의 Request/Response + 소켓  (0) 2025.04.10