
HTTP 요청 메시지를 개발자가 직접 파싱하여 사용할 수도 있지만, 이는 매우 번거롭고 반복적인 작업이다. 서블릿(Servlet)은 HTTP 요청 메시지를 쉽게 파싱하여 개발자가 편리하게 사용할 수 있도록 지원한다.
서블릿이 제공하는 HttpServletRequest와 HttpServletResponse는
HTTP 요청 및 응답 메시지를 편리하게 사용할 수 있도록 도와주는 객체이다.
그러나 서블릿이 개발자의 의도를 완벽히 이해하여 모든 요청 및 응답 처리를 자동화해주는 것은 아니다.
따라서, 개발자는 HTTP 스펙이 제공하는 요청 및 응답 메시지의 구조와 작동 방식을 충분히 이해해야 한다.
@WebServlet(name = "requestParamServlet", urlPatterns = "/request-param")
public class RequestParamServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
== 비즈니스 로직 작성 ==
// super.service(req, resp);
}
}
서블릿의 request와 response 객체를 활용하려면 다음과 같은 설정이 필요하다:
super.service(req, resp)
super.service(req, resp);는 부모 클래스인HttpServlet의 기본 동작을 호출하여 HTTP 요청 메서드(GET, POST 등)에 따라 적절한 메서드(doGet,doPost등)를 자동으로 실행하도록 하는 메서드이다. 이를 활용하면 HTTP 요청 처리 로직을 개별 메서드(doGet,doPost)에 분리하여 작성할 수 있어 코드의 가독성과 유지보수성이 높아진다. 또한, HTTP 프로토콜에 따른 기본 동작(예: 405 Method Not Allowed 처리, OPTIONS 요청 자동 응답 등)을 유지하면서, 공통적인 로직(예: 로깅, 인증 등)을service메서드에 추가하여 확장성을 확보할 수 있다. 즉,super.service(req, resp);는 기본 동작을 유지하면서도 커스터마이징이 가능하도록 유연한 설계를 지원한다.
GET요청을 처리하려면service()메서드에서super.service(req, resp)를 유지하고,doGet()메서드를 오버라이드하여 구현하면 된다. 이 방식은 부모 클래스인HttpServlet의 기본 동작을 활용하여 요청 메서드에 따라 적절한 메서드(doGet,doPost등)를 자동으로 호출하도록 한다. 이를 통해 HTTP 요청 분기를 직접 처리할 필요 없이 간결하게 구현할 수 있으며, 필요에 따라 다른 HTTP 메서드(POST,PUT등)를 추가로 오버라이드하여 유연하게 확장할 수 있다.@WebServlet(name = "helloServlet", urlPatterns = "/hello") public class HelloServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("HelloServlet.service"); super.service(req, resp); // 부모 } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("HelloServlet.doGet"); String username = req.getParameter("username"); System.out.println("username = " + username); resp.setContentType("text/plain"); resp.setCharacterEncoding("utf-8"); resp.getWriter().write("hello " + username); } }
@WebServletservice() 메서드HttpServlet 클래스를 상속받아야 한다.service() 메서드를 구현하여 요청과 응답을 처리한다.service() 메서드를 호출하고,request 객체와 응답을 위한 response 객체를 주입한다.HttpServletRequest vs ServletRequest
@Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { super.service(req, res); }HttpServlet을 상속받으면 service()메서드로 두 파라미터의 경우의 메서드로 오버라이드 가능하다. HttpServletRequest,Response는 ServletRequest,Response에 비해 Http통신에 더욱 특화된 기능을 가진다. 만약 ServletRexxx 를 파라미터로 받는 service()를 사용한다면 HttpServlet이 제공하는 다양한 HTTP 프로토콜 전용 메서드(doGet, doPost, getHeader, setStatus 등)를 사용할 수 없기 때문에, 다음과 같은 작업을 수작업으로 처리해야 한다.
위의 예시에서, HttpServletRequest와 HttpServletResponse를 파라미터로 받는 service 메서드 안에서 req를 통해 요청 메시지를 분석할 수 있다.
req 객체는 get 시리즈 메서드를 통해 요청 메시지의 다양한 부분을 파싱할 수 있도록 지원한다:
getParameter, getParameterNames: 요청 메시지의 쿼리 파라미터 접근.getMethod: 요청 메시지의 HTTP 메서드(GET, POST 등) 확인.getProtocol: 프로토콜 정보(HTTP/1.1, HTTP/2 등) 확인.이러한 메서드들을 활용하여 요청 메시지의 세부 내용을 간편하게 처리할 수 있다.
HTTP 요청 메세지를 통해 클라이언트에서 서버로 데이터를 전달하는 방법은 크게 3가지로 분류된다.
메시지 바디 없이, URL의 쿼리 파라미터에 데이터를 포함해서 전달 예) 검색, 필터, 페이징등에서 많이 사용하는 방식으로 ?로 시작해서 추가 파라미터 구분은 &로 한다.
http://localhost:8080/request-param?username=jkky&age=27
위와 같이 전송할 경우 request와 response가 생성되어 urlPatterns로 매핑된 클래스로 주입된다. 다음과 같이 서블릿을 이용하여 편리하게 쿼리 파라미터를 조회할 수 있게 된다.
String username = request.getParameter("username"); //단일 파라미터 조회
Enumeration<String> parameterNames = request.getParameterNames(); //파라미터 이름들
모두 조회
Map<String, String[]> parameterMap = request.getParameterMap(); //파라미터를 Map으로
조회
String[] usernames = request.getParameterValues("username"); //복수 파라미터 조회
http://localhost:8080/request-param?username=jkky&age=27?username=starlike
위와 같이 username이 두개일 경우 getParameter에서는 가장 먼저 나타난 파라미터를 가져올 수 있고, 만약 모두 가져오고 싶다면 getParamterValue로 가져온다.
HTML form을 사용하는 데이터 전송 방식은 주로 회원 가입, 상품 주문 등에서 사용된다. 이 방식에서는 Content-Type: application/x-www-form-urlencoded의 컨텐츠 타입을 사용해야 하며, 메시지 바디에 쿼리 파라미터 형식으로 데이터를 전달한다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/request-param" method="post">
username: <input type="text" name="username" />
age: <input type="text" name="age" />
<button type="submit">전송</button>
</form>
</body>
</html>
위와 같은 정적 리소스 html 파일을 구성하고 html form을 만들어준 뒤 action으로 submit시에 데이터를 전달할 uri를 설정한다.
위의 input 태그에 값을 입력하고 전송을 누르면 action에 연결된 uri로 post 요청이 데이터와 함께 간다.

html을 만들지 않고 POSTMAN을 통해서도 테스트해볼 수 있다.

application/x-www-form-urlencoded형식은 앞서 GET에서 살펴본 쿼리 파라미터 형식과 같다. 즉 폼 데이터 또한 조회는 getParameter로 진행한다. GET에서 URI에 쿼리 파라미터를 준 것과 동일한 파싱 로직을 가지고 있다.
단순하게, 요청메세지의 바디 데이터를 텍스트 데이터로 구성하는 것이다.
getInputStream과 StreamUtils.copyToString을 이용하여 파싱할 수 있다.
(코드 생략)
API에서 가장 많이 활용되는 표준급인 JSON형식이다. JSON은 파싱시에 보통 객체로 받게 된다. 그러므로 username과 age를 멤버로 받는 객체를 생성하고 활용해보자.
package hello.servlet.basic.request;
import com.fasterxml.jackson.databind.ObjectMapper;
import hello.servlet.basic.HelloData;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.util.StreamUtils;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@WebServlet(name="requestBodyJsonServlet", urlPatterns = "/request-body-json")
public class RequestBodyJsonServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletInputStream inputStream = req.getInputStream();
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
System.out.println("messageBody = " + messageBody);
HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);
System.out.println("helloData.username = " + helloData.getUsername());
System.out.println("helloData = " + helloData);
resp.getWriter().write("OK");
}
}
ObjectManager : 오브젝트 매니저는 jackson이라는 json파싱 라이브러리의 클래스이다 이 인스턴스는 json을 객체로 파싱해줄 수 있다.
과정 : req에서 스트림으로 json을 우선 가져온 후 String으로 변환하고 오브젝트 매니저의 readValue 메서드에 파싱을 위한 객체 클래스와 String으로 변환된 json 요청 데이터를 같이 주입받아 객체를 생성한다.
서블릿은 response 메시지를 구성하기 위해 다양한 편리한 기능들을 제공한다.
request의 경우, 데이터를 가져올 때는 getXXX 메서드를 사용해야 한다면, response의 경우에는 setXXX 메서드를 활용하여 응답을 적절히 구성해야 한다. 예를 들어, Content-Type, Character Encoding, Cookie 등을 설정할 때 setContentType(), setCharacterEncoding(), addCookie()와 같은 메서드를 사용할 수 있다.
서블릿에서 제공하는 Cookie 클래스를 사용하여 쿠키를 생성할 수 있으며, setXXX 메서드를 통해 다양한 설정을 추가할 수 있다 (예: setMaxAge()를 사용해 쿠키의 유효 기간 설정).
쿠키의 key-value를 구성하고 필요한 설정을 완료한 후, HttpServletResponse의 addCookie() 메서드를 사용하여 생성한 쿠키를 응답(response)에 실어 클라이언트로 전송할 수 있다.
private void cookie(HttpServletResponse response) {
//Set-Cookie: myCookie=good; Max-Age=600;
//response.setHeader("Set-Cookie", "myCookie=good; Max-Age=600");
Cookie cookie = new Cookie("myCookie", "good");
cookie.setMaxAge(600); //600초
response.addCookie(cookie);
}
private void redirect(HttpServletResponse resp) throws IOException {
// 방법 1.
// resp.setStatus(HttpServletResponse.SC_FOUND);
// resp.setHeader("Location", "/basic/hello-form.html");
// 방법 2.
resp.sendRedirect("/basic/hello-form.html");
}
서블릿에서 리다이렉션을 구현하는 두 가지 방법에 대해 소개한다.
첫 번째 방법은 resp.setStatus(HttpServletResponse.SC_FOUND)로 HTTP 상태 코드를 302로 설정하고, resp.setHeader("Location", "/basic/hello-form.html")로 리다이렉션 URL을 지정하여 클라이언트에 전달한다. 두 번째 방법인 resp.sendRedirect("/basic/hello-form.html")은 내부적으로 상태 코드 설정과 헤더 지정을 모두 처리해주는 편의 메서드로, 간결하고 가독성이 높아 일반적으로 권장된다. 두 방식 모두 클라이언트가 지정된 URL로 리다이렉트되도록 동작한다.