Spring/스프링 MVC 1편 / / 2023. 1. 18. 11:47

2. 서블릿(3)

목차

HTTP 요청 데이터 - POST HTML Form
HTTP 요청 데이터 - API 메시지 바디 - 단순 텍스트
HTTP 요청 데이터 - API 메시지 바디 - JSON
HttpServletResponse - 기본 사용법
HTTP 응답 데이터 - 단순 텍스트, HTML
HTTP 응답 데이터 - API JSON


HTTP 요청 데이터 - POST HTML Form

이번에는 HTML의 Form을 사용해서 클라이언트에서 서버로 데이터를 전송해보자.

주로 회원 가입, 상품 주문 등에서 사용하는 방식이다.

특징

  • content-type:  application/x-www-form-urlencoded
  • 메시지 바디에 쿼리 파리미터 형식  로 데이터를 전달한다. username=hello&age=20

src/main/webapp/basic/hello-form.html 생성

<!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://localhost:8080/basic/hello-form.html

결과

 

POST의 HTML Form을 전송하면 웹 브라우저는 다음 형식으로 HTTP 메시지를 만든다. (웹 브라우저 개발자

모드 확인)

  • 요청 URL: http://localhost:8080/request-param
  • content-type: application/x-www-form-urlencoded
  • message body: username=hello&age=20

application/x-www-form-urlencoded 형식은 앞서 GET에서 살펴본 쿼리 파라미터 형식과 같다.

따라서 쿼리 파라미터 조회 메서드를 그대로 사용하면 된다.

클라이언트(웹 브라우저) 입장에서는 두 방식에 차이가 있지만, 서버 입장에서는 둘의 형식이 동일하므로,
request.getParameter() 로 편리하게 구분없이 조회할 수 있다.

 

정리하면  request.getParameter() 는 GET URL 쿼리 파라미터 형식도 지원하고, POST HTML Form

형식도 둘 다 지원한다.

> 참고
content-type은 HTTP 메시지 바디의 데이터 형식을 지정한다.

GET URL 쿼리 파라미터 형식으로 클라이언트에서 서버로 데이터를 전달할 때는 HTTP 메시지 바디를 사용

하지 않기 때문에 content-type이 없다.

POST HTML Form 형식으로 데이터를 전달하면 HTTP 메시지 바디에 해당 데이터를 포함해서 보내기 때문

에 바디에 포함된 데이터가 어떤 형식인지 content-type을 꼭 지정해야 한다. 이렇게 폼  로 데이터를

전송하는 형식을 application/x-www-form-urlencoded 라 한다.

Postman을 사용한 테스트

이런 간단한 테스트에 HTML form을 만들기는 귀찮다. 이때는 Postman을 사용하면 된다.

 

Postman 테스트 주의사항

POST 전송시

  • Body -> x-www-form-urlencoded 선택
  • Headers에서 content-type: application/x-www-form-urlencoded 로 지정된 부분 꼭 확인

HTTP 요청 데이터 - API 메시지 바디 - 단순 텍스트

HTTP message body에 데이터를 직접 담아서 요청

  • HTTP API에서 주로 사용, JSON, XML, TEXT
  • 데이터 형식은 주로 JSON 사용
  • POST, PUT, PATCH

먼저 가장 단순한 텍스트 메시지를 HTTP 메시지 바디에 담아서 전송하고, 읽어보자. 

HTTP 메시지 바디의 데이터를 InputStream을 사용해서 직접 읽을 수 있다.

 

RequestBodyStringServlet

package hello.servlet.basic.request;

import org.springframework.util.StreamUtils;

import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

@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");
    }
}

 

Postman을 사용해서 테스트 해보자.

> 참고

inputStream은 byte 코드를 반환한다. byte 코드를 우리가 읽을 수 있는 문자(String)로 보려면 문자표 (Charset)를 지정해주어야 한다. 여기서는 UTF_8 Charset을 지정해주었다.

문자 전송

  • POST http://localhost:8080/request-body-string
  • content-type: text/plain
  • message body: hello
  • 결과: messageBody = hello

HTTP 요청 데이터 - API 메시지 바디 - JSON

이번에는 HTTP API에서 주로 사용하는 JSON 형식으로 데이터를 전달해보자.

 

JSON 형식 전송

  • POST http://localhost:8080/request-body-json 
  • content-type: application/json
  • message body: {"username": "hello", "age": 20}
  • 결과: messageBody = {"username": "hello", "age": 20}

JSON 형식 파싱 추가

JSON 형식으로 파싱할 수 있게 객체를 하나 생성하자

 

hello.servlet.basic.HelloData

package hello.servlet.basic;

import lombok.Getter;
import lombok.Setter;

@Getter @Setter
public class HelloData {

    private String username;
    private int age;
    
}

 

lombok이 제공하는  @Getter , @Setter 덕분에 다음 코드가 자동으로 추가된다. (눈에 보이지는 않는다.)

 

RequestBodyJsonServlet

package hello.servlet.basic.request;

import com.fasterxml.jackson.databind.ObjectMapper;
import hello.servlet.basic.HelloData;
import org.springframework.util.StreamUtils;

import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

@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);

        HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);

        System.out.println("helloData.username = " + helloData.getUsername());
        System.out.println("helloData.age = " + helloData.getAge());

        response.getWriter().write("ok");
    }
}

 

Postman으로 실행해보자.

  • POST  http://localhost:8080/request-body-json
  • content-type: application/json (Body raw, 가장 오른 에서 JSON 선택)
  •  message body: {"username": "hello", "age": 20}

 

> 참고

JSON 결과를 파싱해서 사용할 수 있는 자바 객체로 변환하려면 Jackson, Gson 같은 JSON 변환 라이브러리

를 추가해서 사용해야 한다. 스프링 부트로 Spring MVC를 선택하면 기본으로 Jackson 라이브러리 (ObjectMapper )를 함께 제공한다.

> 참고

HTML form 데이터도 메시지 바디를 통해 전송되므로 직접 읽을 수 있다. 하지만 편리한 파리미터 조회 기능(request.getParameter(...) )을 이미 제공하기 때문에 파라미터 조회 기능을 사용하면 된다.

HttpServletResponse - 기본 사용법

HttpServletResponse 역할
HTTP 응답 메시지 생성 

  • HTTP 응답코드 지정
  • 헤더 생성
  • 바디 생성

편의 기능 제공
Content-Type, 쿠키, Redirect

 

HttpServletResponse - 기본 사용법

hello.servlet.basic.response.ResponseHeaderServlet

package hello.servlet.basic.response;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * http://localhost:8080/response-header
 */
@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);  // 200

        // [response-headers]
        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);


        PrintWriter writer = response.getWriter();
        writer.println("ok");
    }
}

 

Content 편의 메서드

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);
}

 

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");
}

HTTP 응답 데이터 - 단순 텍스트, HTML

HTTP 응답 메시지는 주로 다음 내용을 담아서 전달한다.

 

1. 단순 텍스트 응답

      - 앞에서 살펴봄 ( writer.println("ok"); ) 

2. HTML 응답

3. HTTP API - MessageBody JSON 응답

HttpServletResponse - HTML 응답
hello.servlet.web.response.ResponseHtmlServlet

package hello.servlet.basic.response;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@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>");
    }
}

 

HTTP 응답으로 HTML을 반환할 때는 content-type을  text/html 로 지정해야 한다.

실행

http://localhost:8080/response-html

페이지 소스보기를 사용하면 결과 HTML을 확인할 수 있다.

HTTP 응답 데이터 - API JSON

hello.servlet.web.response. ResponseJsonServlet

package hello.servlet.basic.response;

import com.fasterxml.jackson.databind.ObjectMapper;
import hello.servlet.basic.HelloData;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(name = "responseJsonServlet", urlPatterns = "/response-json")
public class ResponseJsonServlet extends HttpServlet {

    private ObjectMapper objectMapper = new ObjectMapper();
    
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        
        // Content-type: application/json
        response.setHeader("content-type", "application/json");
        response.setCharacterEncoding("utf-8");

        HelloData data = new HelloData();
        data.setUsername("kim");
        data.setAge(20);
        
        // {"username":"kim", "age":20}
        String result = objectMapper.writeValueAsString(data);
        
        response.getWriter().write(result);
    }
}

 

HTTP 응답으로 JSON을 반환할 때는 content-type을  application/json 로 지정해야 한다.

Jackson 라이브러리가 제공하는  objectMapper.writeValueAsString() 를 사용하면 객체를 JSON 문자로 변경할 수 있다.

실행

http://localhost:8080/response-json

> 참고
application/json 은 스펙상 utf-8 형식을 사용하도록 정의되어 있다. 그래서 스펙에서 charset=utf-8과 같은 추가 파라미터를 지원하지 않는다. 따라서 application/json이라고만 사용해야지 application/json;charset=utf-8 이라고 전달하는 것은 의미 없는 파라미터를 추가한 것이 된다.

response.getWriter()를 사용하면 추가 파라미터를 자동으로 추가해버린다. 이때는

response.getOutputStream() 으로 출력하면 그런 문제가 없다.

'Spring > 스프링 MVC 1편' 카테고리의 다른 글

3. 서블릿, JSP, MVC 패턴(2)  (0) 2023.01.19
3. 서블릿, JSP, MVC 패턴(1)  (0) 2023.01.18
2. 서블릿(2)  (0) 2023.01.17
2. 서블릿(1)  (0) 2023.01.17
1. 웹 애플리케이션 이해(2)  (0) 2023.01.17
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유