서블릿은 컨테이너 기반으로 동작하기 때문에 정해진 규격에 의해 프로그래밍해야 한다는 제약이 있다. 따라서 서블릿 클래스를 단순히 만드는 것보다 기본적인 구조와 생명 주기에 대한 이해가 선행되어야 한다.
서블릿 클래스의 구조
서블릿 자체는 자바로 구현하지만 서블릿 컨테이너에 해당 클래스가 서블릿임을 알려야 하며, 어떤 URL 접근에 실행해야 하는지 등록하는 과정이 필요하다. 기본적으로 웹 애플리케이션 구조를 컨테이너에 알려주기 위한 배포 서술자인 web.xml에 등록해야 하지만 서블릿 3.0부터는 별도의 web.xml 작성 없이 애너테이션을 이용한 방법이 주로 사용되고 있다.
서블릿 클래스는 javax.servlet.Servlet 인터페이스를 구현한 추상 클래스인 GenericServlet 클래스와 HttpServlet 클래스 중 하나를 상속해 구현하는 형태다. 웹 프로그래밍의 대부분은 HTTP 프로토콜에 최적화 되어 있는 HttpServlet 클래스를 상속해 구현하는 것이 좋다. 다음은 HttpServlet을 상속받아 doGet(), doPost() 메서드를 오버라이딩한 구조다.
public class HelloWorldServlet extends HttpServlet {
public doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
...
}
public doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
...
}
- doGet() : HTTP GET 요청을 처리하기 위한 메서드다.
- doPost() : HTTP POST 요청을 처리하기 위한 메소드로 보통 doGet()을 호출해 동일하게 처리된다.
- request, response : doGet(), doPost() 메서드에서 파라미터로 사용되며 서블릿 컨테이너가 클라이언트 요청과 응답을 처리할 수 있도록 서블릿에 제공하는 객체로 클라이언트와의 상호작용에 필요한 다양한 메서드를 제공한다.
request와 response
request와 response는 session, application과 함께 JSP의 내장객체이기도 하며 속성 관리 기능을 이용해 컨트롤러와 뷰 페이지 간 데이터 전달 등의 목적에도 사용된다.
HttpServletRequest
HttpServletRequest는 HTTP 프로토콜의 request 정보를 서블릿에 전달하기 위한 목적으로 사용한다. 이때 사용하는 클래스에는 헤더 정보, 파라미터, 쿠키, URL, URI 등의 정보를 읽어들이는 메서드와 HTTP Body의 Stream을 읽어 들이는 메서드를 가지고 있다. 서블릿 컨테이너에서 생성되고 클라이언트 요청이 doGet(), doPost()로 전달될 때 인자로 함께 전달된다. 쉽게 말해서 서블릿에서 클라이언트와 연결되어 처리할 작업은 모두 HttpServletRequest를 통해야 한다.
HttpServletRequest에서 제공하는 주요 메서드
메서드 | 반환 예 | 설명 |
getParameter(name) | hong1234 | name 속성으로 전달된 파라미터 값 |
getParameterValues(name) | {hong, kang, kim} | 동일한 name 속성으로 전달된 모든 파라미터 값 |
getRequestURL() | http://www.abc.com:8080/shop/list.jsp | URL |
getRequestURI() | /shop/list.jsp | URL에서 스키마, 서버이름, 포트 번호를 제외한 나머지 주소와 파라미터 |
getScheme() | http | http, https, FTP와 같은 프로토콜 |
getServerName() | www.abc.com | 서버 이름 |
getServerPort() | 8080 | 서버 포트 |
getContextPath() | /shop | 콘텍스트 경로 |
getMethod() | GET | GET, POST 등의 HTTP 메서드 |
isSecure() | false | SSL보안 여부. https와 같은 보안 채널의 사용 여부(true/false) |
getLocale() | ko_KR | 지역 정보 |
getProtocol() | HTTP/1.1 | 사용하는 프로토콜. 프로토콜/메이저 버전.마이너 버전 |
getLocalAddr() | 127.0.0.1 | 서버의 로컬 IP 주소 |
getRemoteAddr() | 210.102.111.212 | 클라이언트 IP 주소 |
HttpServletResponse
HttpServletRequest와 마찬가지로 클라이언트와 연결된 처리가 가능하다. 다만 클라이언트에서 서버로 전달하는 것과 관련된 것이 아니라 서버에서 클라이언트로 전달하려는 목적을 위한 기능으로 구성된다. 서블릿 컨테이너는 요청 클라이언트에 응답을 보내기 위한 HttpServletResponse 객체를 생성하여 서블릿에 전달한다. 서블릿은 해당 객체를 이용하여 content type, 응답 코드, 응답 메시지 등을 전송할 수 있다.
HttpServletResponse에서 제공하는 주요 메서드
메서드 | 설명 |
sendRedirect(String location) | 클라이언트에 리다이렉트(Redirect) 응답을 보낸 후 특정 URL로 다시 요청하게 함 |
getWriter() | 클라이언트로 데이터를 보내기 위한 출력 스트림을 리턴 |
setContentType(String type) | 클라이언트에 전달되는 콘텐츠 타입을 지정 |
addCookie(Cookie cookie) | 응답에 쿠키를 추가 |
addHeader(String name, String value) | 헤더에 name과 value를 추가 |
encodeURL(String url) | 클라이언트가 쿠키를 지원하지 않을 때 세션 id를 포함한 특정URL을 인코딩 |
getHeaderNames() | 현재 응답이 헤더에 포함된 name을 얻어옴 |
서블릿 정보 등록
서블릿 클래스만으로는 톰캣에서 실행이 불가능하기 때문에 web.xml이나 애너테이션으로 서블릿임을 선언해야 한다.
다음은 서블릿 2.x에서 사용 가능한 web.xml의 작성 예다. 지금은 거의 사용하지 않지만 별도로 작성된 자바 클래스를 서블릿 컨테이너가 알아서 등록할 수 있도록 xml 파일을 사용하는 동작 방식은 알아둘 필요가 있다.
<?xml version="1.0" encoding="UTF-8"?>
<web-app ...>
<servlet>
<servlet-name>HelloWorld</servlet-name> // servlet 이름
<servlet-class>jwbook.servlet.HelloServlet</servlet-class> // 서블릿 클래스 지정
</servlet>
<servlet-mapping>
<servlet-name>HelloWorld</servlet-name> // servlet name을 매핑
<url-pattern>/hello</url-pattern> // 서블릿 요청 주소 매핑
</servlet-mapping>
</web-app>
서블릿 3.0에서부터는 자바 애너테이션을 이용해 다음과 같이 등록한다. 필요하다면 web.xml을 사용하는 것도 가능하지만 애네터이션 사용이 권장된다. 이는 애너테이션 방식이 훨씬 깔끔하고 간단하기 때문이다.
@WebServlet(description="Hello World Servlet", urlPatterns="/hello")
public class HelloWolrdServlet extends HttpServlet {
...
}
- @WebServlet : 애너테이션 방식
- urlPatterns : 어떤 클라이언트 요청에 해당 서블릿을 실행할지 지정하는 것으로 여러 url을 등록할 수도 있다. 패턴 형식으로 특정한 경우에 실행되도록 설정하는 것도 가능하다.
애너테이션 참고
https://www.baeldung.com/java-custom-annotation
properties 파일 사용하기
프로그램 동작에 필요한 정보를 파일 형태로 제공하는 것은 매우 오래된 개념이며, 자바에서는 properties 파일을 사용하는 방식이 가장 단순하다. 다만 복잡한 구조 표현이 어렵기 때문에 서블릿 컨테이너나 스프링 프레임워크 같은 경우 초기에 xml을 주로 사용했고 지금은 자바 애너테이션 yaml을 사용한 설정 파일 등을 주로 활용한다.
서블릿의 생명 주기
서블릿은 컨테이너에 의해 동작하므로 객체의 생성 과정과 종료 과정도 컨테이너 안에서 이루어진다. 이와 같이 객체의 생성에서 종료에 이르는 과정을 생명 주기(Life Cycle)라고 하며, 개발자는 필요에 따라 해당 생명 주기 안에서 적절한 기능을 구현할 수 있어야 한다.
또한 웹의 특성상 비연결 구조로 인해 웹 페이지 간 데이터 공유가 안 되는 문제 해결을 위해 컨테이너에 의해 생성되고 관리되는 특별한 객체인 Scope Object를 잘 활용할 수 있어야 한다.
서블릿 클래스는 기본적으로 doGet(), doPost()와 같이 HTTP 요청 메서드에 따라 필요한 메서드를 오버라이딩해 구현하며, 서블릿 컨테이너에 의해 객체의 생성과 소멸 등이 관리되므로 필요에 따라 특정 생명 주기 이벤트에 동작하는 메서드를 구현하기도 한다.
서블릿의 생성과 소멸은 다음과 같은 과정을 거친다.
- 사용자 URL 요청에 따른 서블릿을 실행한다.
- 이때 서블릿 인스턴스가 생성되지 않았다면 인스턴스를 생성한 후 init() 메서드를 호출한다.
- 이미 객체가 생성되어 있는 경우라면 각 요청별로 서블릿 컨테이너에서 스레드를 생성해 서블릿의 service() 메서드를 호출한다.
- 사용자 요청에 따라 doGet(), doPost() 등의 메서드를 호출한다.
- 서블릿 컨테이너의 종료를 포함해 서블릿 변경 등 기존 서블릿을 종료해야 할 때 destroy() 메서드를 호출한다.
서블릿 초기화: init() 메서드
클라이언트 요청이 들어오면 컨테이너는 해당 서블릿이 메모리에 있는지 확인한다. 해당 서블릿이 메모리에 없을 경우에는 서블릿을 메모리에 적재해야 하는데, 이때 서블릿의 init() 메서드가 호출되며 각종 초기화 작업을 수행한다. 즉 init() 메서드는 처음 한번만 실행되므로 해당 서블릿에 각각의 스레드에서 공통적으로 사용하기 위해 필요한 작업이 있다면 init() 메서드를 오버라이딩해서 구현한다.
만일 실행 중 서블릿이 변경되는 경우에는 기존 서블릿은 종료(Destroy)되고 다시 시작되면서 init()메서드가 호출된다.
요청/응답: service() 메서드
init() 메서드는 최초에 한번만 수행되고 이후 요청은 스레드로 실행되며, sevice() 메서드를 통해 각각 doGet() 이나 doPost()로 분기된다. 이때 파라미터로 HttpServletRequest와 HttpServletResponse 클래서 타입인 request와 response 객체가 제공되는데, 사용자 요청 처리는 request로, 응답 처리는 response 객체로 처리한다.
서블릿 종료: destroy() 메서드
컨테이너로부터 서블릿 종료 요청이 있을 때(컨테이너가 종료될 때 혹은 웹 애플리케이션을 재등록(Reload)할 필요가 있을 때) destroy() 메서드를 호출한다. init() 메서드와 마찬가지로 한번만 실행되며, 서블릿이 종료되면서 정리해야 할 작업이 있을 때는 destroy() 메서드를 오버라이딩해서 구현하면 된다.
서블릿 소스코드의 변경 감지
실제 실행 중인 서블릿의 소스코드가 변경되는 것을 서블릿 컨테이너가 직접 감지할 수는 없으며, 이클립스와 같은 개발도구에서 톰캣을 실행한 경우 코드 변경을 이클립스에서 감지하고 컴파일한 다음 필요한 경우 해당 웹 애플리케이션만 다시 시작하거나 톰캣을 다시 시작하게 된다.
'Programming > Web' 카테고리의 다른 글
[Web] JSP의 개요(JSP의 장단점, page지시어, include지시어, taglib지시어) (0) | 2022.10.20 |
---|---|
[Web] 페이지 이동과 정보 공유(Cookie, Session, Scope Object) (0) | 2022.10.20 |
[Web] 서블릿의 개요 (0) | 2022.10.20 |
[Web] 스프링 프레임워크 (0) | 2022.10.20 |
[Web] REST API와 JAX-RS (0) | 2022.10.20 |