suyeonme

[Java] Servlet이란? 본문

프로그래밍👩🏻‍💻/Java

[Java] Servlet이란?

suyeonme 2022. 10. 11. 21:34

Servlet이란?

클라이언트의 요청(request)을 처리하고 응답(response)을 반환하는 Servlet 클래스를 따라는 자바 프로그램이다.

Servlet 특징

  • HttpServletRequest로 HTTP 요청을 쉽게 다룰 수 있다. (query string, 메세지 바디 조회등)
  • HttpServletResponse로 HTTP 응답을 쉽게 다룰 수 있다. (status code, content-type, cookie, redirect등)
  • 개발자는 서블릿을 사용하여 HTTP를 쉽게 핸들링할 수 있다.

Servlet 예시 코드

@WebServlet(name = "requestParamServlet", urlPatterns = "/request-param")
public class RequestServlet extends HttpServlet {
	@Override
	protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException {
		//...
	}
}

Servlet Container

서블릿을 관리해주는 컨테이너로, 클라이언트의 요청을 받고 응답을 할 수 있도록 웹서버와 소켓으로 통신한다. 대표적으로 톰캣(Tomcat)이 있다. 

Servlet없이 직접 HTTP 요청을 구현한다면?

만약 Servlet없이 HTTP 요청을 직접 구현한다고 가정하면 아래의 단계를 거칠 것 이다.

Servlet은 복잡한 아래 과정을 대신 처리해준다.

  1. 서버에서 TCP/IP(or UDT) 연결을 대기하고 소켓을 연결한다.
  2. http 요청 메세지를 파싱한다. (Content-type 확인 및 Method등 확인, HTTP Body 파싱)
  3. 저장 프로세스를 실행한다.
  4. 비즈니스 로직을 실행한다. 
  5. HTTP 응답 메세지를 생성한다.(HTTP Header, Message Body 입력등)
  6. TCP/IP에 응답을 전달하고 소켓을 종료한다.

HttpServletRequest (요청)

HTTP 요청 파라미터를 조회한다.

1. Query String을 사용하는 경우

  • /request-param?username=suyeon&age=27
  • request.getParameter을 주로 사용한다.
  • HTML Form을 전송하는 경우 동일하게 동작한다.
    • content-type: application/x-www-form-urlencoded
    • 메세지 바디에 쿼리파라미터 형식으로 전달한다(username=suyeon&age=27)
@WebServlet(name = "requestParamServlet", urlPatterns = "/request-param")
public class RequestServlet extends HttpServlet {
	@Override
	protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException {
	
	// 전체 파라미터 조회	
	req.getParameterNames().asIterator()
			.forEachRemaining(paramName -> System.out.println(paramName + "=" + req.getParameter(paramName)));
	}

	// 개별 파라미터 조회
	String username = req.getParameter("username");

	// 응답 
	res.getWriter().write("ok");
}

2. POST의 메세지 바디를 사용하는 경우

  • HTTP 메세지 바디의 데이터를 inputStream으로 읽을 수 있다.
    • inputStream은 바이트 코드를 반환한다.
@WebServlet(name = "requestBodyStringServlet", urlPatterns = "/request-body-string")
public class RequestBodyStringServlet extends HttpServlet {
	@Override
	protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException {
		ServletInputStream inputStream = req.getInputStream(); // 바이트코드로 값을 얻음
		String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8); // 바이트코드를 문자열로 변환
		res.getWriter().write("ok");
	}		
}

3. JSON형식의 메세지 바디를 사용하는 경우

  • content-type: application/json
  • Jackson 라이브러리를 사용하여 JSON 데이터를 파싱한다.
    • Jackson 라이브러리는 스프링에 기본으로 내장되어있다.
    • JSON 변환 라이브러리
// 롬복 라이브러리 사용
@Getter @Setter
public class UserData {
	private String username;
	private int age;
}

@WebServlet(name = "requestBodyJsonServlet", urlPatterns = "/request-body-json")
public class RequestBodyJsonServlet extends HttpServlet {
	private ObjectMapper objectMapper = new ObjectMapper(); // 잭슨 라이브러리

	@Override
	protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException {
		ServletInputStream inputStream = req.getInputStream(); // 바이트코드로 값을 얻음
		String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8); // 바이트코드를 문자열로 변환
		UserData userData = objectMapper.readValue(messageBody, UserData.class); // JSON 데이터를 객체로 변환

		System.out.println(userData.getUsername());
		res.getWriter().write("ok");
	}		
}

HttpServletResponse (응답)

HTTP 헤더 설정 및 응답 메세지를 생성한다.

사용 예시

  • 텍스트 응답: res.getWriter().write("OK") 
@WebServlet(name = "responseHeaderServlet", urlPatterns = "/response-header")
public class responseHeaderServlet extends HttpServlet {
	@Override
	protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException {
		// [status-line]
		res.setStatus(HttpServletResponse.SC_OK); // 200

		// [response-headers];
		res.setHeader("Content-Type", "text/plain");
		// res.setContentType("text/plain"); 위와 동일
		res.setCharacterEncoding("utf-8");
		res.setHeader("Cache-Control", "no-cache", "no-store", "must-revalidate");
		res.setHeader("Pragma", "no-cache");

		// [cookie]
		Cookie cookie = new Cookie("testCookie", "good");
		coockie.setMaxAge(600);
		res.addCookie(cookie);

		// [redirect]
		res.sendRedirect("/test/index.html")

		// [message body]
		res.getWriter().write("ok");
	}		
}

1. HTML 응답

  • content-type: text/html
  • HTML 코드를 아래와 같이 직접 작성하는 것은 불편하다. 따라서 템플릿 엔진을 사용하여 동적으로 HTML을 작성할 수 있다.
    • JSP, velocity, thymeleaf, freemarker등
@WebServlet(name = "responseHtmlServlet", urlPatterns = "/response-html")
public class ResponseHtmlServlet extends HttpServlet {

	@Override
	protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException {
		res.setContentType("text/html");
		res.setCharacterEncoding("utf-8");
		PrintWriter writer = res.getWriter();
		writer.println("<html>");
		writer.println("<body>");
		writer.println("<div>Hello World</div>");
		writer.println("</body>");
		writer.println("</html>");
	}		
}

2. JSON 응답

  • content-type: application/json
  • 잭슨 라이브러리의 objectMapper.writeValueAsString 를 사용하여 객체를 JSON으로 변환한다.
@WebServlet(name = "responseJsonServlet", urlPatterns = "/response-json")
public class ResponseJsonServlet extends HttpServlet {
	private ObjectMapper objectMapper = new ObjectMapper(); // 잭슨 라이브러리

	@Override
	protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException {
		res.setContentType("application/json");
		res.setCharacterEncoding("utf-8");

		UserData userData = new UserData();
		userData.setUsername("suyeon");
		userData.age(27);

		// JSON으로 변환
		String result = objectMapper.writeValueAsString(userData);

		res.getWriter().write(result);
	}		
}
Comments