예전에는 톰캣같은 WAS를 설치하고, 그 위에 서블릿 코드를 올려서 실행했다. 하지만 이 과정은 매우 번거롭다.
스프링 부트는 톰캣을 내장하고 있으므로, 톰캣 설치 없이 편리하게 서블릿 코드를 실행할 수 있다.
스프링 부트 실행 클래스 위에 @ServletComponentScan
어노테이션을 사용해서 Servlet을 빈으로 등록하고 사용할 수 있게 한다.
@ServletComponentScan //서블릿 자동 등록 @SpringBootApplication
public class ServletApplication {
public static void main(String[] args) {
SpringApplication.run(ServletApplication.class, args);
}
}
단, 빈으로 등록해서 사용할 Servlet에는 @WebServlet
어노테이션을 붙여준다. name, urlPattern 속성을 이용해서 해당 서블릿에 이름과 URL을 매핑해준다.
@WebServlet(name = "helloServlet", urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}
서블릿에 매핑된 URL로 HTTP 요청을 보내면 서블릿 컨테이너는 service 메소드를 실행한다.
서블릿은 개발자가 HTTP 요청 메시지를 편리하게 사용할 수 있도록 개발자 대신에 HTTP 요청 메시지를 파싱한다. 그리고 그 결과를 HttpServletRequest
객체에 담아서 제공한다.
개발자는 HttpServletReqeust
를 사용하면 HTTP 요청 메시지에 담긴 정보들을 편리하게 조회하고 사용할 수 있다.
http://localhost:8080/request-header?username=hello
위의 URL로 HTTP 요청을 하면 아래와 같은 HTTP 요청 메시지가 Servlet으로 오게 된다.
GET /request-header?username=hello HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate, br
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
Cache-Control: max-age=0
Connection: keep-alive
Cookie: JSESSIONID=85CE3F8B7693D8DA7870CEAB850E7A33
Host: localhost:8080
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "macOS"
Servlet에서는 HttpServletRequest
객체를 사용해서 HTTP 요청 메시지 내부의 데이터를 사용할 수 있다.
http://localhost:8080/request-param?username=hello&age=20
위의 URL로 실행
@WebServlet(name = "helloServlet", urlPatterns = "/request-param")
public class HelloServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String age = request.getParameter("age");
}
}
http://localhost:8080/request-param
위의 URL로 Form Data 전송
@WebServlet(name = "helloServlet", urlPatterns = "/request-param")
public class HelloServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String age = request.getParameter("age");
}
}
http://localhost:8080/request-body-json
위의 URL로 JSON 데이터 전송({"username": "hello", "age": 20})
@Getter @Setter
public class HelloData {
private String username;
private int age;
}
@WebServlet(name = "requestBodyJsonServlet", urlPatterns = "/request-body-json")
public class RequestBodyJsonServlet extends HttpServlet {
ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletInputStream inputStream = request.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.age = " + helloData.getAge());
response.getWriter().write("OK");
}
}
HttpServletResponse
를 사용해서 클라이언트에 보내줄 응답 메시지, 응답 코드, 헤더, 바디 등을 설정할 수 있다.
Java(TM) EE 8 Specification APIs
@WebServlet(name = "responseHtmlServlet", urlPatterns = "/response-html")
public class ResponseHtmlServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
PrintWriter writer = response.getWriter();
writer.println("<html>");
writer.println("<body>");
writer.println("<div>");
writer.println("안녕?");
writer.println("</div>");
writer.println("</body>");
writer.println("</html>");
}
}
HTTP 응답으로 HTML을 반환할 때는 Content-Type을 text/html
로 지정해야 한다.
@WebServlet(name = "responseJsonServlet", urlPatterns = "/response-json")
public class ResponseJsonServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// Content-Type: application/json
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
HelloData helloData = new HelloData();
helloData.setUsername("cheoljin");
helloData.setAge(9999);
// {"username": "hello", "age": 20}
String result = objectMapper.writeValueAsString(helloData);
response.getWriter().write(result);
}
}
HTTP 응답으로 JSON을 반환할 때는 Content-Type을 application/json
로 지정해야 한다. Jackson 라이브러리가 제공하는 objectMapper.writeValueAsString()
를 사용하면 객체를 JSON 문자로 변경할 수 있다.