[Spring] Part 1-4-2. 나만의 MVC 프레임워크 만들기 | CGI 프로그램과 서블릿 (Servlet)
2023.01.13
-
반응형
1. CGI 프로그램과 서블릿
CGI란?
CGI는 웹 서버와 애플리케이션 사이에 데이터를 주고 받는 규약입니다.
CGI 규칙에 따라서 만들어진 프로그램을 CGI 프로그램이라고 할 수 있는 것입니다.
CGI 프로그램 종류로는 컴파일 방식 (C, C++, Java 등)과 인터프리터 방식(PHP, Python 등)이 존재합니다.
위 그림은 웹서버와 CGI 규칙을 통해서 데이터를 주고 받는 프로그램을 도식화한 것입니다.
인터프리터 방식 CGI 프로그램
인터프리터 방식 CGI 프로그램
인터프리터 방식 CGI 프로그램에서 웹서버는 Script engine(스크립트 엔진)을 실행시키며, 스크립트 엔진은 스크립트 파일을 해석하여 웹서버에게 결과값을 리턴하는 방식의 프로그램입니다.
위 그림에서 웹서버와 스크립트 엔진 사이 CGI 규칙을 통해 통신을 한다고 볼 수 있습니다.
서블릿과 서블릿 컨테이너
서블릿과 서블릿 컨테이너
서블릿(Servlet)과 서블릿 컨테이너(Servlet Container) 역시 동일하게 규칙이 적용되는데, 웹서버와 서블릿 컨테이너 사이에 CGI 규칙에 따라서 데이터를 주고 받습니다.
즉, 개발자는 CGI 규칙에 대해서 자세히 알 필요가 없어졌습니다. 대신 서블릿 컨테이너와 서블릿 파일 사이의 규칙을 알고 있어야 합니다.
서블릿(Servlet)이란?
그렇다면 서블릿이란 무엇일까요?
Servlet (Server + Applet의 합성어)
서블릿이란, 자바에서 웹 애플리케이션을 만드는 기술이라고 간단하게 말할 수 있습니다.
즉, 동적인 웹페이지를 구현하기 위한 표준이라고 보시면 됩니다.
서블릿 컨테이너(Servlet Container)란?
그렇다면 서블릿 컨테이너란 무엇일까요?
인터프리터 방식 CGI 프로그램서블릿과 서블릿 컨테이너
앞서 두 가지 방식은 모두 CGI 규칙을 통해서 데이터를 주고 받는다고 했는데, 어떤 것은 engine이라는 표현을 사용하고 어떤 것은 Container라는 표현을 사용하는 이유가 무엇일까요?
Container라 함은 Life Cycle을 관리한다고 할 때 사용됩니다. 그래서 서블릿 컨테이너는 서블릿의 Life Cycle을 관리하기 때문에 그러한 이름이 붙여졌다고 생각할 수 있습니다.
그래서 Servlet Conatiner는 다음과 같은 특징을 가지고 있습니다.
서블릿의 생성부터 소멸까지의 Life Cycle(라이프 사이클, 생명주기)을 관리하는 역할
서블릿 컨테이너는 웹서버와 소켓을 만들고 통신하는 과정을 대신 처리해 준다.
우리는 지난번 포스팅에서 WAS를 직접 만들어 보았는데 그 과정을 대신해서 해 준다고 생각하면 됩니다.
따라서 개발자는 비지니스 로직에만 집중하면 된다.
서블릿 객체를 싱글톤으로 관리 (싱글톤: 인스턴스 하나만 생성하여 공유하는 방식)
상태를 유지(stateful)하게 설계하면 안 됨(Thread safety 하지가 않기 때문)
2. WAS vs. Servlet Container
그렇다면 WAS라는 표현과 Servlet Conatiner의 차이는 무엇일까요?
WAS는 서블릿 컨테이너를 포함하는 개념이라고 보면 편합니다. 즉, WAS가 조금 더 큰 개념인 것이죠.
지난번 WAS를 구현해 보면서 알게된 점은 WAS는 매 요청마다 Thread pool에서 기존 스레드를 재사용하는데, WAS의 주요 튜닝 포인트(tuning point)는 max thread 수 인 것입니다. (max thread 수에 따라서 최대 처리 스레드 수가 달라지기 때문)
그리고 대표적인 WAS로 톰캣(Tomcat)이 있습니다.
위 사진은 WAS가 Thread pool에서 Thread를 하나 꺼내서 요청에 맞는 처리를 수행하고 과정을 통해 웹서버에 결과값을 전달하며, 클라이언트에게 다시 그 값을 최종적으로 반환하는 모습입니다.
그림에서 Servlet에 해당하는 부분을 보시면 init(), service(), destroy()에 해당하는 것들이 있는데 이것들은 추후에 서블릿을 학습하면서 계속해서 추가적으로 설명을 하도록 할 것입니다.
3. 코드로 살펴보기
앞서 서블릿 객체를 싱글톤으로 관리하기 때문에 상태를 유지(stateful)하게 설계하면 안 된다고 했는데요. (Thread safety 하지가 않기 때문) 왜 그런 것인지 코드로 한번 살펴보겠습니다.
package org.example.counter;
publicclassCounterimplementsRunnable{
privateintcount=0;
publicvoidincrement(){
count++;
}
publicvoiddecrement(){
count--;
}
publicintgetValue(){
return count;
}
@Overridepublicvoidrun(){
this.increment();
System.out.println("Value for Thread After increment " + Thread.currentThread().getName() + " " + this.getValue()); // 1this.decrement();
System.out.println("Value for Thread at last " + Thread.currentThread().getName() + " " + this.getValue()); // 0
}
}
위 코드와 같이 간단한 Counter 라는 스레드를 하나 만들어 그 안에서 count 변수값을 메소드에 의해 증가, 감소 시키고 run 메소드에서 각각을 실행해 보고 값이 어떻게 변화하는지 살펴보도록 구현하였습니다.
그리고 이를 실행시키는 RaceConditionDemo를 하나 만들것인데요, psvm을 작성하면 public static void main을 만들어주는 shortcut이 나오는데 이를 생성해 줍니다.