[Spring] 서블릿

JUNHO YEOM·2023년 2월 5일
1

Spring

목록 보기
7/14

처음 웹서버는 클라잉너트 요청에 대해서 정적인 페이지로만 응답할 수 있었어요
동적인 페이지를 만들기 위해 동적인 프로그램을 만들기 시작했습니다.
서블릿도 동적인 페이지를 만들기 위해 웹페이지에 붙이는 프로그램 입니다.

개발자들이 서블릿을 사용함으로 얻을 수 있는 이득

서블릿이 요구하는 구현 규칙을 지켜주면서 서블릿을 정의해 주면,
Http 요청 정보를 쉽게 사용할 수 있고, 처리 결과를 쉽게 응답으로 변환할 수 있어요
서블릿을 이용해서 웹 요청을 다루게 되면 개발자들이 진짜 집중해야 하는 비즈니스 로직
그러니까 가운데 있는 처리 로직에 더 집중할 수 있게 되는 거죠

서블릿은 이렇게 생겼어요

public class MyServlet extends HttpServlet {
	public void init (ServletConfig config) throws ServletException {
	super .init();
	}
	
    public void destroy() {
		super.destroy () ;
    }
	
    protected void service (HttpServLetRequest request, HttpServletResponse 	response)
		super.service(request, response);
    }
}
public abstract class HttpServlet extends GenericServlet {

	protected void service(HttpServletRequest req, HttpServletResponse resp) throws I0Exception {
    
	String method = req.getMethod();
    
    if (method.equals ("GET")) {
	    doGet(req, resp);
    } else if (method. equals ("HEAD")) {
    	doHead(req, resp);
    } else 1f (method. equals ("POST")) {
    	doPost(req, resp);
    } else if (method, equals ("PUT")) {
   	 	doPut(req, resp);
    } else if (method. equals ("DELETE")) {
    	doDelete(req, resp);
    } else 1f (method. equals ("OPTIONS")) {
    	doOptions(req, resp);
    } else 1f (method. equals ("TRACE")) {
    	doTrace(reg, resp);
    } else {
        String errMsg = "http.method_not_implemented";
        resp.sendError(HttpServLetResponse.SC_NOT_IMPLEMENTED, errMsg) ;
    }
 }   

어떤 요청(Req)이 들어오면 동작합니다.
이때, GET요청이 들어오면 doGet Method가 호출되고,
Post요청이 들어오면 doPost Method가 호출되는 형태인 것입니다.


서블릿 컨테이너와 서블릿이 호출되는 과정

서블릿 컨테이너는 서블릿을 담고, 관리하는 컨테이너 입니다.

서블릿 컨테이너의 동작

  1. Servlet Request / Servlet Response 객체 생성
  2. 설정 파일을 참고하여 매핑할 Servlet을 확인
  3. 해당 서블릿 인스턴스 존재를 유무를 확인하여 없으면 생성 (init)
  4. Servlet Container에 스레드를 생성하고, res, req를 인자로 service 실행

서블릿은 싱글톤으로 관리된다

서블릿 컨테이너가 서블릿이 있는지 확인하고, service가 실행되어 사용된 이후에 삭제되지 않는 이유는
싱글톤으로 관리되기 때문입니다.
서블릿의 생명주기는 Spring에서 서블릿 컨테이너가 관리합니다.

<!-- 서블릿이라는 패키지 아래 HelloServlet이라는 클래스 파일로 정의가 되어 있다-->
<servlet>
	<servlet-name>HelloServlet</servlet-name>
    <servlet-class>servlet.HelloServlet</servlet-class>
</servlet>

<!--/hello라는 요청이 들어오면 HelloServlet이라는 서블릿으로 처리 하는 로직-->
<servlet-mapping>
	<servlet-name>HelloServlet</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>

여러 요청의 처리

한 요청을 처리하는 도중에 다른 요청이 들어오게 되면, 멀티스레드로 요청을 처리하게 됩니다.

멀티쓰레드 처리

스레드를 생성한다는 거 자체가 큰 비용을 만들고
다른 스레드로 전환하는 Context Swtich가
많은 오버헤드를 일으켜요
스레드 생성에 제한을 두지 않는다면
많은 요청을 처리하기 위해 그만큼 많은 스레드를 생성하다가
서버의 하드웨어 한계를 넘어버리면 서버가 터질 수도 있어요



요청당 서블릿을 정해주는 곳에는 되게 비효율적인 부분이 있었어요
1. 관리 측면
멀티쓰레딩을 다뤄야 하는점

개발에 측면에서는 핸들러의 공통 로직이
매번 중복된다는 문제가 있었어요

공통로직 중복의 문제

현재 만들어진 형태로는
아메리카노를 요청하면 아메리카노만을 담당하는 직원이 나와서 주문을 받고, 만들고, 포장을 합니다.
콜드브루 주문하면 콜드브루만을 담당하는 직원이 나와서 같은일을 반복 합니다.
그래서 모든 사람이 모두 주문 받고, 포장하는 방법을 알아야 하고 직접 수행해야 합니다.
비효율적입니다.

다음의 그림을 보면 계산하고 포장하는 공통부분을 빼내서 매니저로 두었습니다.
주문과 포장이라는 공통되는 부분은 매니저가 담당하고,
음료를 만드는 개별적인 부분들은 종류별로 사람을 둬서 제작과정을 위임하게 됩니다.
이런 즉, 손님의 요청을 앞단에서 처리할 수 있는 일을
전담하는 매니저를 두는 게 프론트 컨트롤러 패턴이에요

스프링 MVC도 프론트 컨트롤러 패턴을 따르고 모든 요청을 받는 전면 컨트롤러
서블릿을 Dispatcher Servlet이라고 부르게 되는 거예요
서블릿은 하나만 두고 모든 요청을 다 받을 수 있도록 하는 거죠

그래서 이전에 서블릿을 개별적으로 다뤘던 애들은 요청마다 서블릿을 정의하고
요청을 수행할 때마다 매번 스레드를 생성했다면 이제는 하나의 서블릿만 정의하고
그 서블릿을 모든 요청을 수행할 수 있도록 하는 전략을 따르는 거죠


Dispatcher Servleto web 요청을 처리하는 과정

전면 매니저가 모든 요청을 처리는 하되 계산만을 담당하는 직원, 음료를 담당하는 직원
음료 만드는 직원을 호출하는 직원, 포장만을 담당하는 직원

좀 더 자세히 살펴보면은 디스패치서블릿은 모든 요청을 다 받고
핸들러매핑이라는 애가 내 요청을 처리할 때 컨트롤러를 찾아서 반환하고, 핸들러어뎁터는
그 컨트롤러의 메서드를 호출하여서 처리 로직을 수행하는 역할을 해요
그리고 그 처리결과를 Model And View 객체로 변환해서
디스패치서블릿에 넘기고 다시 디스패치서블릿은
뷰 리졸버를 이용해서 뷰를 찾거나 생성을 하게 돼요
그렇게 얻은 뷰에 이제 아까 모델로 들어왔던 데이터를 넣어서 응답결과 생성을 요청해서
우리가 볼 수 있는 JsP나 Thymeleaf 같은 데이터를 담은 출력 파일로 응답을 하게 됩니다.
근데 말이 쉽지 사실, 다 인터페이스들이고 구현체들도 되게 많거든요
핸들러매핑만 하더라도 스프링 mvc에서 제공하는 구현체가 4~5개 이상 있었어요
그러면 역할을 분리하고 스프링을 사용하면서 더 일이 많아진 것 같지 않으신가요?
좋은 소식을 드리면 결국 개발자들이 신경을 써야 할 부분은
노란색으로 표시된 컨트롤러 또는 핸들러 이것뿐이에요
스프링 없이 개발했으면 빨간색들도 다 직접 처리했어야 했을 부분들인데
그럼 빨간색으로 표시한 부분들은 누가 해주나요?
이 역할을 하는 객체들은 디스패치서블릿이
스프링 컨테이너로부터 주입을 받아서 사용하고 동작을 하게 되는거에요
스프링컨테이너도 서블릿 컨테이너하고 되게 비슷해요
프로그램이 동작하는 동안 사용되는 자바 객체들을 프레임워크가 대신 관리하고
보관하기 위해서 사용되는 바구니에요

두 개가 있는데 서블릿 웹 어플리케이션 컨텍스트는
웹 요청 처리 관련 객체들이 담겨있고
루트 웹 어플리케이션 컨텍스트 안에는
웬 요청처리 관련된 빈 외의 컴포넌트들
그러니까 서비스, 레포지토리 관련 객체들이 관리가 되는 거에요
그리고 이 컨테이너가 개발에 필요한 부분이나
디스패치서블릿이 요청을 처리할 때
필요한 부분은 알아서 주입하게 되는 거에요

한마디로 서블릿 설정파일만 잘 작성해주면
설정대로 생성된 객체가 스프링 컨테이너에서 관리되고
필요한 부분에서 주입 받아서
디스패처서블릿이 알아서 사용할 수 있게 된다는 얘기 해요
꼭 서블릿 웹 어플리케이션 컨텍스트,그러니까 디스패치서블릿의 요청처리뿐만 아니라
컨트롤러를 포함하는 어플리케이션 전반적인 개발 안에서도 필요한 부분에서
루트 웹 어플리케이션 컨텍스트,그러니까 서블릿 컨테이너를 활용해서 개발할 수 있게 되는 거예요

결국, 스프링으로 웹 요청을 처리하는 것은
스프링 mvc에서 제공하는 디스패체서블릿과
웹 요청 처리 관련 구현체들을 사용할 수 있다는
이야기와 동시에 스프링 컨테이너,즉 스프링 IoC를 사용해서
개발할 수 있다는 이야기가 돼요
그래서 최종적인 목적은 개발자에게 핸들러,
그러니까 우리가 개발할 때 집중해야하는
요청처리 로직들에만 신경을 쓸 수 있도록 하기 위함이에요


조금 더 나아가면
1. 웹 서버와 웹 어플리케이션 서버
2. WAS가 멀티 스레드를 관리하는 방식
3. 스프링 설정 파일과 서블릿 설정 파일
4. 스프링 컨테이너에 빈을 등록하는 방법 / 컨테이너가 주입 위치를 찾는 방법
5. 디폴트 서블릿
6. 스프링과 스프링 부트

다음의 글을 문서로 정리한 내용입니다.
https://www.youtube.com/watch?v=calGCwG_B4Y

1개의 댓글

comment-user-thumbnail
2023년 2월 7일

클라잉너트는 무슨 너트인가요? 볼트는 없나요? ㅋ 댕~🎵

답글 달기