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로 구성.
  1. 클라이언트 요청 보냄
  2. 톰캣이 소켓을 통해 요청을 수신
  3. 요청 라인, 헤더, 바디를 파싱하고 이를 바탕으로 HttpServletRequest와 HttpServletResponse 객체를 생성
  4. 톰캣은 요청 URL(/request-header)에 등록된 서블릿을 찾고 그 서블릿의 service() 메서드를 호출
  5. 이 때 아까 만들어준 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);
    }
}