[Spring MVC] [1] 2. 서블릿_1

윤경·2021년 9월 4일
0

Spring MVC

목록 보기
2/26
post-thumbnail

[1] 프로젝트 생성

📌 프로젝트 생성 사이트
주의 Jar❌ War🅾️
➡️ 실행 속도가 더 빨라짐 요 설정을 따라해주세요.

준비 완료 !


[2] Hello 서블릿

개발은 꼬옥 따라해보자 !!

이 챕터는 실습 내용을 올린 것이라 별 내용은 없다 ^^; (이실별)

✔️ ServletApplication

: main이니 이걸 실행시켜보자.

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

}

⬇️ 실행 후 접속

✔️ RequestHeaderServlet.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;
  @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);
	}
}

@WebServlet 서블릿 애노테이션

name: 서블릿 이름
urlPatterns: URL 매핑

이것들은 중복이 있음 안 됨 !!

결과

⬇️ (톰캣 관련)

HelloServlet.service
request = org.apache.catalina.connector.RequestFacade@3b7952fe
response = org.apache.catalina.connector.ResponseFacade@574015ea

✔️ basic.html, index.html


⬇️ 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

<!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>

HTML 결과

서블릿 컨테이너 동작 방식




[3] HttpServletRequest - 개요

HttpServletRequest 역할

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

➡️ HttpServletRequest를 사용하면 이런 http 요청 메시지를 편리하게 조회할 수 있다.

또한 여러가지 부가 기능도 함께 제공한다.

✔️ 임시 저장소 기능
: 해당 HTTP 요청의 시작부터 끝(생존 범위)까지 유지되는 임시 저장소

저장: request.setAttribute(name, value)
조회: request.getAttribute(name)
➡️ 이렇게 값을 넣고 꺼낼 수 있음

✔️ 세션 관리 기능
(로그인 했다 안했다 상태를 유지)
request.getSession(create: true)

📌

HttpServletRequest, HttpServletResponse를 사용할 때 이 객체들이 HTTP 요청 메시지, HTTP 응답 메시지를 편리하게 사용하도록 도와주는 객체라는 점을 기억하자 !!

따라서, 이 기능에 대해 깊이있게 이해하게 위해서는 HTTP 스펙이 제공하는 요청, 응답 메시지 자체를 이해해야 한다.


[4] HttpServletRequest - 기본 사용법

이실별2 '^';

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

@WebServlet(name = "requestHeaderServlet", urlPatterns = "/request-header")
public class RequestHeaderServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException {
        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()); //HTTP/1.1
        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();
    }

    // Header 모든 정보
    private void printHeaders(HttpServletRequest request) {
        System.out.println("--- Headers-LINE - start ---");

        /*
        Enumeration<String> headerNames = request.getHeaderNames();
        while(headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            System.out.println(headerName + ": " + headerName);
        }
        */

        // 이렇게 하면 38-44 주석처리 한 줄과 같은 내용을 간단하게 할 수 있음.
        request.getHeaderNames().asIterator()
                        .forEachRemaining(headerName -> System.out.println(headerName + ": " + headerName));
        
        System.out.println("--- Headers-LINE - end ---");
    }

    //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 - 쿼리 파라미터
    ex. /url?username=hello&age=20
    메시지 바디 없이 URL 쿼리 파라미터에 데이터를 포함해 전달하는 방법이다.

  2. POST - HTML Form
    바디에 대한 설명: content-type: application/x-www-form-urlencoded
    메시지 바디에 쿼리 파라미터 형식으로 전달한다. (쿼리 파라미터와 비슷하게 생겼기 때문에 조회할 때 호환이 가능하다.)

  3. HTTP message body에 직접 데이터를 담아 요청
    HTTP API에서 주로 사용하며 JSON, XML, TEXT와 같은 것들인데 주로 JSON을 사용한다.


[6] GET 쿼리 파라미터

검색, 필터, 페이징 등에서 많이 사용하는 방식이다.

쿼리 파라미터는 ?를 시작으로 보내며 &를 통해 추가 파라미터를 붙일 수 있다.
ex. http://localhost:8080/request-param?username=hello&age=20

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

✔️ RequestParamServlet.java

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

/*
    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.getParameter()로 키 값을 꺼내는 것임
        request.getParameterNames().asIterator()
                        .forEachRemaining(paramName -> System.out.println(paramName + "=" + request.getParameter(paramName)));


        System.out.println("[전체 파라미터 조회] - end");
        System.out.println();

        System.out.println("[단일 파라미터 조회]");
        // request.getParameter() 해놓고 option+enter 하면 자동 완성
        String username = request.getParameter("username");
        String age = request.getParameter("age");

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

        // http://localhost:8080/request-param?username=hello2&age=20&username=hello2
        System.out.println("[이름이 같은 복수 파라미터 조회]");
        String[] usernames = request.getParameterValues("username");
        for (String name : usernames) {
            System.out.println("username = " + name);
        }

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


📌 복수 파라미터에서 단일 파라미터 조회
파라미터 이름은 하나인데 값이 중복일땐 어떻게 될까?
request.getParameter()하나의 파라미터 이름에 대해 단 하나의 값만 있을 때 사용해야 한다.
값이 중복일 때request.getParameterValues()를 사용하면 된다.

참고로 중복일 때 request.getParameter()를 사용하면 request.getParameterValues()첫번째 값을 반환한다.


[7] HTTP 요청 데이터 - POST HTML Form

: 웹 브라우저가 HTTP 메시지를 생성해 요청
(회원 가입, 상품 주문 등에서 주로 사용)

📌 특징

  • content-type: application/x-www-form-urlencoded
  • 메시지 바디에 쿼리 파라미터 형식으로 데이터를 전달 (→ 그래서 호환 가능)

✔️ 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>


➡️ 위에서 쿼리문과 형태가 똑같아서 호환이 가능하다고 했다. 즉, 이렇게 GET 요청과 같이 request.getParameter(), request.getParameterValues()로 값을 꺼낼 수 있다.

GET 쿼리 파라미터 형식과 같아 쿼리 파라미터 조회 메소드를 그대로 사용

클라이언트 입장에서 두 방식에 차이가 있긴 하지만, 서버 입장에서는 둘의 형식이 같으므로 request.getParameter()로 편리하게 조회가 가능하다.
➡️ request.getParameter()는 GET URL쿼리 파라미터 형식, POST HTML Form 형식 둘 다 지원

📌 참고

  • GET URL 쿼리 파라미터 형식은 클라이언트에서 서버로 데이터를 전달할 때 HTTP 메시지 바디를 사용하지 않아 content-type이 없다
  • POST HTML Form 형식은 데이터를 전달할 때 HTTP 메시지 바디에 해당 데이터를 포함해 보내기 때문에 바디에 포함된 메시지가 어떤 형식인지 content-type을 꼭 지정해야 한다.
    이런 방식을 application/x-www-form-urlencoded이라고 한다.

🤷🏻 위에서는 HTML form을 이용했으나 이런 단순한 테스트를 위해 앞으로 계속 html을 만들긴 너무 귀찮지 않아?

Postman을 이용하자 !!


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

: HTTP message body에 데이터를 직접 담아 요청하기

HTTP API에서 주로 사용하며 JSON, XML, TEXT 형식으로 보낼 수 있지만 JSON이 사실상 표준이 되어버렸다.
POST, PUT, PATCH에서 주고 사용된다.

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

➡️ InputStream으로 읽을 수 있다.

(InputStream은 byte 코드를 반환한다. 우리는 이대로 읽을 수 없으므로 UTF-8로 지정해주어 변환하였다.)

그런데 보통은 json 형식으로 주고 받지 이렇게 주고받진 않는다. 바로 JSON 형식으로 주고 받는 것을 배워보자.


[9] 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}

✔️ 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();

    // ser만 쳐도 알아서 option+enter로 생성됨
    @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");
    }
}

➡️ lombok 제공 @Getter, @Setter로 눈에 보이지 않게 코드가 추가되어 사용 가능하다.

📌

JSON 결과를 파싱해 사용할 수 있는 자바 객체로 변환하려면 Jackson, Gson 같은 JSON 변환 라이브러리를 추가해 사용해야 한다.
스프링 부트로 Spring MVC를 선택하면 기본으로 Jackson 라이브러리 (ObjectMapper)를 함께 제공한다.

📌

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


profile
개발 바보 이사 중

0개의 댓글