자바로 웹 개발을 하는 사람이라면 서블릿을 한 번쯤은 들어봤을 것이다.
특히, 자바 스프링 공부를 하기 전에 서블릿을 알아두면 좋다고 많이들 말한다.
이번 포스팅에서는 이러한 서블릿이 무엇인지, 그리고 이 서블릿이 스프링에서는 어떻게 이용되고 있는지 정리해보았다.
서블릿(Servlet)의 등장
서블릿에 대해 알아보기 전에 어떤 배경에서 서블릿이 탄생하게 됐는지 알아보자.
과거 정적 데이터만을 제공하는 웹 서버(Web Server)만으로는 답답함이 느껴졌다.
모든 사용자들에게 동일한 정보만 제공하다가 사용자들에 따라 각기 다른 정보를 제공하고 싶은 욕구가 생긴 것이다.
그렇게 해서 동적 데이터를 제공하기 위한 방법으로 PHP, Java와 같은 언어들로 프로그램을 만들었고, 해당 프로그램과 서버 간의 통신을 통해 동적 데이터를 제공하게 되었다.
그런데 통신을 하는데 있어서 한 가지 문제점이 있었다.
동적 데이터를 제공하기 위해 만든 프로그램이 하나의 언어가 아닌 다양한 언어로 구현되어 있다는 점이었다.
그래서 이러한 문제를 해결하고자 CGI라는 규약을 정했고, 해당 규약에 맞춰 통신을 진행하기 시작했다.
하지만 이 CGI에는 큰 문제점이 있었다.
하나의 클라이언트 요청 마다 하나의 프로세스가 배정된다는 점에서 비효율적이었다.
또한, 각 클라이언트 마다 CGI 객체가 따로 생성되어 객체가 여러 개 생성된다는 문제점이 있었다.
이를 해결한 방법으로 서블릿이 탄생했다.
서블릿도 CGI 규칙에 따라 데이터를 주고 받지만, 이를 서블릿 컨테이너에게 위임하고 대신에 서블릿 컨테이너와 서블릿 사이의 규칙이 존재하게 만들었다.
서블릿은 프로세스 대신에 쓰레드를 생성하도록 하고, 싱글톤 패턴을 활용해서 하나의 객체가 생성되도록 했다.
서블릿 동작 방식
서블릿의 등장으로 이제 정적 데이터는 물론 동적 데이터도 효율적으로 처리할 수 있게 되었다.
클라이언트에게서 요청이 오면 각 요청을 쓰레드로 받고, 서블릿 객체가 이를 수행하고, 이 로직은 WAS 내에서 수행되는 형태이다.
서블릿은 서블릿 컨테이너에 의해서 관리된다.
Servlet은 인터페이스로 제공되는데, Servlet 인터페이스를 구현하기 위해서는 GenericServlet을 상속받거나, HTTP Servlet을 상속받아 구현할 수 있다.
Servlet 인터페이스는 Servlet을 초기화하고, 요청을 처리하고, Servlet을 서버에서 제거하는 방법에 대해 정의한다. 이러한 방법을 생명 주기라고 하고 다음과 같은 순서로 호출된다.
- Servlet Request / Servlet Response 객체 생성
- 설정 파일(Web.xml)을 참고하여 매핑할 Servlet을 확인
- 해당 서블릿 인스턴스 존재 유무를 확인하여 없으면 생성(init())
- Servlet Container에 스레드를 생성하고, res, req를 인자로 service를 실행
- 응답 후, response, request 객체를 소멸
서블릿 객체는 생성만하고 소멸시키지 않는데, 이는 서블릿 객체가 싱글톤으로 관리되기 때문이다.
서블릿 객체는 소멸되지 않고 남아있다가 다음번 같은 요청이 들어왔을 때 컨테이너에 의해 호출되어 또 사용된다.
쉽게 말해서 우리는 URL 마다 해당 요청을 수행하는 Servlet을 생성하고, 이를 Web.xml에 등록하여 관리하는 식으로 동적 데이터를 만들어 보내줄 수 있게 된 것이다.
아래 이미지를 참고하자.
서블릿에서 MVC 패턴으로
각 URL 요청을 담당하여 응답을 내려주는 방식으로 서블릿을 사용하고 있는데, 이를 더 개선할 수는 없을까?
만약 각 요청 URL 마다 서블릿을 매칭하는 것이 아니라 중간에 요청만을 담당하여 받는 역할을 두면 어떨까?
그렇게 해서 탄생한 것이 바로 프론트 컨트롤러 패턴이다.
프론트 컨트롤러 패턴은 앞에서 요청을 받는 서블릿이 존재하고, 요청에 따라 알맞은 서블릿을 호출해주는 방식이다.
이렇게 구성함으로써 요청을 받는 로직과 실제 비즈니스 로직을 분리할 수 있으며, 요청을 받고 각 서블릿을 호출해주는 부분을 한 곳에서 관리할 수 있기 때문에 유지보수에도 훨씬 좋은 구조가 된 것이다.
구조는 대략 다음과 같다.
맨 앞단에서 요청을 받는 서블릿은 요청 외에도 각 요청의 컨트롤러를 찾아줘야 하는 역할과 컨트롤러로부터 넘어온 데이터를 다시 응답 데이터로 보내주는 등 많은 역할을 하고 있다.
따라서 이러한 역할을 좀 더 세분화하여 나누어 구현하면서 MVC 패턴을 기반으로 점점 구조가 발전하였고, 현재 스프링 프레임워크가 사용하는 MVC 구조가 탄생하게 된 것이다.
Spring MVC에서 서블릿
앞서 말한 내용들을 통해 발전된 형태가 스프링 MVC 구조의 기반이 됐다.
결국 Servlet과 Spring은 별개의 기술이 아니며, Spring이 Servlet을 내부적으로 구현하고 있다고 보면 된다.
스프링에서는 Servlet을 Dispatcher Servlet으로 활용하고 있다.
앞서 언급한 프론트 컨트롤러 패턴에서 프론트 컨트롤러 역할을 하는 것이 바로 이 Dispatcher Servlet이라고 할 수 있다.
자, 이제 스프링이 어떻게 동작하는지 아래의 이미지와 함께 간단하게 살펴보자.
- Dispatcher Servlet을 Web.xml 파일에 등록 후, 맨 앞단에서 요청을 받아 처리하는 역할로 둔다. 모든 요청이 들어왔을 때 Dispatcher Servlet으로 통합된다.
- Dispatcher Servlet은 HandlerMapping을 통해서 요청에 맞는 컨트롤러를 찾아준다. HandlerMapping이 컨트롤러를 찾는 방법에는 4가지 정도가 있으며, 해당 방법은 서블릿을 등록한 Web.xml에서 설정이 가능하다.
- 해당 HandlerMapping을 처리할 수 있는 HandlerAdaper를 HandlerAdaper 목록에서 조회하고, HandlerAdaper가 찾은 컨트롤러를 호출해준다. 그리고 그 결과를 MondelAndView에 담아 다시 Dispatcher Servlet에게 반환한다.
- 받은 ModelAndView를 바탕으로 Dispatcher Servlet은 ViewResolver를 이용해서 실제 View를 찾아준다.
- Dispatcher Servlet이 찾은 View에 Model 데이터를 심어주고, 클라이언트에게 응답으로 보내준다.
이미지로만 봐도 쉽게 알 수 있듯이 단순히 URL에 따라 담당 서블릿을 호출하는 구조보다는 훨씬 역할이 세분화되어 동작한다는 것을 알 수 있다.
과거 Servlet만을 이용했을 때는 요청을 받아 비즈니스 로직을 처리하고, 이를 View에 내려주는 것까지 모두 직접 만들었었다.
이를 해소하고자 JSP가 나왔지만, JSP를 통해 구현하면 View에 너무 많은 코드가 작성되게 되었고 이에 따라 JSP가 무거워지는 문제들이 발생하곤 했다.
하지만, MVC 패턴을 기반으로한 Spring Web MVC가 등장하게 되면서 Dispatcher Servlet을 도입하고, View를 강제로 분리시키는 효과를 보게 되어 이러한 문제들을 해결할 수 있게 된 것이다.
참고
https://mangkyu.tistory.com/14
https://jaimemin.tistory.com/1817
'👨💻 개발' 카테고리의 다른 글
스프링과 스프링부트 차이(Spring vs Spring boot) (0) | 2021.12.11 |
---|---|
웹 애플리케이션 서버(WAS)란? 웹 서버(WS)와의 차이 (0) | 2021.12.11 |
IAM이란? (0) | 2021.12.08 |
SpringBoot에 CloudFront 적용 (0) | 2021.12.07 |
AWS CloudFront 배포하기 (0) | 2021.12.07 |