ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 서블릿(Servlet)이란?
    백엔드/서블릿&JSP 2020. 10. 30. 11:58

    서블릿이란?

    자바를 기반으로 하는 웹페이지를 동적으로 만들어줄 수 있는 일종의 프로그램을 말한다.

     

    사실 좁게 보면 서블릿이란 위와 같은 기능을 하는 자바의 클래스를 뜻한다.

     

    넓게보면 위 기능을 수행하기 위한 자바의 패키지를 뜻한다.

    서블릿의 등장 배경

    서버 부분에서 이야기했다시피 과거 서버는 정적인 자료(주로 HTML 문서)만 주고받을 수 있었다.

     

    초기의 클라이언트가 자료를 요청하면 서버는 미리 만들어진 자료를 저장하고 있다가 반환했다.

     

    하지만 인터넷 사용자가 많아지고 다양한 기능을 웹을 통해 구현하고자 하는 움직임이 많아졌다.

     

    사용자는 정적인 자료가 아닌 자기 필요에 맞는 자료를 웹페이지를 통해 제공 받고 싶어했다.

     

    그리고 사용자 요구에 맞춰 동적으로 반응하는 페이지을 만들기 위해 만들어진 것이 서블릿이다.

     


    서블릿과 서버

    과거 정적인 웹페이지만 자료를 제공할 때는 서버의 구조도 단순했다.

     

     

     

     

    사용자가 요청하면 서버는 이미 저장된 페이지를 반환하하기만 했다.

     

    이러한 서버는 동적인 페이지를 제공하기 위해 한 단계 업그레이드 됐다.

     

     

     

     

    서버가 업그레이드 되면서 연산 기능까지 가지게 됐다.

     

    그렇게 되면서 과거에는 서버라 불리던 것은 크게 WEB 서버와 WAS(Web Application Server)로 나눠졌다.

     

    위 그림에서 볼 수 있듯 WEB 서버는 사용자의 요청에 따라 정적인 페이지를 제공한다.

     

    그리고 WAS는 사용자의 요청 중 연산이 필요한 부분을 맡아서 그 결과를 계산한다.

     

    그리고 WAS는 연산 결과를 웹서버로 제공하고 웹서버 정적 페이지를 만들어 사용자에게 전달한다.

     

    이때 WAS에서 연산을 담당하는 것이 서블릿이다.

     

    서블릿은 WAS 안에 있는 서블릿 컨테이너 또는 웹 컨테이너라고 불리는 공간에서 활용하게 된다.

    웹 컨테이너란(WEB Container)?

    서블릿 컨테이너라고도 불리는 웹 컨테이너는 서블릿을 이용해 사용자 요청을 처리하는 역할을 한다.

     

    보통 일반적으로 홈페이지를 들어가면 한 가지 기능만 제공하는 홈페이지는 거의 없다.

     

    쇼핑몰 하나만 생각해도 상품 등록, 장바구니, 게시판, 회원 가입 등 다양한 기능을 한다.

     

    그렇다는 이야기는 각각의 기능을 구현할 다양한 서블릿이 한 서버 안에서 작동한다는 것이다.

     

    하지만 이러한 서블릿이 서버에서 잘 운용되도록 일일이 컨트롤하는 것은 쉬운 일이 아니다.

     

    웹 컨테이너는 이러한 다양한 서블릿이 고객의 요청에 따라 작동하도록 서블릿을 제어한다.

     

    이를 통해 효율적으로 서버 관리 및 사용자 요청을 처리할 수 있다.

     


    서블릿의 구조

    대략적인 서블릿과 서버에 대한 이야기는 했으니 이제 다시 서블릿으로 넘어오자.

     

    서블릿은 위에서 정의했다시피 클래스이자 하나의 자바 패키지라고 했다.

     

    이것은 무슨말일까?

     

    우선 서블릿은 클라이언트의 동적인 웹페이지 요청을 처리하기 위해 만들었다.

     

    그리고 우리가 서블릿을 작성해보면 알겠지만 서블릿은 자바 클래스의 형태를 가지고 있다.

     

    즉 서블릿은 자바 언어로 쓰여지고 자바를 기반으로 하는 프로그램이라는 것이다.

     

    그리고 이러한 서블릿 클래스는 javax.servlet.http라는 패키지 안에 포함되어있다.

     

    javax.servlet 패키지는 서블릿 구현을 위한 다양한 인터페이스와 클래스가 포함된 꾸러미다.

     

    그리고 우리가 쓰는 서블릿 클래스는 이 패키지 안에서 하나를 빌려와서 구현한 것이다.

    서블릿 패키지 내 구조

    우리가 흔히 사용하는 서블릿  클래스를 불러오면 다음과 같은 코드로 되어있다.

     

    public class Test extends HttpServlet {
    	private static final long serialVersionUID = 1L;
    	
        public Test() {
            super();
        }
    
    	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    		response.getWriter().append("Served at: ").append(request.getContextPath());
    	}
    
    	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    		doGet(request, response);
    	}
    
    }

     

    선언부를 보면 알 수 있듯이 우리가 사용하는 서블릿은 'HttpServlet'을 상속받아 구현하고 있다.

     

    위에서 말했듯 우리가 생각하는 서블릿 클래스는 실은 javax.servlet 패지키의 일부라고 말했다.

     

    그렇다면 서블릿 클래스를 사용하기까지 구조는 어떻게 될까?

     

     

     

     

    우리가 상속받아 만드는 'HttpServlet' 클래스의 패키지내 구조는 위와 같다.

     

    간단히 각 인터페이스와 설명하면 다음과 같다.

    1. Servlet 인터페이스

    각종 서블릿 클래스를 구현하는 가장 기본 토대가 되는 인터페이스.

     

    해당 인터페이스를 구현하면 서블릿 클래스를 구현하여 사용할 수 있다.

     

    해당 인터페이스를 구현하기 위해서는 아래 5개의 메서드를 구현해야 한다.

     

    이때 init(), service(), destroy() 메서드는 life-cycle 메서드라고 한다.

     

    그리고 이 메서드들은 서블릿의 사용주기에 관련된 역할을 한다.

     

    (1) init()

    웹 컨테이너에 의해 호출되어 서블릿이 서비스에 위치할 수 있도록 초기화하는 메서드.

     

    웹 컨테이너는 서블릿 객체를 생성하며 서블릿 생명 주기 중 단 한번 init()메서드를 호출한다.

     

    즉 서블릿 객체 생성 이후에 서블릿의 초기화는 단 한번만 이뤄진다는 것이다.

     

    서블릿은 init() 메소드가 완료되어야 서비스에 배치가 가능하다.

     

    이때 초기화 중 필요한 정보는 ServletConfig 인터페이스 타입의 객체를 매개변수로 받는다.

     

    그리고 init() 메서드는 일반적으로 클라이언트가 서블릿을 처음 요청할 때 호출된다.

    하지만 설정으로 요청 없이 서버 시작시 서블릿을 로드하고 init()메서드를 호출할 수도 있다.

     

    (2) service()

    웹 컨테이너에 의해 호출되어 서블릿이 클라이언트의 요청에 응답할 수 있도록 하는 메서드.

     

    service() 메서드는 init() 메서드가 성공적으로 실행된 이후 호출이 가능하다.

     

    클라이언트의 요청을 전달하는 ServletRequest 객체를 통해 웹 컨테이너에 정보가 전달되면

     

    service() 메서드가 실행되고 service() 메서드는 요청에 따른 메서드를 실행한다.

     

    예를 들면 doGet, doPost, doPut, doDelete와 같은 메서드가 있다.

     

    그 후 실행에 따른 결과물을 클라이언트에게 요청을 전달하는 ServletResponse로 응답한다.

     

    이때 중요한 점은 다음과 같다.

     

    웹 컨테이너는 service()을 실행할 때 새로운 스레드에서 service() 메서드를 실행한다는 것이다.

     

    예를 들어 한 가지 서블릿에 대해 다중 요청이 들어와도 서블릿을 매번 로드하고 초기화하지 않는다.

     

    서블릿은 한번 불러왔다면 초기화는 더이상 수행되지 않는다.

     

    그리고 사용자 요청이 있으면 웹 컨테이너는 사용자의 요청에 맞춰 스레드를 만든다.

     

    그리고 그 스레드를 통해서 service 메서드를 실행하고 이미 초기화가 끝나 대기중인 서블릿을 실행한다.

     

    (3) destroy()

    웹 컨테이너에 의해 호출되어 수명이 다한 서블릿을 서비스에서 제외되도록 하는 메서드.

     

    일반적으로 개발자가 아닌 웹 컨테이너에 의해 자동으로 수행된다.

     

    이 또한 init() 메서드와 같이 단 한번만 실행된다.

     

    만약 서버를 끄거나 재시작할 경우에도 해당 메서드가 실행된다.

     

    (4) getServletConfig()

    서블릿의 초기화를 위한 정보를 포함하는 ServletConfig의 객체를 반환하는 메서드.

     

    참고로 이때 반환되는 객체는 이미 한번 init() 메서드로 전달된 상태의 객체다.

     

    (5) getServletinfo()

    서블릿에 관한 다양한 정보(버전, 저작권자, 작성자)를 반환함.

    2. ServletConfig 인터페이스

    초기화 중에 서블릿에 정보를 전달하기 위해 서블릿 컨테이너에서 사용하는 서블릿 구성을 위한 객체.

    3. GenericServlet 추상 클래스

    Servlet 인터페이스와 ServletConfig 인터페이스를 구현하여 만든 추상 클래스다.

     

    기존 Servlet 인터페이스를 구현하기 위해서는 일일이 메서드를 따로 만들어줘야했다.

     

    게다가 ServletConfig 인터페이스까지 같이 구현해야 했다.

     

    이를 조금 더 수월하게 하기 위해 만든 것이 GenericServlet 추상 클래스다.

     

    GenericServlet은 Servlet의 라이프사이클 메서드 중 init(), destroy()를 간단하게 제공한다.

     

    즉 사용자는 service()만 구현하여 서블릿을 간편하게 실행할 수 있다.

    4. HttpServlet 추상 클래스

    GenericServlet를 상속하여 만든 추상 클래스다.

     

    HttpServlet는 GenericServlet과 마찬가지로 상속받으면 간단하게 서블릿을 실행할 수 있다.

     

    그리고 우리가 대부분 사용하는 서블릿은 HttpServlet 추상 클래스를 상속받은 클래스다.

     

    HttpServlet가 init(), destroy()까지 구현했다면 HttpServlet는 service()까지 구현되있다.

     

    설정상으로는 건드릴 필요가 없을 정도로 구체화 정도가 높은 편이다.

     

    다만 이름에서 알 수 있듯이 GenericServlet은 http뿐 아니라 다양한 프로토콜에 대응이 가능하다.

     

    즉 Http 프로토콜을 이용하지 않을 때도 사용이 가능하다.

     

    하지만 HttpServlet는 웹개발서 주로 쓰는 프로토콜이 http라서 이를 편하게하려고 만들었다.

     

    그래서 HttpServlet는 Http 프로토콜에만 한정적으로 사용할 수 있다.

     

    단, 이때 주의할 것이 HttpServlet가 상속받는 GenericServlet는 javax.servlet의 패키지다.

     

    하지만 우리가 흔히 쓰는 서블릿 클래스는 HttpServlet을 상속받는데 이 추상클래스는

     

    Http 프로토콜 대응을 위한 서블릿 전용 패키지인 javax.servlet.http 패키지속해있다.

     

    상속도를 봤을 때 GenericServlet 추상클래스까지는 javax.servlet의 패키지에 속한다.

     

    그리고 우리가 흔히 상속받는 HttpServlet 추상클래스는 javax.servlet.http의 패키지에 속한다.

     


    웹 컨테이너에서의 서블릿 실행 순서

    앞서 말했듯 서블릿은 웹 컨테이너에 의해 제어된다고 이야기했다.

     

    그렇다면 지금까지 설명한 내용을 정리해서 서블릿이 어떻게 요청에 따라 실행되는지 알아보자.

    1. HttpServlet 추상 클래스를 상속한 일반적인 서블릿 실행 순서

     

     

     

     

    (1) 클라이언트의 요청

    클라이언트는 HTTP프로토콜을 통해서 원하는 데이터를 요청(Request)한다.

     

    이때 고객의 요청이 정적 페이지 요청이라면 WEB서버를 통해서 바로 처리해준다.

     

    반대로 동적인 페이지에 대한 요청이라면 고객의 요청은 웹 컨테이너(서블릿 컨테이너)로 넘어간다.

     

    (2) HttpServletRequest,  HttpServletReponse 객체 생성

    HttpServletRequest 객체는 고객의 요청 정보를 담은 객체로서 웹 컨테이너에 의해 생성된다.

     

    이 객체는 웹 컨테이너에서 고객 요청 정보를 담고 해당 정보가 처리될때까지 웹 컨테이너를 돌아다닌다.

     

    반대로 HttpServletReponse 객체는 요청에 따른 처리 정보를 담은 객체로서 웹 컨테이너에 의해 생성된다.

     

    그리고 이 객체는 고객 요청의 처리에 따라 떠돌다가 최종적으로는 고객의 요청에 응답하게 된다.

     

    고객의 요청에 따른 데이터 처리를 위한 흐름은 HttpServletRequest 객체가 처리한다.

     

    그리고 데이터 처리 후 결과물을 반환하기 위한 흐름은 HttpServletReponse 객체가 처리한다.

     

    (3) Web.xml 파싱

    배포 서술자(deployment descriptor)라고도 불리는 Web.xml은 서버를 초기화 할때

     

    서블릿의 위치가 어디있는지 설정을 도와주는 문서다.

     

    즉 어떤 서블릿이 프로젝트 폴더 내에 있다면 어느 위치에 존재하고 어떤 URL로 접속해야하는지 알려준다.

     

    컨테이너는 고객 요청 정보를 파악한 이후에 Web.xml 문서에 따라 그에 맞는 서블릿 주소를 찾는다.

     

    그리고 서블릿에 접근을 하게 된다.

     

    (4) 서블릿 초기화

    컨테이너가 실행할 서블릿을 찾았다면 이제 해당 서블릿 클래스를 로드하게 된다.

     

    즉 서버에 불러온다.

     

    그리고 서블릿에서 사용할 객체를 만든다.

     

    그리고 이 객체를 이용해 init() 메서드를 호출하여 서블릿을 사용할 수 있도록 초기화한다.

     

    이때 매우 중요한 점은 위 과정은 한 개의 서블릿이 처음 요청될 때 수행된다는 점이다.

     

    앞서 service() 메서드에서 설명했듯 서블릿은 사용자가 요청할 때마다 새로 초기화되지 않는다.

     

    클래스 로드, 서블릿 인스턴스 생성, init()은 서블릿 생명 주기에서 단 한번만 수행된다.

     

    이때 만들어지는 인스턴스는 싱글톤 패턴에 따른 것으로 주기 내내 단 한번 만들어진다.

     

    그리고 많은 요청이 다중으로 들어와도 이 한 개의 인스턴스를 사용해 service() 메서드를 실행한다.

     

    이는 스레드 생성에 따라 가능한 것이다.

     

    서블릿이 최초 요청에 따라 초기화 및 실행된 이후에는 고객의 요청은

     

    새로운 스레드를 만들어서 service() 메서드를 실행하는 식으로 이뤄진다.

     

    이로 인해 불필요한 자원 낭비를 막을 수 있고 효율적인 자원 관리가 가능해진다.

     

    단, 이렇게 서블릿을 이용할 경우 최초 요청을 한 사용자는 요청에 따른 딜레이라 발생할 수 있다.

     

    하지만 이는 설정을 통해 극복할 수 있다.

     

    모든 서블릿의 로드와 초기화를 사용자 요청 없이도 서버 시작과 동시에 진행하도록 설정할 수 있다.

     

    이렇게 설정할 경우 최초 사용자라하더라도 딜레이없이 요청에 대한 처리를 받을 수 있다.

     

    (5) service() 실행

    초기화가 진행된 이후에는 고객의 요청을 실질적으로 처리하는 service() 메서드가 실행된다.

     

    service()는 초기화가 진행되어야 실행된다.

     

    그리고 service()는 고객 요청이 get방식이냐 post방식이냐에 따라 거기에 맞는 메서드를 실행한다.

     

    고객의 요청이 get방식으로 전달됐다면 doGet() 메서드를 실행한다.

     

    고객의 요청이 post방식으로 전달됐다면 doPost() 메서드를 실행한다.

     

    이전에도 말했듯이 서블릿은 생명 주기에서 초기화가 단 한번 진행된다.

     

    그리고 나선 요청에 따라 스레드를 생성하여 service() 메서드를 실행하게 된다.

     

    (6) destroy() 실행

    더이상 사용되지 않는다고 판단하거나 서버가 종료될 때 컨테이너는 destroy()를 실행한다.

     

    destroy() 메서드는 초기화되어 실행 중인 servlet을 제거하는 역할을 한다.

     

    서버가 종료되거나 오랫동안 서블릿이 사용되지 않는다고 판단되면 실행된다.

     

    즉 JVM의 가비지 컬렉터(GC)의 기능을 하는 메서드라고 보면 된다.

     

    일반적으로 컨테이너가 자동적으로 실행하고 관리하게 된다.

    웹 컨테이너에서 서비스 실행

     

     

    앞서 말했듯 service() 메서드를 실행하기 위해서는 최초 요청에만 초기화를 단 한번 진행한다고 했다.

     

    그림으로 나타내면 위와 같다.

     

    컨테이너에서 서블릿에 대한 최초 요청이 있을 때만 서블릿을 초기화하는 작업을 한다.

     

    그 뒤에는 다른 사용자가 해당 서블릿에 대해 요청할 경우에 스레드를 만들어서 대응한다.

     

    이렇게 스레드를 이용해 최초에 만든 인스턴스 하나로 서블릿의 service()메서드를 실행하여 처리한다.

     

    단 서버 구동시 미리 서블릿에 대한 초기화를 미리 진행하면 최초 사용자도 초기화없이 사용이 가능하다.

    '백엔드 > 서블릿&JSP' 카테고리의 다른 글

    세션과 쿠키(Session과 Cookie)  (0) 2021.01.07
    javax.servlet.http 패키지  (0) 2021.01.07
    javax.servlet 패키지  (0) 2021.01.06
    JSP 기초 문법  (0) 2020.11.02
    JSP(JavaServer Pages)란?  (0) 2020.11.01
Designed by Tistory.