본문 바로가기
Spring/MVC 1편

인프런 MVC 1 강의 정리 #2

by 예린lynn 2023. 12. 27.
728x90

1 . 프로젝트 생성

서블릿을 이용한 스프링 부트 프로젝트를 생성해보자. start.spring.io에서 Gradle, WAR로 설정하여 Generate한다. 

파일을 열어서 롬복을 설치하고, Settings -> Annotation Processors의 Enable annotation processing을 체크한다.

 

기본 설정을 마친 후 기본 메인 클래스를 실행해보고 동작하는지 확인하기 위해 http://localhost:8080을 들어가본다. 아래와 같은 Error page가 나오면 정상 작동하는 것이다.

 

2 . Hello 서블릿

스프링 부트는 톰캣 서버를 내장하고 있기 때문에, 톰캣 서버 설치 없이 편리하게 서블릿 코드를 실행할 수 있다.

본격적으로 스프링 부트 서블릿 환경을 구성해보자.

 

1) 서블릿 환경 구성

 

main/hello/servlet/ServletApplictation 클래스에 @ServletComponentScan 어노테이션을 추가한다. 

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@ServletComponentScan //서블릿 자동 등록
@SpringBootApplication

public class ServletApplication {

public static void main(String[] args) {
SpringApplication.run(ServletApplication.class, args);

}
}

 

cf) @ServletComponentScan : 서블릿 관련 컴포넌트들을 자동으로 스캔하여 등록하는 데 사용된다. 이를 통해 개발자는 명시적으로 이러한 서블릿 컴포넌트들을 등록하는 코드를 작성할 필요 없이, 스캔되어야 할 패키지나 클래스를 지정하여 Spring이 해당 클래스들을 찾아내고 등록할 수 있다.

 

2) 서블릿 등록하기

 

다음으로 실제 동작하는 서블릿 코드를 등록해보자. main/hello/servlet에 basic 패키지를 생성하고, 그 안에 HelloServlet 클래스를 만들어 다음과 같은 코드를 추가한다.

 

1. 먼저 @WebServlet 어노테이션을 추가한다. 웹 어플리케이션에서 서블릿을 정의하고 등록하기 위한 어노테이션이다.

(name: 서블릿 이름, urlPatterns: URL 매핑)

2. 그 다음 HttpServlet을 extends로 상속한다.

3. service 메소드를 오버라이드해서 서블릿 코드를 작성한다.

   -> 단축키 Ctrl + O 를 사용하여 service 를 선택

 

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

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

        String username = request.getParameter("username");
        System.out.println("username = " + username);

        response.setContentType("text/plain");
        response.setCharacterEncoding("utf-8");
        response.getWriter().write("hello " +username);
    }
}

 

http://localhost:8080/hello?username=kim를 실행하면 다음과 같은 화면이 나온다.

 

3) HTTP 요청 메시지 로그로 확인하기

 

main/resources/application.properties 에 다음 코드를 추가하기만 하면 된다.

logging.level.org.apache.coyote.http11=debug

 

- 서블릿 컨테이너 동작 방식

 

스프링 부트를 실행하면, 스프링 부트 안에 있는 내장 톰켓 서버가 실행된다. 톰켓 서버는 내부에 서블릿 컨테이너를 가지고 있기 때문에 서블릿(helloServlet)을 생성해준다.

 

그 후 다음과 같은 HTTP 요청, HTTP 응답 메시지를 생성하게 된다.

 

서버는 이렇게 만든 요청, 응답 메시지를 기반으로 request, response 객체를 만든다. 그리고 서블릿(helloServlet)를 호출하면서 response, request 객체를 넘겨준다. 서블렛이 종료되면 위 사진의 HTTP 응답 메시지와 같은 형태로 반환해준다.

 

4) welcome 페이지 추가

먼저 main에 webapp 디렉토리를 생성하고 그 안에 index.html 파일을 만들어 다음 코드를 추가한다.

<!DOCTYPE html>
<html>
<head>
 <meta charset="UTF-8">
 <title>Title</title>
</head>
<body>
<ul>
 <li><a href="basic.html">서블릿 basic</a></li>
</ul>
</body>
</html>

 

그 다음으로 main/webapp에 basic.html 파일을 생성하고 다음 코드를 추가한다.

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<ul>
    <li>hello 서블릿
        <ul>
            <li><a href="/hello?username=servlet">hello 서블릿 호출</a></li>
        </ul>
    </li>
    <li>HttpServletRequest
        <ul>
            <li><a href="/request-header">기본 사용법, Header 조회</a></li>
            <li>HTTP 요청 메시지 바디 조회
                <ul>
                    <li><a href="/request-param?username=hello&age=20">GET - 쿼리
                        파라미터</a></li>
                    <li><a href="/basic/hello-form.html">POST - HTML Form</a></li>
                    li>
                    <li>HTTP API - MessageBody -> Postman 테스트</li>
                </ul>
            </li>
        </ul>
    </li>
    <li>HttpServletResponse
        <ul>
            <li><a href="/response-header">기본 사용법, Header 조회</a></li>
            <li>HTTP 응답 메시지 바디 조회
                <ul>
                    <li><a href="/response-html">HTML 응답</a></li>
                    <li><a href="/response-json">HTTP API JSON 응답</a></li>
                </ul>
            </li>
        </ul>
    </li>
</ul>
</body>
</html>

 

그 후 localhost:8080을 들어가면 다음과 같은 welcome 페이지가 만들어지는 것을 확인할 수 있다.

 

3 . HttpServletRequest - 개요

HTTP요청 메세지를 개발자가 직접 파싱하면 불편하기 때문에, 서블릿은 HTTP 요청 메시지를 파싱하는 객체인 HttpServletRequest를 제공한다.

 

HttpServletRequest를 사용하면 다음과 같은 HTTP 요청 메시지를 편리하게 조회할 수 있다.

  • start line : HTTP 메서드, URL, 쿼리 스트링, 스키마, 프로토콜
  • 헤더 : 헤더 조회
  • 바디 : form 파라미터 형식 조회, message body 데이터 직접 조회
  • 부가적으로 해당 HTTP 요청이 시작부터 끝날 때까지 유지되는 임시 저장소 기능도 수행 (저장: request.setAttribute(name, value), 조회: request.getAttribute(name)) 

 

4 . HttpServiceRequest - 기본 사용법

main/java/servlet/basic에 request 패키지를 생성하고, 그 안에 RequestHeaderServlet 클래스를 만든다.

앞에서 서블릿 등록을 위해 했던 과정과 동일하게 수행한다.

 

1. @WebServlet 어노테이션 추가

2. 그 다음 HttpServlet을 extends로 상속

3. service 메소드를 오버라이드해서 서블릿 코드 작성

   -> 단축키 Ctrl + O 를 사용하여 service 를 선택

@WebServlet(name="requestHeaderServlet", urlPatterns = "/request-header")

public class RequestHeaderServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        
    }
}

 

1) start-line 코드

 

RequestHeaderServlet 클래스에 다음 코드를 추가한다. 이때 아래 단축키를 이용해 method 추출할 수 있다.

cf) Extract Method : 추출할 부분 드래그 + (Ctrl + Alt + Shift + T)

//start line 정보

private void printStartLine(HttpServletRequest request) {
 System.out.println("--- REQUEST-LINE - start ---");
 System.out.println("request.getMethod() = " + request.getMethod()); //GET
 System.out.println("request.getProtocol() = " + request.getProtocol()); //HTTP/1.1
 System.out.println("request.getScheme() = " + request.getScheme()); //http
 // http://localhost:8080/request-header
 System.out.println("request.getRequestURL() = " + request.getRequestURL());
 // /request-header
 System.out.println("request.getRequestURI() = " + request.getRequestURI());
 //username=hi
 System.out.println("request.getQueryString() = " +
request.getQueryString());
 System.out.println("request.isSecure() = " + request.isSecure()); //https 사용유무
 System.out.println("--- REQUEST-LINE - end ---");
 System.out.println();
}

 

실행하면 다음과 같은 start-line 결과가 출력된다.

--- REQUEST-LINE - start ---
request.getMethod() = GET
request.getProtocol() = HTTP/1.1
request.getScheme() = http
request.getRequestURL() = http://localhost:8080/request-header
request.getRequestURI() = /request-header
request.getQueryString() = username=hello
request.isSecure() = false
--- REQUEST-LINE - end ---

 

2) Header 코드

RequestHeaderServlet 클래스에 다음 코드를 추가한다.

//Header 모든 정보

private void printHeaders(HttpServletRequest request) {
 System.out.println("--- Headers - start ---");
 
/*
 Enumeration<String> headerNames = request.getHeaderNames();
 while (headerNames.hasMoreElements()) {
 String headerName = headerNames.nextElement();
 System.out.println(headerName + ": " + request.getHeader(headerName));
 }
*/

 request.getHeaderNames().asIterator()
 .forEachRemaining(headerName -> System.out.println(headerName + ": "
+ request.getHeader(headerName)));
 System.out.println("--- Headers - end ---");
 System.out.println();
}

 

실행하면 다음과 같은 header 결과가 출력된다.

--- Headers - start ---
host: localhost:8080
connection: keep-alive
cache-control: max-age=0
sec-ch-ua: "Chromium";v="88", "Google Chrome";v="88", ";Not A Brand";v="99"
sec-ch-ua-mobile: ?0
upgrade-insecure-requests: 1
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_0) AppleWebKit/537.36 
(KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/
webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
sec-fetch-site: none
sec-fetch-mode: navigate
sec-fetch-user: ?1
sec-fetch-dest: document
accept-encoding: gzip, deflate, br
accept-language: ko,en-US;q=0.9,en;q=0.8,ko-KR;q=0.7
--- Headers - end ---

 

3) Header 편리한 조회

RequestHeaderServlet 클래스에 다음 코드를 추가한다.

//Header 편리한 조회

private void printHeaderUtils(HttpServletRequest request) {
 System.out.println("--- Header 편의 조회 start ---");
 System.out.println("[Host 편의 조회]");
 System.out.println("request.getServerName() = " + request.getServerName()); //Host 헤더
 System.out.println("request.getServerPort() = " + request.getServerPort()); //Host 헤더
 System.out.println();
 
 System.out.println("[Accept-Language 편의 조회]");
 request.getLocales().asIterator()
 .forEachRemaining(locale -> System.out.println("locale = " + locale));
 System.out.println("request.getLocale() = " + request.getLocale());
 System.out.println();
 
 System.out.println("[cookie 편의 조회]");
 if (request.getCookies() != null) {
 for (Cookie cookie : request.getCookies()) {
 System.out.println(cookie.getName() + ": " + cookie.getValue());
 }
 }
 
 System.out.println();
 System.out.println("[Content 편의 조회]");
 System.out.println("request.getContentType() = " + request.getContentType());
 System.out.println("request.getContentLength() = " + request.getContentLength());
 System.out.println("request.getCharacterEncoding() = " + request.getCharacterEncoding());
 
 System.out.println("--- Header 편의 조회 end ---");
 System.out.println();
}

 

실행하면 다음과 같은 결과가 출력된다.

--- Header 편의 조회 start ---
[Host 편의 조회]
request.getServerName() = localhost
request.getServerPort() = 8080
[Accept-Language 편의 조회]
locale = ko
locale = en_US
locale = en
locale = ko_KR
request.getLocale() = ko

[cookie 편의 조회]
[Content 편의 조회]
request.getContentType() = null
request.getContentLength() = -1
request.getCharacterEncoding() = UTF-8

--- Header 편의 조회 end ---

 

4) 기타 정보

RequestHeaderServlet 클래스에 다음 코드를 추가한다.

//기타 정보

private void printEtc(HttpServletRequest request) {
 System.out.println("--- 기타 조회 start ---");
 System.out.println("[Remote 정보]");
 System.out.println("request.getRemoteHost() = " + request.getRemoteHost()); //
 System.out.println("request.getRemoteAddr() = " + request.getRemoteAddr()); //
 System.out.println("request.getRemotePort() = " + request.getRemotePort()); //
 System.out.println();
 
 System.out.println("[Local 정보]");
 System.out.println("request.getLocalName() = " + request.getLocalName()); //
 System.out.println("request.getLocalAddr() = " + request.getLocalAddr()); //
 System.out.println("request.getLocalPort() = " + request.getLocalPort()); //
 
 System.out.println("--- 기타 조회 end ---");
 System.out.println();
}

 

실행하면 다음과 같은 결과가 출력된다.

--- 기타 조회 start ---

[Remote 정보]
request.getRemoteHost() = 0:0:0:0:0:0:0:1
request.getRemoteAddr() = 0:0:0:0:0:0:0:1
request.getRemotePort() = 54305

[Local 정보]
request.getLocalName() = localhost
request.getLocalAddr() = 0:0:0:0:0:0:0:1
request.getLocalPort() = 8080

--- 기타 조회 end ---

 

전체 RequestHeaderServlet 클래스의 코드는 다음과 같다.

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

@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);
    }
    
// start-line 정보
    private void printStartLine(HttpServletRequest request) {
        System.out.println("--- REQUEST-LINE - start ---");
        System.out.println("request.getMethod() = " + request.getMethod()); //GET
        System.out.println("request.getProtocol() = " + request.getProtocol()); //HTTP/1.1
        System.out.println("request.getScheme() = " + request.getScheme()); //http
        // http://localhost:8080/request-header
        System.out.println("request.getRequestURL() = " + request.getRequestURL());
        // /request-header
        System.out.println("request.getRequestURI() = " + request.getRequestURI());
        //username=hi
        
        System.out.println("request.getQueryString() = " + request.getQueryString());
        System.out.println("request.isSecure() = " + request.isSecure()); //https 사용        유무
        System.out.println("--- REQUEST-LINE - end ---");
        System.out.println();
    }

    // Header 모든 정보
    private void printHeaders(HttpServletRequest request) {
        System.out.println("--- Headers - start ---");
/*
 Enumeration<String> headerNames = request.getHeaderNames();
 while (headerNames.hasMoreElements()) {
 String headerName = headerNames.nextElement();
 System.out.println(headerName + ": " + request.getHeader(headerName));
 }
*/
        request.getHeaderNames().asIterator()
                .forEachRemaining(headerName -> System.out.println(headerName + ": "
                        + request.getHeader(headerName)));
        System.out.println("--- Headers - end ---");
        System.out.println();
    }

    //Header 편리한 조회
    private void printHeaderUtils(HttpServletRequest request) {
        System.out.println("--- Header 편의 조회 start ---");
        System.out.println("[Host 편의 조회]");
        System.out.println("request.getServerName() = " + request.getServerName()); //Host 헤더
        System.out.println("request.getServerPort() = " + request.getServerPort()); //Host 헤더
        System.out.println();
        
        System.out.println("[Accept-Language 편의 조회]");
        request.getLocales().asIterator()
                .forEachRemaining(locale -> System.out.println("locale = " + locale));
                
        System.out.println("request.getLocale() = " + request.getLocale());
        System.out.println();
        System.out.println("[cookie 편의 조회]");
        if (request.getCookies() != null) {
            for (Cookie cookie : request.getCookies()) {
                System.out.println(cookie.getName() + ": " + cookie.getValue());
            }
        }
        
        System.out.println();
        System.out.println("[Content 편의 조회]");
        System.out.println("request.getContentType() = " + request.getContentType());
        System.out.println("request.getContentLength() = " + request.getContentLength());
        System.out.println("request.getCharacterEncoding() = " + request.getCharacterEncoding());
        
        System.out.println("--- Header 편의 조회 end ---");
        System.out.println();
    }

    //기타 정보
    private void printEtc(HttpServletRequest request) {
        System.out.println("--- 기타 조회 start ---");
        System.out.println("[Remote 정보]");
        System.out.println("request.getRemoteHost() = " +
                request.getRemoteHost()); //
        System.out.println("request.getRemoteAddr() = " +
                request.getRemoteAddr()); //
        System.out.println("request.getRemotePort() = " +
                request.getRemotePort()); //
        System.out.println();
        System.out.println("[Local 정보]");
        System.out.println("request.getLocalName() = " + request.getLocalName()); //
        System.out.println("request.getLocalAddr() = " + request.getLocalAddr()); //
        System.out.println("request.getLocalPort() = " + request.getLocalPort()); //
        System.out.println("--- 기타 조회 end ---");
        System.out.println();
    }
}

 

5 . HTTP 요청 데이터 - 개요

HTTP 요청 메시지를 통해 클라이언트에서 서버로 데이터를 전달하는 방법을 알아보자.

 

1) GET - 쿼리 파라미터

  • /url**?username=hello&age=20**
  • 메시지 바디 없이, URL의 쿼리 파라미터에 데이터를 포함해서 전달
  • ex) 검색, 필터, 페이지 등에서 많이 사용하는 방식

2) POST - HTML Form

  • content-type: application/x-www-form-urlencoded
  • 메시지 바디에 쿼리 파리미터 형식으로 전달 username=hello&age=20
  • ex) 회원 가입, 상품 주문, HTML Form 사용

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

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

6 . HTTP 요청 데이터 - GET 쿼리 파라미터

메시지 바디 없이, URL의 쿼리 파라미터를 사용해서 아래와 같이 데이터를 전달할 수 있다.

이때 쿼리 파라미터는 ?를 시작으로 하고, 추가 파라미터는 &로 구분한다.

http://localhost:8080/request-param?username=hello&age=20

 

- 쿼리 파라미터 조회 메서드

String username = request.getParameter("username"); //단일 파라미터 조회
Enumeration<String> parameterNames = request.getParameterNames(); //파라미터 이름들 모두 조회
Map<String, String[]> parameterMap = request.getParameterMap(); //파라미터를 Map으로 조회
String[] usernames = request.getParameterValues("username"); //복수 파라미터 조회

 

1) 파라미터 전송 기능

http://localhost:8080/request-param?username=hello&age=20 을 전송하는 코드를 만들어보자.

 

먼저 main/java/basic/request에 RequestParamServlet 클래스를 생성한다.

서블릿 코드를 작성한 후, 전체 파라미터와 단일 파라미터를 조회하는 코드를 아래와 같이 추가한다.

/**
 * 1. 파라미터 전송 기능
 * http://localhost:8080/request-param?username=hello&age=20
 */

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

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

        System.out.println("[단일 파라미터 조회]");
        String username = request.getParameter("username");
        String age = request.getParameter("age");

        System.out.println("username = " + username);
        System.out.println("age = " + age);
        
    }
}

 

2) 동일한 파라미터 전송 기능

http://localhost:8080/request-param?username=hello&username=kim&age=20 을 전송하는 코드를 만들어보자.

RequestParamServlet 클래스에 다음과 같은 코드를 추가하는데 이때, 이름이 여러개일 경우는 getParameterValues를 이용해서 꺼낸다.

/** 2. 동일한 파라미터 전송 가능
 * http://localhost:8080/request-param?username=hello&username=kim&age=20
 */

System.out.println("[이름이 같은 복수 파라미터 조회]");

        String[] usernames = request.getParameterValues("username");
        for (String name : usernames) {
            System.out.println("username = " + name);
        }

 

전체 코드는 다음과 같다.

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.util.Enumeration;

/**
 * 1. 파라미터 전송 기능
 * http://localhost:8080/request-param?username=hello&age=20
 * <p>
 * 2. 동일한 파라미터 전송 가능
 * http://localhost:8080/request-param?username=hello&username=kim&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();
 
 System.out.println("[단일 파라미터 조회]");
 
 String username = request.getParameter("username");
 System.out.println("request.getParameter(username) = " + username);
 
 String age = request.getParameter("age");
 System.out.println("request.getParameter(age) = " + age);
 System.out.println();
 
 System.out.println("[이름이 같은 복수 파라미터 조회]");
 
 System.out.println("request.getParameterValues(username)");
 String[] usernames = request.getParameterValues("username");
 
 for (String name : usernames) {
 System.out.println("username=" + name);
 }
 
 response.getWriter().write("ok");
 }
}

 

위 코드를 실행하면 다음과 같은 결과가 출력된다.

[전체 파라미터 조회] - start
username=hello
age=20
[전체 파라미터 조회] - end

[단일 파라미터 조회]
request.getParameter(username) = hello
request.getParameter(age) = 20

[이름이 같은 복수 파라미터 조회]
request.getParameterValues(username)
username=hello
username=kim

 

cf) username=hello&username=kim 과 같이 파라미터 이름은 하나인데, 값이 중복이면 어떻게 될까?

  • request.getParameter() 는 하나의 파라미터 이름에 대해서 단 하나의 값만 있을 때 사용
  • 중복일 때는 request.getParameterValues() 를 사용
  • 중복일 때 request.getParameter() 를 사용하면, request.getParameterValues() 의 첫 번째 값 반환

7 . HTTP 요청 데이터 - POST HTML Form

  • content-type: application/x-www-form-urlencoded
  • 메시지 바디에 쿼리 파리미터 형식으로 데이터를 전달  ex) 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 형식도 둘 다 지원한다

 

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

src/main/java/basic/request에 RequestBodyStringServlet 클래스를 생성하고 아래 코드를 추가한다.

 

request.getInputStream()을 통해 메시지 바디 내용을 byte 코드로 얻을 수 있다. 그 다음 바이트 코드를 string으로 바꾸기 위해 StreamUtils.copyToString()을 사용한다.

import jakarta.servlet.ServletException;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.util.StreamUtils;

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

    }
}

 

9 . HTTP 요청 데이터 - API 메시지 바디 (JSON)

최근에는 HTTP API에서 주로 JSON 형식을 사용해서 데이터를 전달한다.

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

이때 JSON 형식으로 파싱할 수 있도록 객체를 생성해보자.

src/main/java/servlet/basic에 HelloData 클래스를 생성하고 아래 코드를 작성하자. 이때 @Getter, @Setter 어노테이션을 이용해서 간결하게 표현한다.

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class HelloData {

    private String username;
    private int age;
}

 

그 다음으로 src/main/java/servlet/basic/request에 RequestBodyJsonServlet 클래스를 생성한다.

먼저 앞서 HTTP API의 단순 텍스트 코드를 작성한 후, JSON 변환 라이브러리(ObjectMapper)를 추가하여 코드를 다음과 같이 작성한다.

import com.fasterxml.jackson.databind.ObjectMapper;
import hello.servlet.basic.HelloData;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.util.StreamUtils;

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

    }
}

 

cf) ObjectMapper : ObjectMapper는 Jackson 라이브러리의 일부로서, JSON 문자열을 Java 객체로 변환하거나, Java 객체를 JSON 문자열로 변환하는데 사용된다. JSON 문자열을 Java 객체로 변환하려면 readValue() 메서드를 사용하고, Java 객체를 JSON 문자열로 변환하려면 writeValueAsString() 메서드를 사용한다.

 

Postman으로 다음과 같이 실행해보면, 제대로 출력되는 것을 확인할 수 있다.

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

 

 10 . HttpServletResponse - 기본 사용법

- HttpServletResponse 역할

  • HTTP 응답 메시지 생성 : HTTP 응답코드 지정, 헤더 생성, 바디 생성
  • 편의 기능 제공 : Content-Type, 쿠키, Redirect

main/servlet/basic에 response 패키지를 생성하고, 그 안에 ResponseHeaderServlet 클래스를 생성한다.

먼저 HTTP 응답 메시지 코드를 다음과 같이 작성한다.

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

 

다음으로 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");
}

 

 

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

-HTTP 응답 메시지 내용

  • 단순 텍스트 응답  -> writer.println("ok");
  • HTML 응답
  • HTTP API - MessageBody JSON 응답

이 중 HTML 응답에 대해 알아보자.

main/servlet/basic/response에 ResponseHtmlServlet 클래스를 생성한다.

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.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>");

    }
}

 

localhost:8080/response-html에 들어가서 페이지 소스를 확인해보면 다음과 같은 결과가 나온다.

 

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

 

 12 . HTTP 응답 데이터 - API JSON

main/hello/servlet/response에 ResponseJsonServlet 클래스를 생성한다.

요청 데이터를 작성했을 때와 마찬가지로 ObjectMapper를 이용하고, writeValueAsString으로 변환한다.

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;

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

 

localhost:8080/response-json으로 들어가서 페이지 소스를 확인하면 다음과 같이 json 형식으로 쓰인 것을 확인할 수 있다.

 

-> JSON 형식으로 반환할 때, content-type은 application/json 으로 지정하고, 객체를 JSON 문자로 변경하기 위해 objectMapper.writeValueAsString()을 사용한다.

 

 

 

728x90

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

인프런 MVC 1 강의 정리 #6  (0) 2024.01.09
인프런 MVC 1 강의 정리 #5  (1) 2024.01.08
인프런 MVC 1 강의 정리 #4  (0) 2024.01.01
인프런 MVC 1 강의 정리 #3  (1) 2023.12.31
인프런 MVC 1 강의 정리 #1  (1) 2023.12.27