서블릿

LeeKyoungChang·2022년 1월 7일
0
post-thumbnail

'스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술' 수업을 듣고 정리한 내용입니다.

 

📚 1. Hello 서블릿

스프링 부트 환경에서 서블릿을 등록하고 사용해보자.

참고
서블릿은 톰캣 같은 웹 애플리케이션 서버를 직접 설치하고, 그 위에 서블릿 코드를 클래스 파일로 빌드해서 올린 다음, 톰캣 서버를 실행하면 된다. 하지만 이 과정은 매우 번거롭다.
스프링 부트는 톰캣 서버를 내장하고 있으므로, 톰캣 서버 설치 없이 편리하게 서블릿 코드를 실행할 수 있다.

 

📖 A. 스프링 부트 서블릿 환경 구성

@ServletComponentScan : 스프링 부트는 서블릿을 직접 등록해서 사용할 수 있도록 @ServletComponentScan 을 지원한다.

 

main/java/servlet/ServletApplication.java

package hello.servlet;

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

 

실행 화면
스크린샷 2022-01-05 오후 6 48 09

스크린샷 2022-01-05 오후 6 47 55

 

ServletApplicaiton 을 실행하였을 때 아무것도 뜨지 않는다. (아직 입력이 없다.)

 

서블릿 등록하기
처음으로 실제 동작하는 서블릿 코드를 등록해보자.

 

main/java/hello.servlet/basic/HelloServlet.java


package hello.servlet.basic;


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;

// hello로 오면 이게 실행된다.
@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);

    }
}

 

실행 결과
localhost:8080/hello?username=chang
여기서
?username=chang : 쿼리 파라미터 (서블릿을 편하게 읽는 것을 지원하다.)
스크린샷 2022-01-06 오전 10 27 30

 

스크린샷 2022-01-06 오전 10 36 00

 

@WebServlet 서블릿 애노테이션
@WebServlet 애노테이션은 해당 클래스를 서블릿으로 사용할 수 있게 해주는 애노테이션이다.

  • name : 서블릿 이름
  • urlPatterns : URL 매핑
  • nameurlPatterns 는 겹치면 안된다.

 

HTTP 요청을 통해 매핑된 URL이 호출되면 서블릿 컨테이너는 다음 메서드를 실행한다.
protected void service(HttpServletRequest request, HttpServletResponse response)

 

웹브라우저 실행

  • http://localhost:8080/hello?username=world
  • 결과 : hello world

 

콘솔 실행 결과

HelloServlet.service
request = org.apache.catalina.connector.RequestFacade@a8d60b1
response = org.apache.catalina.connector.ResponseFacade@63c4ff4b
username = chang

 

📖 B. HTTP 요청 메시지 로그로 확인하기

application.properties
logging.level.org.apache.coyote.http11=debug 을 추가

 

서버를 다시 시작하고, 요청해보면 서버가 받은 HTTP 요청 메시지를 출력하는 것을 확인할 수 있다.

Host: localhost:8080
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="96", "Google Chrome";v="96"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "macOS"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 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

]
HelloServlet.service
request = org.apache.catalina.connector.RequestFacade@51cb338d
response = org.apache.catalina.connector.ResponseFacade@43c45faf
username = world
2022-01-06 10:56:10.807 DEBUG 1362 --- [nio-8080-exec-1] o.a.coyote.http11.Http11InputBuffer      : Before fill(): parsingHeader: [true], parsingRequestLine: [true], parsingRequestLinePhase: [0], parsingRequestLineStart: [0], byteBuffer.position(): [0], byteBuffer.limit(): [0], end: [732]
2022-01-06 10:56:10.807 DEBUG 1362 --- [nio-8080-exec-1] o.a.coyote.http11.Http11InputBuffer      : Received []
2022-01-06 10:56:10.807 DEBUG 1362 --- [nio-8080-exec-1] o.apache.coyote.http11.Http11Processor   : Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@40cdcc4:org.apache.tomcat.util.net.NioChannel@28525a1e:java.nio.channels.SocketChannel[connected local=/0:0:0:0:0:0:0:1:8080 remote=/0:0:0:0:0:0:0:1:50308]], Status in: [OPEN_READ], State out: [OPEN]


📝 참고
운영서버에 이렇게 모든 요청 정보를 다 남기면 성능저하가 발생할 수 있다. 개발 단계에서만 적용하자.

 

📖 C. 서블릿 컨테이너 동작 방식 설명

(1) 내장 톰캣 서버 생성
스크린샷 2022-01-06 오전 11 00 26

스프링 부트 내부에 포함된 내장 톰캣이 띄워지면서 가지고 있는 서블릿 컨테이너를 통해 서블릿을 생성하고 등록해준다.

 

(2) HTTP 요청, HTTP 응답 메시지
스크린샷 2022-01-06 오전 11 00 31

  • 웹브라우저에서 HTTP 요청 포맷을 만들어서 서버에 요청한다.
  • 서버에서 동작을 마치면 HTTP 응답 메시지를 만들어 웹 브라우저에 전달한다.

 

(3) 웹 애플리케이션 서버의 요청 응답 구조
스크린샷 2022-01-06 오전 11 00 37

  • 서버에서 요청받은 HTTP 요청 메시지를 기반으로 request, response 객체를 생성한다.
  • 서블릿 컨테이너에서 싱글톤으로 등록된 서블릿 중 매핑되는 서블릿을 호출하며 생성된 request, response 객체를 전달한다.
  • 전달받은 response를 기반으로 HTTP 응답 메시지를 생성해서 웹 브라우저로 전송한다.

 

📝 참고
HTTP 응답에서 Content-Length는 웹 애플리케이션 서버가 자동으로 생성해준다.

 

이외로 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>
                <li><a href="/basic/hello-form.html">POST - HTML Form</a></
                <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>

 

index.html
스크린샷 2022-01-06 오전 11 10 48

basic.html
스크린샷 2022-01-06 오전 11 10 54

 

📚 2. HttpServletRequest - 개요

HttpServletRequest 역할
서블릿은 개발자가 HTTP 요청 메시지를 편리하게 사용할 수 있도록 개발자 대신에 HTTP 요청 메시지를 파싱한다.
그리고 그 결과를 HttpServletRequest 객체에 담아서 제공한다.

 

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

POST /save HTTP/1.1
HOST: localhost:8080
Content-Type: application/x-ww-form-urlencoded

username=kim&age=20

 

(1) START LINE

  • HTTP 메소드
  • URL
  • 쿼리 스트링
  • 스키마, 프로토콜

(2) 헤더

  • 헤더 조회

(3) 바디

  • form 파라미터 형식 조회
  • message body 데이터 직접 조회

HttpServletRequest 객체는 추가로 여러가지 부가기능도 함께 제공한다.

 

📖 A. HttpServletRequest 부가 기능

임시 저장소 기능
해당 HTTP 요청이 시작부터 끝날 때까지 유지되는 임시 저장소 기능

  • 저장 : request.setAttribute(name, value)
  • 조회 : request.getAttribute(name)

 

세션 관리 기능

  • request.getSession(create: true)
  • ex) 로그인할 때 로그 했다 안했다. 세션으로 관리한다.

 

💡 중요
HttpServletRequest, HttpServletResponse를 사용할 때 가장 중요한 점은 이 객체들이 HTTP 요청 메시지, HTTP 응답 메시지를 편리하게 사용하도록 도와주는 객체라는 점이다. 따라서 이 기능에 대해서 깊이있는 이해를 하려면 HTTP 스펙이 제공하는 요청, 응답 메시지 자체를 이해해야 한다.

 

📚 3. HttpServletRequest - 기본 사용법

HttpServletRequest가 제공하는 기본 기능들을 알아보자!

 

📖 A. 전체 조회

hello.servlet.basic.request.RequestHeaderServlet.java

package hello.servlet.basic.request;

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;

@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.getProtocal() = " + request.getProtocol()); //

        System.out.println("request.getScheme() = " + request.getScheme()); //http
        // http://localhost:8080/request-header
        System.out.println("request.getRequestURL() = " + request.getRequestURL());
        // /request-test
        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();
    }


실행 결과


--- REQUEST-LINE - start ---
request.getMethod() = GET
request.getProtocal() = 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 ---


 

헤더 정보


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



실행 결과

스크린샷 2022-01-06 오후 12 17 15


--- Headers - start ---
host:localhost:8080
connection:keep-alive
cache-control:max-age=0
sec-ch-ua:" Not A;Brand";v="99", "Chromium";v="96", "Google Chrome";v="96"
sec-ch-ua-mobile:?0
sec-ch-ua-platform:"macOS"
upgrade-insecure-requests:1
user-agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 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 ---


 

📖 B. Header 편리한 조회

값을 간단하게 조회하고 싶을 때 : request.getHeader("host");

 


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

Postman에서 테스트

  • POSTBody 에 Hello!를 추가한다.
    스크린샷 2022-01-06 오후 12 30 35

 

  • 추가할시 Headerskey , values 가 추가된다.
    스크린샷 2022-01-06 오후 12 30 53

 

  • 다음으로 Send 를 클릭하여 서버에 보낸다.

 

결과


--- Header 편의 조회 start ---
[Host 편의 조회]
request.getServerName() = localhost
request.getServerPort() = 8080

[Accept-Language 편의 조회]
locale = ko_KR
request.getLocale() = ko_KR

[cookie 편의 조회]

[Content 편의 조회]
request.getContentType() = text/plain
request.getContentLength() = 6
request.getCharacterEncoding() = UTF-8
--- Header 편의 조회 end ---

 

request.getContentLength() 결과 6을 확인할 수 있다.

 

📖 C. 기타 정보 조회

기타 정보는 HTTP 메시지의 정보는 아니다.

 


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() = 60605

[Local 정보]
request.getLocalName() = localhost
request.getLocalAddr() = 0:0:0:0:0:0:0:1
request.getLocalPort() = 8080
--- 기타 조회 end ---

 

📝 참고
로컬에서 테스트하면 IPv6 정보가 나오는데, IPv4 정보를 보고 싶으면 다음 옵션을 VM option에 넣어주면 된다.
-Djava.net.preferIPv4Stack=true

 

지금까지 HttpServletRequest 를 통해서 HTTP 메시지의 start-line, header 정보 조회 방법을 이해했다.
다음부터는 HTTP 요청 데이터를 어떻게 조회하는지 알아보자.

 

📚 3. HTTP 요청 데이터 - 개요

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

 

주로 다음 3가지 방법을 사용한다.
(1) GET - 쿼리 파라미터

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

(2) POST - HTML Form

  • content-type: application/x-www-form-urlencoded
  • 메시지 바디에 쿼리 파라미터 형식으로 전달 username=hello&age=20
  • 예) 회원 가입, 상품 주문, HTML Form 사용
  • POST: HTML-Form 예시
    스크린샷 2022-01-06 오후 1 01 45

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

  • HTTP API에서 주로 사용, JSON, XML, TEXT등이 있다.

데이터 형식은 주로 JSON 사용

  • POST, PUT, PATCH

 

이제 하나씩 알아보자!

 

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

다음 데이터를 클라이언트에서 서버로 전송해보자.

  • username=hello
  • age=20

메시지 바디 없이, URL의 쿼리 파라미터를 사용해서 데이터를 전달하자
쿼리 파라미터는 URL에 다음과 같이 ? 를 시작으로 보낼 수 있다. 추가 파라미터는 &로 구분하면 된다.

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

 

서버에서는 HttpServletRequest 가 제공하는 다음 메서드를 통해 쿼리 파라미터를 편리하게 조회할 수 있다.

 

쿼리 파라미터 조회 메서드


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

 

📖 A. 단일 파라미터 조회

단일 파라미터 : username=hello 로 조회

main.java.hello.servlet.request.RequestParamServlet

package hello.servlet.basic.request;

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;


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

@WebServlet(name = "requestParamServlet", urlPatterns = "/request-param")
public class RequestParamServlet extends HttpServlet {

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

         /*
          Enumeration<String> parameterNames = request.getParameterNames();
          while (parameterNames.hasMoreElements()) {
              String paramName = parameterNames.nextElement();
              System.out.println(paramName + "=" +request.getParameter(paramName));
          }
          */


        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("[단일 파라미터 조회] - start");
        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("[단일 파라미터 조회] - end");
        System.out.println();

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

    }
}


실행결과 - 파라미터 전송
서블릿을 만든 후 http://localhost:8080/request-param?username=hello&age=20 파라미터를 전송하면 다음과 같이 출력된다.

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

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

 

📖 B. 복수 파라미터에서 단일 파라미터 조회

복수 파라미터, 파라미터 이름은 하나이지만 값이 중복인 경우 :
username=hello&username=chang

 

위 코드에 추가


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

실행결과 - 동일 파라미터 전송
http://localhost:8080/request-param?username=hello&username=chang&age=20

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

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

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

 

username=hello&username=chang 와 같이 파라미터 이름은 하나이지만, 값이 중복일때

  • request.getParameter() : 하나의 파라미터 이름에 대해서 단 하나의 값만 있을 때 사용한다.
  • request.getParameterValues() : 지금처럼 값이 중복일 때 사용한다.
  • 이렇게 중복일 때 request.getParameter() 를 사용하면 request.getParameterValues() 의 첫 번째 값을 반환한다.

 

📚 5. 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
스크린샷 2022-01-06 오후 3 58 29

스크린샷 2022-01-06 오후 3 59 39

 

💡 주의
웹 브라우저가 결과를 캐시하고 있어서, 과거에 작성했던 html 결과가 보이는 경우도 있다. 이때는 웹 브라우저의 새로 고침을 직접 선택해주면 된다.
물론 서버를 재시작 하지 않아서 그럴 수도 있다.

 

📖 A. POST의 HTML Form을 전송**

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에서 살펴본 쿼리 파라미터 형식과 같다. (GET 쿼리 파라미터 조회 메서드를 그대로 사용하면 된다.)
클라이언트(웹 브라우저) 입장에서는 두 방식에 차이가 있지만, 서버 입장에서는 둘의 형식이 동일하므로, request.getParameter() 로 편리하게 구분없이 조회할 수 있다.

 

실행
localhost:8080/basic/hello-form.html
스크린샷 2022-01-06 오후 4 12 59


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

[단일 파라미터 조회] - start
request.getParameter(username) = lee
request.getParameter(age) = 20
[단일 파라미터 조회] - end

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

 

📝 정리
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 라 한다.

 

📖 B. Postman

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

 

Postman 테스트 주의사항
Post 전송시

  • Bodyx-www-form-urlencoded 선택
  • Headers에서 content-type: application/x-www-form-urlencoded 로 지정된 부분 꼭 확인하기❗️

Postman에서 실행 결과
스크린샷 2022-01-06 오후 4 18 42


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

[단일 파라미터 조회] - start
request.getParameter(username) = kim
request.getParameter(age) = 20
[단일 파라미터 조회] - end

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

 

profile
"야, (오류 만났어?) 너두 (해결) 할 수 있어"

0개의 댓글