1. 서블릿(Servlet)이란
자바를 사용하여 웹페이지를 동적으로 생성하는 서버측 프로그램
HTML안에 자바코드가 포함된 JSP와는 달리 자바 코드안에 HTML코드가 포함되어 있다.
출처 : https://ko.wikipedia.org/wiki/%EC%9E%90%EB%B0%94_%EC%84%9C%EB%B8%94%EB%A6%BF
2. HttpServlet
- HttpServlet을 상속받아 service()메서드를 오버라이딩 하면
- HTTP요청을 통해 매핑된 URL이 호출되면 서블릿 컨테이너는 service()를 실행한다.
@WebServlet(name = "helloServlet", urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("HelloServlet.service 호출 : " + LocalDateTime.now());
System.out.println("request = " + request);
System.out.println("response = " + response);
String username = request.getParameter("username");
System.out.println("username = " + username);
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
response.getWriter().write("hello " + username);
}
}
위의 로직에서 아래와 같이 호출을 하면
이러한 로그가 찍히는 것을 확인할 수 있다.
- HTTP요청 메시지를 보고 싶다면
- application.properties에서 logging.level.org.apache.coyote.http11=debug을 설정해주면 된다.
- 하지만 운영단계에서 사용하면 로그로 인한 성능 저하가 생길 수 있기 때문에 개발할 때만 사용하자.)
3. HttpServletRequest
- HttpServlet은 HTTP요청 메시지를 개발자가 사용하기 편리하도록 파싱해서 HttpServletRequest 객체에 담아서 준다.
3.1 헤더 정보 알아보기
@WebServlet(name="requestHeaderServlet", urlPatterns = "/request-header")
public class RequestHeaderServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
printStartLine(request);
printHeaders(request);
}
private void printStartLine(HttpServletRequest request) {
System.out.println("---REQUEST-LINE - start ---");
System.out.println("request.getMethod() = " + request.getMethod()); //GET
System.out.println("request.getProtocal() = " + request.getProtocol()); //HTTP/1.1
System.out.println("request.getScheme() = " + request.getScheme()); //http
// http://localhost:8080/request-header
System.out.println("request.getRequestURL() = " + request.getRequestURL());// /request-test
System.out.println("request.getRequestURI() = " + request.getRequestURI());//username=hi
System.out.println("request.getQueryString() = " + request.getQueryString());
System.out.println("request.isSecure() = " + request.isSecure()); //https사용 유무
System.out.println("--- REQUEST-LINE - end ---");
System.out.println();
}
private void printHeaders(HttpServletRequest request) {
System.out.println("--- Headers - start ---");
// Enumeration<String> headerNames = request.getHeaderNames();
// while (headerNames.hasMoreElements()) {
// String headerName = headerNames.nextElement();
// System.out.println(headerName + " : " + headerName);
// }
request.getHeaderNames().asIterator()
.forEachRemaining(headerName -> System.out.println(headerName + " : " + request.getHeader(headerName)));
System.out.println("--- Headers - end ---");
}
}
3.2 헤더 편의 기능 조회
//Header 편리한 조회
private void printHeaderUtils(HttpServletRequest request) {
System.out.println("--- Header 편의 조회 start ---");
System.out.println("[Host 편의 조회]");
System.out.println("request.getServerName() = " + request.getServerName()); //Host 헤더
System.out.println("request.getServerPort() = " + request.getServerPort()); //Host 헤더 System.out.println();
System.out.println("[Accept-Language 편의 조회]");
request.getLocales().asIterator().forEachRemaining(locale -> System.out.println("locale = " +locale));
System.out.println("request.getLocale() = " + request.getLocale());
System.out.println();
System.out.println("[cookie 편의 조회]");
if(request.getCookies() != null) {
for (Cookie cookie : request.getCookies()) {
System.out.println(cookie.getName() + ": " + cookie.getValue());
}
}
System.out.println();
System.out.println("[Content 편의 조회]");
System.out.println("request.getContentType() = " +
request.getContentType());
System.out.println("request.getContentLength() = " +
request.getContentLength());
System.out.println("request.getCharacterEncoding() = " +
request.getCharacterEncoding());
System.out.println("--- Header 편의 조회 end ---");
System.out.println();
}
3.3 부가 기능
- 임시 저장소 기능 - HTTP요청이 시작할 때부터 끝날 때까지 유지되는 저장
- 저장 : request.setAttribute(name, value)
- 조회 : request.getAttribute(name)
- 세션 관리 기능
- request.getSession()
4. HttpServletResponse
4.1 HTTP 응답 메시지 생성하기
- HTTP 응답코드를 지정하고 헤더를 생성, body에 데이터를 쓴다.
@WebServlet(name = "responseHeaderServlet", urlPatterns = "/response-header")
public class ResponseHeaderServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// [status-line]
response.setStatus(HttpServletResponse.SC_OK);
// [response-header]
response.setHeader("Content-Type", "text/plain;charset=utf-8");
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
response.setHeader("Pragma", "no-cache");
response.setHeader("my-header", "hello");
// [ Header 편의 메서드 ]
content(response);
cookie(response);
redirect(response);
// [ message body ]
PrintWriter writer = response.getWriter();
writer.write("OK");
}
}
4.2 HTTP 응답 편의 기능
- Content-Type
private void content(HttpServletResponse response) {
//Content-Type: text/plain;charset=utf-8
//Content-Length: 2
//response.setHeader("Content-Type", "text/plain;charset=utf-8");
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
//response.setContentLength(2); //(생략시 자동 생성)
}
- Cookie
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);
}
- Redirect
private void redirect(HttpServletResponse response) throws IOException {
//Status Code 302
//Location: /basic/hello-form.html
//response.setStatus(HttpServletResponse.SC_FOUND); //302
//response.setHeader("Location", "/basic/hello-form.html");
response.sendRedirect("/basic/hello-form.html");
}
5. HTTP 요청 데이터와 응답 데이터
5.1 HTTP 요청 데이터
5.1.1 쿼리 파라미터
- HTTP 메서드는 GET
- 쿼리 파라미터에 데이터를 포함시켜서 전달한다.(검색, 정렬, 페이징 정보 등)
- http://localhost:8080/request-param?username=hyunbin&age=100
//단일 파라미터 조회
String username = request.getParameter("username");
//파라미터 이름들 모두 조회
Enumeration<String> parameterNames = request.getParameterNames();
//파라미터를 Map 으로 조회
Map<String, String[]> parameterMap = request.getParameterMap();
//복수 파라미터 조회
String[] usernames = request.getParameterValues("username");
/**
* 파라미터 전송 기능
* http://localhost:8080/request-param?username=hyunbin&age=100
*/
@WebServlet(name="requestParamServlet", urlPatterns = "/request-param")
public class RequestParamServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("[ 전체 파라미터 조회 - 시작]");
request.getParameterNames().asIterator()
.forEachRemaining(paramName -> System.out.println(paramName + " : " + request.getParameter(paramName)));
System.out.println("[ 전체 파라미터 조회 - 종료]");
System.out.println();
System.out.println("[ 단일 파라미터 조회 - 시작]");
String username = request.getParameter("username");
String age = request.getParameter("age");
System.out.println("username : " + username);
System.out.println("age : " + age);
System.out.println("[ 단일 파라미터 조회 - 종료]");
System.out.println();
System.out.println("[ 이름이 같은 복수 파라미터 조회 - 시작 ]");
String[] usernames = request.getParameterValues("username");
for (String name : usernames) {
System.out.println("username : " + name);
}
System.out.println("[ 이름이 같은 복수 파라미터 조회 - 종료 ]");
}
}
5.1.2 HTML Form
- HTTP 메서드는 POST
- content-type : application/x-www-form-urlencoded
- messageBody에 쿼리 파라미터 형식으로 데이터를 전달한다. ➡️ 전달하는 형식이 GET의 쿼리 파라미터 형식과 같기 때문에 쿼리 파라미터 조회 메서드를 그대로 사용가능하다.
5.1.3 HTTP messageBody
- HTTP messageBody에 데이터를 담아서 전달
- HTTP 메서드는 POST, PUT, PATCH
- 데이터의 형식은 JSON, XML, TEXT 등이 있고 JSON형식을 주로 사용한다.
- TEXT
@WebServlet(name="requestBodyStringServlet", urlPatterns = "/request-body-string")
public class RequestBodyStringServlet extends HttpServlet {
@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);
response.getWriter().write("OK");
}
}
- JSON
@WebServlet(name = "requestBodyJsonServlet", urlPatterns = "/request-body-json")
public class RequestBodyJsonServlet extends HttpServlet {
private ObjectMapper obejctMapper = 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);
System.out.println();
// 객체로 변환
HelloData helloData = obejctMapper.readValue(messageBody, HelloData.class);
System.out.println("helloData.getUsername() : " + helloData.getUsername());
System.out.println("helloData.getAge() = " + helloData.getAge());
response.getWriter().write("OK");
}
}
5.2 HTTP 응답 데이터
5.2.1 TEXT
PrintWriter writer = response.getWriter();
writer.write("OK");
5.2.2 HTML
@WebServlet(name = "responseHtmlServlet", urlPatterns = "/response-html")
public class ResponseHtmlServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// Content-Type: text/html;charset=utf-8
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
PrintWriter writer = response.getWriter();
writer.println("<html>");
writer.println("<body>");
writer.println(" <div>안녕하세요</div>");
writer.println("</body>");
writer.println("</html>");
}
}
5.2.3 JSON
@WebServlet(name = "responseJsonServlet", urlPatterns = "/response-json")
public class ResponseJsonServlet extends HttpServlet {
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("hello", 10);
String result = objectMapper.writeValueAsString(helloData);
response.getWriter().write(result);
}
}
6. 서블릿으로 동적으로 동작하는 응답페이지 만들기
- 회원가입 페이지
@WebServlet(name = "memberFormServlet", urlPatterns = "/servlet/members/new-form")
public class MemberFormServlet extends HttpServlet {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
PrintWriter w = response.getWriter();
w.write("<!DOCTYPE html>\n" +
"<html>\n" +
"<head>\n" +
" <meta charset=\"UTF-8\">\n" +
" <title>Title</title>\n" +
"</head>\n" +
"<body>\n" +
"<form action=\"/servlet/members/save\" method=\"post\">\n" +
" username: <input type=\"text\" name=\"username\" />\n" +
" age: <input type=\"text\" name=\"age\" />\n" +
" <button type=\"submit\">전송</button>\n" +
"</form>\n" +
"</body>\n" +
"</html>\n");
}
}
- 회원가입 처리 페이지
@WebServlet(name = "memberSaveServlet", urlPatterns = "/servlet/members/save")
public class MemberSaveServlet extends HttpServlet {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("MemberSaveServlet.service");
String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age"));
Member member = new Member(username, age);
memberRepository.save(member);
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
PrintWriter w = response.getWriter();
w.write("<html>\n" +
"<head>\n" +
" <meta charset=\"UTF-8\">\n" +
"</head>\n" +
"<body>\n" +
"성공\n" +
"<ul>\n" +
" <li>id="+member.getId()+"</li>\n" +
" <li>username="+member.getUsername()+"</li>\n" +
" <li>age="+member.getAge()+"</li>\n" +
"</ul>\n" +
"<a href=\"/index.html\">메인</a>\n" +
"</body>\n" +
"</html>");
}
}
서블릿을 이용하여 정적인 웹 페이지가 아니라 동적으로 동작하는 웹 페이지를 만들었다.
근데... 직접 코드를 짜지 않고 보기만 해도 말도 안되게 불편하고 비효율적이고 오류가 날 확률이 많아 보인다.
그래서 필요한 것이 우리가 흔히 아는 JSP, Thymeleaf, mustache과 같은 템플릿 엔진이다.
이러한 템플릿 엔진을 사용하여 우리가 원하는 부분에만 동적으로 변경되는 웹 페이지를 만들 수 있다.
'Spring > Spring Framework' 카테고리의 다른 글
[Spring Framework] 웹 애플리케이션의 이해 (0) | 2023.05.30 |
---|---|
[Spring Framework] 의존관계 주입(Dependency Injection) (0) | 2023.04.24 |
[Spring Framework] @ComponentScan (0) | 2023.04.10 |
[Spring Framework] 스프링 컨테이너와 싱글톤 컨테이너 (0) | 2023.04.10 |
[Spring Framework] 스프링 컨테이너와 스프링 빈 (0) | 2023.04.06 |