Servlet 에 대해 개념도 알아보고 활용도 해보자.
서버 : 특정 서비스를 제공하는 컴퓨터
클라이언트 : 서비스를 이용하는 사용자

나뉜 이유?
| 종류 | 설명 |
|---|---|
| Web Server | 웹 브라우저와 HTTP 프로토콜을 사용하여 사용자의 요구에 따른 특정 서비스를 제공하는 서버 |
| Mail Server | 인터넷을 통해 사용자 간의 전자 우편을 주고 받는 서비스 제공 |
| FTP Server | 서버 내에 파일을 업로드, 다운로드 할 수 있도록 파일 관리 기능 제공 |
| Telnet Server | Terminal, 텍스트로만 이루어진 창에서 특정 명령어를 통해 원격지 서버를 접속 및 관리 |
| Database Server | 데이터를 저장하고, 원격지에 접속할 경우 권한에 따라 해당 데이터를 열람, 추가, 수정, 삭제하는 기능 처리 |



사용자에게 HTML 페이지 같이 정적인 요소들을 화면에 보여주는 역할을 하는 HTTP 프로토콜을 통해 웹 브라우저에 제공하는 서버를 뜻한다.
위와 같은 것들이 있다.
사용자가 요청한 서비스의 결과를 스크립트 언어 등으로 가공하여 생성한 동적인 페이지를 사용자에게 보여주는 역할
웹 서버가 웹 애플리케이션 서버에 요청하면, 웹 애플리케이션 서버가 해당 프로그램을 실행하는 방식
-> 동일 프로그램에 여러 요청이 있을 시, 한 개의 프로그램을 실행해 다수 요청을 처리한다.

예시)
위와 같은 것들이 있다.

| 구분 | 장점 | 단점 |
|---|---|---|
| Web Server | - 빠른 처리 속도 → 요청에 대한 결과 페이지만 전송 - 쉬운 구현 → HTML같은 단순한 문서만으로 구성 | - 한정적인 서비스 → 만들어진 정보만 보여주므로 서비스가 한정적임 - 글의 추가, 수정, 삭제가 어려움 → 문서의 내용이 변경될 경우 직접 수정 |
| WAS | - 서비스의 다양성 → 여러 데이터 활용 가능 - 글의 추가, 수정, 삭제가 쉬움 → 문서 내용이 변경되면 직접 수정하지 않음 | - 느린 처리 속도 → 데이터를 처리하여 결과 전송 - 어려운 구현 → 서비스에 해당하는 소스 직접 작성 |
@annotation 이용한 방법클라이언트가 서블릿의 url로 요청을 보내면, 톰캣이 이를 해석해 매핑된 서블릿으로 연결하는 구조이다.

mapping 경로가 잘못 되면 tomcat 실행 자체가 되지 않는다.
<servlet>
<servlet-name>xmlmapping</servlet-name>
<servlet-class>실제 클래스 명t</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>xmlmapping</servlet-name>
<url-pattern>/xml-lifecycle</url-pattern>
</servlet-mapping>
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServlet;
import java.io.IOException;
public class LifeCycleTestServlet extends HttpServlet {
/* 설명. 기본 생성자 */
public LifeCycleTestServlet() {
System.out.println("xml 방식 기본 생성자 실행");
}
/* 설명. 서블릿의 요청이 최초인 경우 서블릿 객체를 생성하고 자동으로 호출하게 되는 메소드 */
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("xml 매핑 init() 메소드 호출");
System.out.println("실제로는 요청에 따라 doGet() 또는 doPost()가 실행됨");
}
/* 설명.
* 서블릿 컨테이너에 의해 호출되며 최초 요청 시에만 init() 이후에 동작하고, 2번재부터는 service()만 호출되게 된다.
* */
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
System.out.println("xml 매핑 service() 메소드 호출");
}
/* 설명. 컨테이너가 종료될 때 호출하는 메소드이며 주로 자원을 반납하는 용도로 사용된다. */
@Override
public void destroy() {
System.out.println("xml 매핑 destroy() 메소드 호출");
}
}
index.jsp
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<title>Servlet Life</title>
</head>
<body>
<h1 align="center">라이프 사이클 테스트</h1>
<br/>
<a href="xml-lifecycle">라이프 사이클 테스트(xml)</a>
</body>
</html>
LifeCycleTestServlet를 실행해서 링크도 클릭해보면 잘 출력되는지 확인 가능하다.
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import java.io.IOException;
@WebServlet(value="/annotation-lifecycle")
public class LifeCycleTestServlet extends HttpServlet {
public LifeCycleTestServlet() {
System.out.println("어노테이션 방식 기본 생성자 실행");
}
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("어노테이션 매핑 init() 메소드 호출");
System.out.println("실제로는 요청에 따라 doGet() 또는 doPost()가 실행됨");
}
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
System.out.println("어노테이션 매핑 service() 메소드 호출");
}
@Override
public void destroy() {
System.out.println("어노테이션 매핑 destroy() 메소드 호출");
}
}
index.jsp
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<title>Servlet Life</title>
</head>
<body>
<h1 align="center">라이프 사이클 테스트</h1>
<br/>
<a href="xml-lifecycle">라이프 사이클 테스트(xml)</a> <br>
<a href="annotation-lifecycle">라이프 사이클 테스트(annotation)</a>
</body>
</html>
또한 잘 되는 것을 알 수 있다.


요청과 응답을 주고받는 패킷은 요청 정보와 응답 정보를 직렬화한 Byte단위의 문자열 데이터로, 인코딩과 디코딩이 필요하다.

service() 메소드에서는 request, response 요청 정보를 가지고 처리 로직을 거쳐 응답한다.전송 방식 중에 get 방식, post 방식에 대해 메소드와 함께 알아보자.
서블릿이 get/post의 두 방식 중 하나로 요청 정보를 전달 받으면, request와 response를 전달하면서 해당하는 처리 메소드(doGet() 메소드 또는 doPost() 메소드)를 호출한다.
HTML에서 method 속성을 이용해 방식을 결정하며, default는 get 방식이다.
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/request-service")
public class ServiceMethodTestServlet extends HttpServlet {
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
System.out.println("req = " + req);
System.out.println("res = " + res);
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String httpMethod = request.getMethod();
System.out.println("요청 방식 : " + httpMethod);
if ("GET".equals(httpMethod)) {
doGet(request, response);
} else if("POST".equals(httpMethod)) {
doPost(request, response);
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("GET 요청을 처리할 메소드 호출 중");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("POST 요청을 처리할 메소드 호출 중");
}
}
index.jsp
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<title>JSP - Hello World</title>
</head>
<body>
<h1 align="center">Service Method</h1>
<h3>GET 방식의 요청</h3>
<h4>a 태그의 href 속성값 변경</h4>
<a href="request-service">서비스 메소드 요청하기</a>
<h3>POST 방식의 요청</h3>
<h4>form 태그의 method 속성을 post로 설정</h4>
<form action="request-service" method="post">
<input type="text" name="number" value="123">
<input type="text" name="name" value="홍길동">
<input type="submit" value="POST 방식 요청 전송">
</form>
</body>
</html>
위 2가지 코드를 통해 실행해보면

이런 형태가 되는데 index.jsp 에서의 <form action="request-service" method="post"> 이 부분에서 method ="get" 으로 바꿔서 실행할 수도 있다.
post 상태에서 버튼을 눌렀을 때 호출되는 것들은 아래 사진과 같다.

HTTP Servlet을 위한 요청 정보(request information)를 제공하는 메소드를 지정
| method 명 | 내용 |
|---|---|
| getParameter(String) | client가 전송한 값의 명칭이 매개변수와 같은 값 가져옴 |
| getParameterNames() | client가 전송한 값의 명칭 가져옴 |
| getParameterValues(String) | client가 전송한 값이 여러 개이면 배열로 가져옴 |
| getParameterMap() | client가 전송한 값 전체를 Map방식으로 가져옴 |
| setAttribute(String, object) | request 객체로 전달할 값을 String 이름-Object 값으로 설정 |
| getAttribute(String) | 매개변수와 동일한 객체 속성 값 가져옴 |
| removeAttribute(String) | request객체에 저장된 매개변수와 동일한 속성 값 삭제 |
| setCharacterEncoding(String) | 전송 받은 request객체 값들의 CharaterSet 설정 |
| getRequestDispatcher(String) | 컨테이너 내에서 request, response객체를 전송하여 처리한 컴포넌트(jsp파일 등)를 가져옴 (forward() method와 같이 사용) |
HttpServletResponse 아래에 있는 활용에는 사용되지 않았지만, 그래도 HttpServletRequest 안에 포함되는 내용이기에 추가했다.
General header
Request header
Response header
Entity header
getHeader() 메소드로 확인 가능한 값
| header 속성 | 값 |
|---|---|
| accept | 요청을 보낼 때 서버에게 요청할 응답 타입 |
| accept-encoding | 응답 시 원하는 인코딩 방식 |
| accept-language | 응답 시 원하는 언어 |
| connection | HTTP 통신이 완료된 후에 네트워크 접속을 유지할지 결정 |
| (기본값: keep-alive = 연결을 열린 상태로 유지) | |
| host | 서버의 도메인 네임과 서버가 현재 Listening 중인 TCP포트 지정 |
| (반드시 하나가 존재. 없거나 둘 이상이면 404) | |
| referer | 이 페이지 이전에 대한 주소 |
| sec-fetch-dest | 요청 대상 |
| sec-fetch-mode | 요청 모드 |
| sec-fetch-site | 출처(origin)와 요청된 resource 사이의 관계 |
| sec-fetch-user | 사용자가 시작한 요청일 때만 보내짐 (항상 ?1 값 가짐) |
| cache-control | 캐시 설정 |
| upgrade-insecure-requests | HTTP 메시지 전송 시 보안 적용 |
| user-agent | 현재 사용자가 어떤 클라이언트(OS, browser 포함)을 이용해 보낸 요청인지 명시 |
cross-site : 요청 개시자와 resource를 호스팅하는 서버가 다른 사이트일 경우same-origin : 요청 개시자와 resource를 호스팅하는 서버가 동일한 출처(origin)를 가질 경우same-site : 요청 개시자와 resource를 호스팅하는 서버가 동일한 scheme, 도메인/서브도메인을 가지지만 port가 다른 경우none : 요청이 사용자로부터 시작되었을 경우. (ex. 주소창에 URL 입력, 브라우저 창에 파일 끌어다 놓기 등.)cors : CORS protocol 요청navigate : HTML document 사이 이동 시no-cors : no-cors 요청same-origin : 요청 중인 resource와 동일한 출처websocket : websocket 연결을 설정하기 위한 요청요청에 대한 처리 결과를 작성하기 위해 사용하는 객체이다.
| method 명 | 내용 |
|---|---|
| setContentType(String) | 응답으로 작성하는 페이지의 MIME type을 설정 |
| setCharacterEncoding(String) | 응답하는 데이터의 CharacterSet을 지정 |
| getWriter() | 페이지에 문자 전송을 위한 Stream을 가져옴 |
| getOutputStream() | 페이지에 byte단위의 전송을 위한 Stream을 가져옴 |
| sendRedirect(String) | client가 매개변수의 페이지를 다시 서버에 요청함 |
doGet(), doPost() 메소드를 활용해서 같이 볼 수 있다.
index.jsp
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<title>JSP - Hello World</title>
</head>
<body>
<h1 align="center">Request Parameter</h1>
<h3>Get 방식의 요청</h3>
<h4>form 태그를 이용한 get 방식 요청</h4>
<form action="querystring" method="get">
<label>이름 : </label><input type="text" name="name"> <br>
<label>나이 : </label><input type="number" name="age"> <br>
<label>생일 : </label><input type="date" name="birthday"> <br>
<label>성별 : </label>
<input type="radio" name="gender" id="male" value="M">
<label for="male">남자</label>
<input type="radio" name="gender" id="female" value="F">
<label for="female">여자</label> <br>
<label>국적 : </label>
<select name="national">
<option value="ko">한국</option>
<option value="ch">중국</option>
<option value="jp">일본</option>
<option value="etc">기타</option>
</select>
<br>
<label>취미 : </label>
<input type="checkbox" name="hobbies" id="movie" value="movie"><label for="movie">영화</label>
<input type="checkbox" name="hobbies" id="music" value="music"><label for="music">음악</label>
<input type="checkbox" name="hobbies" id="sleep" value="sleep"><label for="sleep">취침</label>
<br>
<input type="submit" value="GET 요청">
<button type="button">GET 요청</button>
</form>
<h4>a 태그의 href 속성에 직접 파라미터를 포함한 쿼리 스트링 방식으로 작성해get 요청하기</h4>
<a href="querystring?name=홍길동&age=30&birthday=2024-02-06&gender=M&national=ko&hobbies=movie">
a 태그를 활용한 쿼리 스트링 값 전달
</a>
<h4>form 태그를 이용한 post 방식 요청</h4>
<form action="formdata" method="post">
<label>이름 : </label><input type="text" name="name"> <br>
<label>나이 : </label><input type="number" name="age"> <br>
<label>생일 : </label><input type="date" name="birthday"> <br>
<label>성별 : </label>
<input type="radio" name="gender" id="male" value="M">
<label for="male">남자</label>
<input type="radio" name="gender" id="female" value="F">
<label for="female">여자</label> <br>
<label>국적 : </label>
<select name="national">
<option value="ko">한국</option>
<option value="ch">중국</option>
<option value="jp">일본</option>
<option value="etc">기타</option>
</select>
<br>
<label>취미 : </label>
<input type="checkbox" name="hobbies" id="movie" value="movie"><label for="movie">영화</label>
<input type="checkbox" name="hobbies" id="music" value="music"><label for="music">음악</label>
<input type="checkbox" name="hobbies" id="sleep" value="sleep"><label for="sleep">취침</label>
<br>
<input type="submit" value="POST 요청">
<button type="button">POST 요청</button>
</form>
</body>
</html>

사진과 같이 만들어진다. 이제 값들을 넣어보자.
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
@WebServlet("/querystring")
public class QueryStringServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
System.out.println("name = " + name);
int age = Integer.parseInt(req.getParameter("age"));
System.out.println("age = " + age);
java.sql.Date birthday = java.sql.Date.valueOf(req.getParameter("birthday"));
System.out.println("birthday = " + birthday);
char gender = req.getParameter("gender").charAt(0);
System.out.println("gender = " + gender);
String national = req.getParameter("national");
System.out.println("national = " + national);
String[] hobbies = req.getParameterValues("hobbies");
System.out.println("hobbies = " + Arrays.toString(hobbies));
}
}
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
@WebServlet("/formdata")
public class FormDataServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
System.out.println("name = " + name);
Enumeration<String> names = req.getParameterNames();
while(names.hasMoreElements()) {
System.out.println(names.nextElement());
}
}
}

a 태그에 쿼리 스트링 방식으로 넣은건 querystring?name=홍길동&age=30&birthday=2024-02-06&gender=M&national=ko&hobbies=movie 이 부분인걸 알 수 있을 것이다.
실행결과
GET 요청 버튼 클릭 시 웹 브라우저 URL

GET 요청 버튼 클릭 시 인텔리제이 출력

a 태그 링크 클릭 시 웹 브라우저 URL

a 태그 링크 클릭 시 인텔리제이 출력

POST 요청 버튼 클릭 시


컨테이너라는 이름으로 비슷하게 생각할 수 있지만, 둘은 다른 역할을 한다. 그것에 대해서는 다음 글에서 작성하겠다.