서블릿
서블릿(Servlet)이란?
서블릿은 자바 플랫폼에서 컴포넌트를 기반으로 하는 웹 애플리케이션을 개발할 때 사용하는 핵심 기술이다. JSP는 서블릿이라는 서버에 기반하는 웹 프로그래밍 기술로써, 내부적으로는 JSP가 서블릿 형태로 변환되어 실행된다.
서블릿을 모른다고 해서 JSP를 배울 수 없는 것은 아니지만 서블릿을 이용하면 웹 애플리케이션 확장이 용이해지고 독립적인 플랫폼으로 개발할 수 있다.
일반적인 장점
1. 자바를 기반으로 하므로 자바 API를 모두 이용할 수 있다.
2. 운영체제나 하드웨어에 영향을 받지 않으므로, 한 번 개발된 애플리케이션은 다양한 서버 환경에서도 실행할 수 있다.
3. 웹 애플리케이션에서 효율적인 자료 공유 방법을 제공한다.
4. 다양한 오픈소스 라이브러리와 개발도구를 활용할 수 있다.
웹 애플리케이션에서의 서블릿 이용 장점
1. MVC 패턴을 쉽게 적용할 수 있고 컨테이너와 밀접한 서버 프로그램을 구현할 수 있다.
2. MVC 패턴을 적용할 때 콘텐츠와 비즈니스 로직을 분리할 수 있으며 컨트롤러와 뷰가 역할을 분담함으로써, 웹 디자이너와 개발자 간에 작업을 원활하게 할 수 있다.
3. 리스너 및 필터 서블릿 등 고급 프로그래밍 기법을 통해 더욱 효과적인 웹 애플리케이션을 설계할 수 있다.
서블릿과 서블릿 컨테이너
서블릿은 컨테이너라고 불리는 서버 소프트웨어에 의해 동작한다.
서블릿 컨테이너는 웹 서버와 마찬가지로 URL을 기반으로 한 요청에 따라 해당 서블릿을 실행한다. JSP나 서블릿을 사용하지 않는 웹 프로그램 개발에는 웹 서버만 있으면 되지만, JSP나 서블릿으로 개발하려면 아파치 톰캣과 같은 서블릿 컨테이너가 필요하다.
서블릿 컨테이너는 웹 서버와 마찬가지로 URL을 기반으로 한 요청에 따라 해당 서블릿을 실행한다.
서블릿 동작 과정
서블릿은 개발자가 소스 작성 후 컴파일 과정을 거쳐 컨테이너에 배치(deploy)하게 되면 컨테이너에 의해 실행되어 관리된다. 이후 사용자 요청에 따라 스레드 단위로 실행되면서 데이터베이스 연동 등 필요한 작업을 수행하고 처리 결과를 사용자에게 HTML 형식으로 전달하는 구조다.
서블릿은 실행될 때 정해진 생명주기(라이프 사이클)를 따르는데, 여기서 생명주기란 서블릿 클래스가 컨테이너에 의해 실행되고, 서비스되며, 종료되는 일련의 과정을 의미한다.
클라이언트 요청에 대한 서블릿 동작 과정
1. 컨테이너는 서블릿 클래스를 로딩한다.
2. 서블릿 클래스의 생성자 메서드를 호출해서 인스턴스를 생성한다.
3. 생성된 인스턴스의 init() 메서드가 호출된다. init() 메서드는 서블릿 생명주기에서 단 한 번만 실행된다. 따라서 init() 메서드에서는 각종 초기화 작업 등을 수행한다.
4. 서블릿에 대한 사용자 요청에 대해서 web.xml 파일 혹은 애너테이션을 참조해서 URL 매핑을 확인하고, 해당 서블릿 인스턴스로부터 스레드를 생성하고 service() 메서드를 호출한다. 모든 사용자 요청에 대해서는 개별적인 service() 메서드가 호출되며 이후 GET/POST에 대해 doGet()이나 doPost() 등의 메서드가 호출된다.
5. destroy() 메서드는 서블릿이 종료될 때 호출되는 메서드로써, 필요한 작업이 있다면 이 메서드에 내용을 구현하면 된다.
서블릿 구조와 API
자바 서블릿 API는 서블릿과 서버 사이의 인터페이스를 정의하고 있다.
모든 서블릿은 javax.servlet.Servlt 인터페이스를 구현해야 한다. 이는 javax.servlet.GenericServlet과 javax.servlet.http.HttpServlet 클래스 중 하나를 상속해서 구현한다. 대개는 후자를 상속하는 것이 유리하기 때문에 특별한 경우가 아니면 HttpServlet을 상속받아 구현한다.
클라이언트 요청에 따라 서블릿 컨테이너는 service() 메서드를 호출하고, service() 메서드 요청이 GET인지 POST인지를 구분하여 각각 doGet(), doPost() 메서드를 호출한다.
service() 메서드는 컨테이너가 자동으로 호출하므로 개발자는 doGet(), doPost() 메서드를 구현하면 된다. 클라이언트 특성을 고려해서 각각의 요청을 다르게 처리할 필요가 있을 땐 GET과 POST를 구별하고, 그럴 필요가 없으면 doGet() 메서드에서 다시 doPost() 메서드를 호출하거나 service() 메서드를 오버 라이딩해서 각각의 요청에 상관없이 특정 메서드를 호출해서 처리하는 방법을 사용한다.
특히 JSP 등에 URL 입력을 통해 직접 접속하는 것을 방지하기 위해 보안상의 이유로 사용자 요청을 구분해야 하는 경우가 있으며, 웹 서비스를 개발할 경우 GET, POST, PUT 등에 따라 다르게 처리해주어야 하는 경우도 있을 수 있다.
HTTP 사용자 요청은 프로토콜에 정의된 명령에 의해 처리되는데, 일반적으로 GET과 POST를 사용한다.
예를 들어 http://www.hanbit.co.kr/index.html 이라는 브라우저 요청은 HTTP 프로토콜에서는 GET /index.html과 같이 서버에 전달된다. HTTP 프로토콜에는 이외에도 HEAD, DELETE, OPTIONS, TRACE와 같은 요청이 정의되어 있다. 서블릿에서는 각각 doHead(), doDelete(), doOptions(), doTrace() 메서드를 통해서 이러한 요청을 처리할 수 있지만 GET/POST 이외의 요청에 대한 처리는 거의 사용하지 않는다.
GET 방식
- 서버에 있는 정보를 가져오려고 설계된 방법
- 서버에 최대 240Byte까지 데이터를 전달할 수 있다. (GET으로도 서버에 정보를 전달할 수 있다)
- QUERY_STRING 환경변수를 통해서 서버로 전달되는데, 다음 형식을 따른다.
-> http://www.xxx.co.kr/servlet/login?id=hj&name=hong - ? 이후의 값들은 서버에서 QUERY_STRING을 통해 전달된다.
- URL이 노출되기 때문에 보안에 문제가 생길 수 있다.
POST 방식
- 서버로 정보를 올리려고 설계된 방법
- 서버에 전달할 수 있는 데이터 크기에 제한이 없다.
- URL에는 매개변수가 표시되지 않는다.
서블릿 생명주기
init() 메서드
클라이언트 요청이 들어오면 컨테이너는 해당 서블릿이 메모리에 있는지 확인한다. 없을 경우에는 서블릿을 메모리에 다시 적재해야 하는데, 이때 서블릿의 init() 메서드가 호출되며 각종 초기화 작업을 수행한다. 즉 init() 메서드는 처음 한 번만 실행되므로 해당 서블릿에 각각의 스레드에서 공통적으로 사용하기 위해 필요한 작업이 있다면 init() 메서드를 오버 라이딩해서 구현한다.
실행 중 서블릿이 변경될 경우에는 기존 서블릿은 종료(destroy)되고 새로운 내용을 다시 적재하려고 init() 메서드가 호출된다.
service() 메서드
init() 메서드는 최초에 한 번만 수행되고 이후 요청은 스레드로 실행되며, 각각 service() 메서드를 통해 doGet()이나 doPost()로 분기된다. 이때 파라미터로 HttpServletRequest와 HttpServletResponse 클래스 타입인 request와 response 객체가 제공되는데, 사용자 요청 처리는 request로, 응답 처리는 response 객체로 처리한다.
destroy() 메서드
컨테이너로부터 서블릿 종료 요청이 있을 때 destroy() 메서드를 호출한다. init() 메서드와 마찬가지로 한 번만 실행되며, 서블릿이 종료되면서 정리해야 할 작업이 있을 때는 destroy() 메서드를 오버라이딩해서 구현하면 된다.