
서블릿이란
자바를 이용하여 웹 페이지를 생성하는 개념으로 HTML을 이용하여 클라이언트에게 데이터를 전송하고 데이터베이스와 상호작용하고 로직 처리 등 복잡한 작업을 수행할 수 있다
서블릿은 크게 아래 순서의 작동 원리를 갖는다
1. 클라이언트 요청: 웹 브라우저가 HTTP요청을 서블릿에 전송
2. 웹 서버: 요청을 받은 웹 서버는 해당 요청을 처리할 서블릿을 찾아서 호출
3. 서블릿 실행: 서블릿은 요청을 처리한 후, 결과를 HTTP응답 형태로 웹 서버에 전달
4. 응답 전송: 웹 서버는 서블릿으로부터 받은 응답을 클라이언트에게 전송
서블릿의 편리성
Servlet이 개발자에게 어떤 편리성을 주는지 알아보자
아래와 같이 username을 브라우저로부터 입력받아서 post방식으로 서버에 요청하는 HTML FORM이 있다
'전송' 버튼을 클릭하는 순간 서버는 웹 브라우저가 생성한 복잡한 HTTP요청 메시지를 파싱해서 읽고 method는 무엇인지 파악하고 쿼리 파라미터(username) 데이터 또한 사용할 수 있도록 파싱하고, 개발자가 원하는 비즈니스 로직을 실행한 뒤, HTTP 응답 메시지를 생성하고 해당 응답 메시지를 브라우저에게 응답한다.
Servlet이 없다면 위와 같은 복잡한 과정들을 개발자가 개발할 때 직접 코드로 일일이 설계해서 수행할 수 있도록 해야한다.
Servlet을 사용함으로써 모든 과정을 Servlet이 처리하도록 하고 개발자는 요청 HTTP메시지로부터 처리하고자 하는 비즈니스 로직만 실행하도록 하면 된다
보통 이러한 서블릿은 Tomcat과 같이 WAS에서 서블릿 컨테이너를 통해 제공해주며 서블릿 컨테이너는 요청된 서블릿 객체의 생성, 초기화, 종료와 같은 생명주기까지 직접 관리해주며, 동시 요청을 위한 멀티 쓰레드 처리 또한 지원해준다
HttpServlet 호출
@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");
}
}
name속성은 servlet을 식별하기위한 속성이다
urlPatterns는 해당하는 url요청이 입력되었을 때, 해당 서블릿을 호출하도록 한다.
웹 애플리케이션 서버를 실행한 뒤, /hello url을 요청하면 아래와 같이 urlPatter에 매핑되는 서블릿을 호출하는 것을 확인할 수 있다
HTTP 헤더 / 바디 정보 조회
위에서 살펴본 service메서드의 파라미터로 HttpServletRequest를 받는 것을 확인할 수 있는데 이 객체를 사용하여 HTTP요청 메시지를 편리하게 조회할 수 있도록 하고
req.setAttribute(name, value) , req.getAttribute(name) 메서드를 통하여 요청을 통해 받은 데이터를 임시로 저장하고 조회할 수 있도록 하는 메서드를 제공한다.
@WebServlet(name = "helloServlet", urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//"username"이라는 쿼리 파라미터의 밸류 값 조회
String username = req.getParameter("username");
System.out.println("username = " + username);
//HTTP method가 어떤 요청인지 조회 (POST,GET 등등)
String method = req.getMethod();
System.out.println("method = " + method);
//도메인을 포함한 URL전체를 조회
StringBuffer requestURL = req.getRequestURL();
System.out.println("requestURL = " + requestURL);
//도메인을 제외한 요청 URI만 조회 (/hello)
String requestURI = req.getRequestURI();
System.out.println("requestURI = " + requestURI);
}
}
위 결과 외에도 더 많은 HTTP 헤더/바디의 정보들을 조회할 수 있다
HTTP 요청
HTTP 요청 메시지를 통하여 클라이언트에서 서버로 데이터를 전달하는 방법에는 크게 3가지가 있다
1. GET - 쿼리 파라미터
2. POST - HTML Form
3. HTTP message body
GET - 쿼리 파라미터
쿼리 파라미터는 URL에서 도메인 뒷 부분에 나와있는 키-값 형태의 데이터를 쿼리파라미터 라고한다
https://search.naver.com/search.naver?query=축구
URL검색창에서 위와 같이 입력하면 서버는 해당 문자열을 파싱하여 해석한다
지금은 ?의 뒷 부분만 살펴보면 되는데 ?의 앞부분은 도메인 영역이고 ?의 뒷부분이 바로 쿼리 파라미터의 영역으로
=을 기준으로 query는 키값, 축구는 value이다
쿼리 파라미터는 &으로 한 번에 여러개의 쿼리파라미터를 요청할 수 있으며 서버에서는 여러 개의 쿼리파라미터를 처리할 때 배열이나 컬렉션으로 해당 데이터를 처리할 수 있다
@WebServlet(name = "helloServlet", urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
int age = Integer.parseInt(req.getParameter("age"));
System.out.println("name = " + name);
System.out.println("age = " + age);
}
}
위의 코드에서 "name"이라는 키의 값을 name변수에 저장하고, "age"의 키의 값을 age변수에 저장한다.
이 때 쿼리 파라미터의 값은 String타입으로 가져오기 때문에 age와 같은 숫자를 받을 때는 파싱을 해주어야한다
GET 쿼리파라미터 방식은 message body없이 URL의 쿼리 파라미터에 데이터를 포함해서 전달하며
주로 검색, 필터, 페이징등에서 많이 사용된며 GET방식은 요청을 받은 데이터를 JS를 통해서 정적 페이지에서 데이터를 출력할 수 있다
회원가입이나 로그인 시에 GET방식을 사용하게 된다면 URL에 회원의 개인정보가 그대로 노출되기 때문에 보안상에 위험이 있기 때문에 사용되지 않는다
POST - HTML FORM
POST방식 중 HTML의 FORM태그로 데이터를 요청할 경우에는 GET방식과 마찬가지로 쿼리 파라미터 형식으로 데이터를 키-값형태로 파싱하지만, GET방식에서는 URL에 데이터를 전달하였다면 POST메서드에서는 메시지 바디에 쿼리 파라미터 형식으로 데이터를 전달하게 된다 따라서 아래와 같이 FORM태그에 데이터를 입력할 경우 name=hong&age=14 형태로 데이터가 message body에 입력되어 서버에게 전달된다.
아래와 같이 결과를 확인할 수 있으며 아래에서 test2.html에 에러가 발생한 이유는 405에러인데 정적 페이지인 html에서는 POST로 전달받은 데이터를 처리할 수 없기 때문이다.
이 때는 서버사이드 랜더링, 클라이언트 사이드 랜더링을 통해서 데이터를 보여줄 수 있는데 주로 JSP, Thymeleaf, AJAX등의 기술이 사용된다.
POST방식은 GET방식과는 다르게 message body에 데이터를 넣어 전송하기 때문에 message body가 생성된다
content-type: application/x-www-form-urlencoded
message body: name=hong&age=14
POST - HTML Form방식은 주로 회원 가입, 상품 주문에서 사용된다
Postman을 사용하면 POST나 PUT, PATCH요청을 Form태그를 생성하지 않고도 쉽게 테스트할 수 있다
HTTP message body - JSON
HTTP API에서 주로 사용되며 서버와 서버간의 통신, 모바일 앱에서 서버로 요청, React나 Vue에서 서버로 통신할 때 사용된다
데이터 형식으로는 JSON, XML이 사용되며 최근에는 JSON이 표준화 되었다.
Postman을 통하여 JSON형식의 데이터 요청을 테스트해보도록 하겠습니다
먼저 위의 그림과 같이 URL을 내가 전송하고자하는 URL로 설정하고 method를 POST로 설정해주고 아래의 키값중에서 Content-Type을 application/json으로 설정되었는지 확인합니다
메시지 타입을 raw로 설정하고 JSON으로 변경해준 뒤, 데이터 입력란에 JSON형식으로 원하는 데이터를 입력하여준 뒤 SEND를 통해 데이터를 전송합니다
그럼 위와 같이 JSON형식으로 요청 데이터를 받은 것을 확인할 수 있습니다
@WebServlet(name = "helloServlet", urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//바이트 코드로 데이터를 읽어옴
ServletInputStream inputStream = req.getInputStream();
//바이트 코드를 문자열로 변환 (UTF-8)
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
System.out.println("messageBody = " + messageBody);
}
}
JSON데이터를 콘솔에 출력하기위한 코드로 StreamUtils는 스프링에서 제공해주는 바이트 코드를 String타입으로 변환해주는 기능을 제공합니다
아래 코드는 JSON형식으로 요청 받은 데이터를 잭슨 라이브러리를 이용하여 파싱한 데이터를 출력하는 코드입니다
import com.fasterxml.jackson.databind.ObjectMapper;
@WebServlet(name = "helloServlet", urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//바이트 코드로 데이터를 읽어옴
ServletInputStream inputStream = req.getInputStream();
//바이트 코드를 문자열로 변환 (UTF-8)
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
User user = objectMapper.readValue(messageBody, User.class);
System.out.println("messageBody = " + messageBody);
System.out.println("user.name = " + user.getName());
System.out.println("user.age = " + user.getAge());
}
}
자바스크립트로 JSON데이터 형식을 처리하는 방법 첨부
JSON으로 데이터를 요청하기위해서는 자바스크립트나 다른 기술을 사용해야하므로 Postman을 이용하여 JSON데이터 형식을 전송하고 확인해보도록 하겠습니다. 아래는 ajax를 통해 JSON형식의 데이터를 가져오는 코드입니다
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<form id="myForm" action="/hello" method="post">
name: <input type="text" name="name" />
age: <input type="text" name="age">
<button type="submit">전송</button>
</form>
<!-- JavaScript를 여기에 추가 -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
// JavaScript 코드
</script>
</body>
</html>
$(document).ready(function() {
$('#myForm').submit(function(e) {
e.preventDefault(); // 폼 기본 전송을 방지
var formData = {
name: $('input[name="name"]').val(),
age: $('input[name="age"]').val()
};
$.ajax({
type: "POST",
url: "/hello",
contentType: "application/json",
data: JSON.stringify(formData),
success: function(response) {
console.log("서버로부터 응답: ", response);
},
error: function(xhr, status, error) {
console.error("에러 발생: ", error);
}
});
});
});
HTTP 응답
HTTP 응답 데이터 - HTML
클라이언트(브라우저)에서 서버로부터 HTML을 응답 데이터로 받는 것이다
@WebServlet(name = "responseTest", urlPatterns = "/response-test")
public class ResponseTest extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//HTTP Header의 ContentType을 html로 설정하여 데이터 형식을 지정
resp.setContentType("text/html");
//인코딩 타입을 utf-8로 지정, 지정하지않을 경우 한글이 깨지는 현상이 나타남
resp.setCharacterEncoding("utf-8");
PrintWriter writer = resp.getWriter();
writer.println("<html>");
writer.println("<body>");
writer.println("<div>hello</div>");
writer.println("</body>");
writer.println("</html>");
}
}
위의 코드를 실행하여 지정된 url패턴으로 들어가보면 아래와 같은 실행결과를 확인할 수 있다
위의 코드를 통해서 알 수 있듯이 서블릿에서 HTML코드를 응답시켜주면 자바코드를 통해 동적인 HTML코드를 제공할 수 있지만 오탈자를 찾기힘들뿐더러 작성도 힘들다
이러한 불편함을 해소해주는 템플릿이 JSP, Thymeleaf이다
HTTP 응답 데이터 - JSON
HTTP요청에서 JSON데이터를 처리한 것과 반대로 처리하면 된다
@WebServlet(name = "responseTest", urlPatterns = "/response-test")
public class ResponseTest extends HttpServlet {
ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("content-type", "application/json");
resp.setCharacterEncoding("utf-8");
User user = new User();
user.setName("hong");
user.setAge(20);
String result = objectMapper.writeValueAsString(user);
resp.getWriter().write(result);
}
}
setHeader로 컨텐트 타입을 명시하여 지정할 수 있으며 이전에 HTML응답에서 사용하였듯이 setContentType으로도 컨텐트 타입을 명시할 수 있다
'CS > HTTP' 카테고리의 다른 글
JSP와 JSP가 사장되는 이유 + MVC패턴을 사용하는 이유 (0) | 2024.04.09 |
---|---|
웹 서버와 웹 애플리케이션 서버(WAS) (1) | 2024.04.07 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!