HTTP(HyperText Transfer Protocol)
- 클라이언트와 서버가 데이터를 주고받기 위해 사용하는, 정해진 구조와 절차의 약속 (프로토콜)
- 어떤 방식으로 요청을 보내야 하고, 응답은 어떤 구조로 줘야 하고 등의 프로토콜을 정한 표준화된 약속
- 이 약속을 따름으로써, 브라우저와 서버가, 앱과 서버가, 심지어 마이크로서비스끼리도 서로 이해할 수 있는 방식으로 통신할 수 있게 된다.
서블릿
- HTTP 요청이 들어오면, 서블릿 컨테이너가 request와 response 객체를 생성해 서블릿의 메서드에 전달하고, 서블릿은 response 객체에 응답 내용을 작성한다.
// name: 서블릿 이름, urlPatterns: ULR 매핑
@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");
System.out.println("request = " + request);
System.out.println("response = " + response);
// 쿼리 파라미터 조회 : http://localhost:8080/hello?username=seol
String username = request.getParameter("username");
System.out.println("username = " + username);
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
response.getWriter().write("hello + " + username);
}
}
HttpServletRequest
- 서블릿은 HTTP 요청 메시지를 편리하게 사용할 수 있도록 HTTP 요청 메시지를 파싱. 그 결과를 HttpServletRequest 객체에 담아서 제공. 웹 요청을 처리하는 모든 서블릿/컨트롤러의 입력 포탈인 것.
- HttpServletRequest 객체는 클라이언트의 HTTP 요청을 서블릿이 쉽게 접근할 수 있도록 가공해서 제공하는 API 객체
- 즉, 개발자는 이 객체를 통해 요청 메서드(GET/POST), URI, 헤더, 쿼리 파라미터, 바디 내용 등에 접근 가능
- Start Line, Header, Body로 구성.
- 클라이언트 요청 보냄
- 톰캣이 소켓을 통해 요청을 수신
- 요청 라인, 헤더, 바디를 파싱하고 이를 바탕으로 HttpServletRequest와 HttpServletResponse 객체를 생성
- 톰캣은 요청 URL(/request-header)에 등록된 서블릿을 찾고 그 서블릿의 service() 메서드를 호출
- 이 때 아까 만들어준 request, response 객체를 인자로 넘겨줌
@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);
printHeaderUtils(request);
printEtc(request);
}
....
}
HTTP 요청 데이터
GET /hello?name=taehyun HTTP/1.1 ← ✅ Start Line
Host: localhost:8080 ← ✅ Header
Content-Type: application/json
Content-Length: 48
{ "username": "taehyun", "age": 25 } ← ✅ Body (Message Body)
- 기본적으로 HTTP 요청 메시지는 위와 같이 생겼다.
- HTTP 요청 데이터는 URL의 쿼리 스트링(Query) 또는 메시지 바디(Body)에 담길 수 있다.
- GET 요청은 주로 쿼리 파라미터를 URL에 포함해서 전송한다. (예: /search?keyword=java&page=2). (검색, 필터 등)
- POST 요청은 데이터를 메시지 바디에 담아 전송하며, (회원 가입, 상품 주문 등)
- HTML Form의 경우 key=value 형식
- JSON, XML 등의 경우는 별도의 구조를 가진다.
- 메시지 바디는 클라이언트가 서버로 복잡한 구조의 데이터(JSON, XML 등)를 전송할 때 사용된다.
| 요청 형태 |
스프링에서 받는 방법 |
| URL 쿼리 |
@RequestParam |
| 폼 데이터 |
@RequestParam 또는 @ModelAttribute |
| JSON 바디 |
@RequestBody |
HTTP 요청 데이터 - GET 쿼리 파라미터
/**
* 1. 파라미터 전송 기능
* http://localhost:8080/request-param?username=hello&age=20
*/
@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("[전체 파라미터 조회] - start");
request.getParameterNames().asIterator()
.forEachRemaining(paramName -> System.out.println(paramName + " = " + request.getParameter(paramName)));
System.out.println("[전체 파라미터 조회] - end");
System.out.println();
// 첫 번째 username 값을 반환함
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("[이름이 같은 복수 파라미터 조회]");
String[] usernames = request.getParameterValues("username");
for (String name : usernames) {
System.out.println("username = " + name);
}
response.getWriter().write("yes");
}
}
HTTP 요청 데이터 - POST HTML Form
- content-type은 HTTP 메시지 바디의 데이터 형식을 지정한다.
- GET URL 쿼리 파라미터 형식으로 클라이언트에서 서버로 데이터를 전달할 때는 HTTP 메시지 바디를 사용하 지 않기 때문에 content-type이 없다.
- POST HTML Form 형식으로 데이터를 전달하면 HTTP 메시지 바디에 해당 데이터를 포함해서 보내기 때문에 바디에 포함된 데이터가 어떤 형식인지 content-type을 꼭 지정해야 한다.
- 이렇게 폼으로 데이터를 전송하는 형 식을 application/x-www-form-urlencoded라 한다.
<!DOCTYPE html>
<html>
<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>
HTTP 요청 데이터 - API 메시지 바디 - 단순 텍스트
@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");
}
}
HTTP 요청 데이터 - API 메시지 바디 - JSON
@Getter @Setter
public class HelloData {
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 request, HttpServletResponse response) throws ServletException, IOException {
ServletInputStream inputStream = request.getInputStream();
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
System.out.println("messageBody = " + messageBody);
// JSON 결과를 파싱해서 사용할 수 있는 자바 객체로 변환하려면, Jackson 같은 JSON 변환 라이브러리 필요
HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);
System.out.println("helloData.username = " + helloData.getUsername());
System.out.println("helloData.userAge = " + helloData.getAge());
response.getWriter().write("ok");
}
}
HttpServletResponse
- 서블릿에서 HTTP 응답을 생성할 때 사용하는 객체로, HTTP 응답의 상태 코드, 헤더, 메시지 바디를 설정할 수 있는 다양한 메서드를 제공
- 개발자는 이 객체를 통해 응답 내용을 선언적으로 정의하고, 서블릿 컨테이너는 이를 바탕으로 최종 HTTP 응답 메시지를 자동으로 생성하여 클라이언트에 전송
@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.println("ok");
}
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); //(생략시 자동 생성)
}
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 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");
}
}
HTTP 응답 데이터 - 단순 텍스트, 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
// HTTP 응답으로 HTML을 반환할 때는 content-type을 text/html로 설정해야 함.
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
PrintWriter writer = response.getWriter();
writer.println("<html>");
writer.println("<body>");
writer.println(" <div>안녕?</div>");
writer.println("</html>");
writer.println("</body>");
}
}
HTTP 응답 데이터 - API JSON(REST API)
@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 {
// HTTP 응답으로 JSON을 반환할 때는 content-type을 application/json으로 설정해야 함
// Content-Type: application/json
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
HelloData helloData = new HelloData();
helloData.setUsername("seol");
helloData.setAge(24);
// {"username": "kim", "age": 20}
String result = objectMapper.writeValueAsString(helloData);
response.getWriter().write(result);
}
}