[스프링 MVC 1편] - 서블릿

Chooooo·2023년 1월 1일
0

스프링 MVC 1편

목록 보기
2/11
post-thumbnail

이 글은 강의 : 김영한님의 - "[스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술]"을 듣고 정리한 내용입니다. 😁😁


이번 시간부터 서블릿에 대해 알아볼 것이다. 프로젝트 생성부터 먼저 진행할 것인데,

프로젝트 생성

필요 항목

  • JDK 11 +
  • IDE : IntelliJ

프로젝트 생성

  • 스프링 공식 홈페이지에서 프로젝트 구성(https://start.sping.io)
  • IntelliJ Ultimate 이상이면 자체적으로 생성 가능하다

프로젝트 구성

  • Project : Gradle Project
  • Language : Java
  • Sping Boot : 최신 버전 사용
  • Project Matadata
    • Group : hello
    • Artifact : servlet
    • Name : servlet
    • Package name : hello.servlet
    • Packaging : War (지금은 서블릿으로 구현하기 때문에..)
    • JAVA : 11

의존성 관리

  • Sping Web
  • Lombok

Lombok 적용

🎈 Preferences > plugin > lombok 검색 실행(재시작)
🎈 Preferences > Annotation Processors 검색 > Enable annotation processiong 체크(재시작)

실행 및 확인

🎃 ServletApplicaion.main() 실행
🎃 http://localhost:8080으로 접속해서 Whitelabel Error Page가 나올 경우 정상 동작한다.

  • 현재 매핑될 페이지가 구현되지 않았기에 에러 페이지가 나오는게 정상

Postman 설치

url 테스트를 위해 postman 설치해두자

사전 준비 : view 페이지 작성

HTML 파일은 그대로 복붙해서 사용하자. 경로는 src/main 디렉토리 하위에 webapp 디렉터리 생성.

index.html

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

basic.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<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>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>

basic/hello-form.html

<!DOCTYPE html>
<html lang="en">
<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>

작성 완료 후 패키지 구조

🎈 Welcome Page : index.html은 Welcome 페이지인데 따로 경로를 지정해주지 않아도 루트 경로로 실행되는 기본 페이지이다!

Hello 서블릿

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

@ServletComponentScan

@ServletCompnentScan : 프로젝트의 ServletApplicaion 클래스에 가서 애노테이션 추가한다.
이 애노테이션은 프로젝트 내의 애노테이션이 선언된 클래스가 포함된 패키지부터 그 하위 패키지까지 작성된 서블릿들을 등록하고 사용할 수 있도록 해준다.

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

}

HelloServlet 생성

이제 실제로 서블릿을 생성해서 테스트해볼 필요가 있다. 패키지 경로 hello.servlet.basic에 HelloServlet 만들기.

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

🎈 Servlet을 사용하기 위해서는 HttpServlet을 상속받아야 한다.
→ 더하여 service 메서드를 오버라이딩해서 사용해야 한다. (접근 제어자가 protected)

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

  • name 속성으로 서블릿 이름 지정
  • urlPatterns 속성으로 서블릿에 매핑될 request url을 지정해준다.
    주의할 것은 모든 속성이 중복되서는 안돼!

🎈 HTTP 요청을 통해 매핑된 URL이 호출되면 서블릿 컨테이너는 service메서드를 실행한다.

HelloServlet 실행 및 동작 확인

주의

IntelliJ 무료(커뮤니티) 버전을 사용하는데 서버가 정상적으로 실행되지 않는다면,
프로젝트 생성 > IntelliJ Gradle 대신에 자바 직접 실행에 있는 주의 사항 참고

참고 : HTTP 요청 메세지를 로그 출력

웹 브라우저 혹은 postman을 통해 요청하는 HTTP 요청 메세지 혹은 응답 메세지를 직접 확인해보고 싶다면, 설정 파일에 로깅 옵션 넣어서 확인.
logging.level.org.apache.coyote.http11=debug

  • 운영 서버에서 사용했다간 성능저하가 발생할 수 있으니 개발 단계에서만 사용하자.
  • 요청했을 경우 출력되는 로그 내역
...o.a.coyote.http11.Http11InputBuffer: Received [GET /hello?username=servlet 
HTTP/1.1
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_1) 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: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://localhost:8080/basic.html
Accept-Encoding: gzip, deflate, br
Accept-Language: ko,en-US;q=0.9,en;q=0.8,ko-KR;q=0.7

응답 메세지 작성 및 확인

작성한 HelloServlet의 service 메서드에 코드 추가

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

response.setHeader("Content-Type", "text/plain;charset=utf-8") 로 작성해도 되지만, 매번 key/value를 모두 작성하는것은 힘들고 오타가 날 확률도 높아지기에 위와같은 편의메서드를 제공한다.

다시 localhost:8080/hello?username=world URL로 요청을 보낸 다음 응답 메세지를 확인하면 hello + username이 정상적으로 출력되는 것을 확인할 수 있다.

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

코드를 통해서 프로젝트에서 서블릿을 생성/등록 후 HTTP 요청을 통해 동작이 되는 것도 확인해보았다. 그렇다면 이 서블릿 컨테이너와 등록된 서블릿은 어떻게 동작을 하길래 요청을 받고 응답을 하는 것인가?
이 부분은 스프링 부트를 기반으로 설명한다.

내장 톰캣 생성

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

HTTP request, HTTP response

🎃 HTTP request

  • 웹 브라우저에서 위 사진 좌측의 HTTP 요청 포맷을 만들어서 서버에 요청한다.

🎃 HTTP response

  • 서버에서 로직 수행이 끝나면 사진 우측과 같은 HTTp 응답 메세지를 만들어 웹 브라우저에 전달한다.

웹 애플리케이션 서버의 요청 응답 구조

  • 서버에서는 요청받은 HTTP request를 가지고 request, response 객체를 생성
  • 서블릿 컨테이너에 싱글톤으로 등록된 서블릿 중 매핑되는 서블릿(HelloServlet)을 호출하며 생성한 request, response 객체 전달
  • 해당 서블릿에선 자신이 작성한 비즈니스로직(여기선 service 메서드) 수행
  • 서블릿에서 작성한 응답 객체(response)를 기반으로 HTTP 응답 메세지를 만들어서 웹 브라우저로 반환

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

HttpServletRequest - 개요

웹 브라우저에서는 위와 같이 HTTP 요청 메세지를 서버에 전달한다. 결국 텍스트 구조일텐데 이걸 개발자가 서블릿 내에서 사용하려면 직접 하나하나 파싱(데이터 추출 및 가공하기 쉬운 상태로 바꾸는 것)해줘야 한다.

HTTPServletRequest는 이런 파싱 작업을 개발자 대신 진행해서 담아 놓은 객체로 개발자가 HTTP 요청 메세지를 편리하게 사용할 수 있도록 해주는 객체이다.

요청 메세지 분석

위 HTTP 요청 메세지를 HTTPServletRequest 객체를 사용하면 편리하게 사용 가능하다.

HttpServletRequest 부가 기능

🎈 임시 저장소 기능

  • 해당 HTTP 요청이 시작부터 끝날 때까지 유지되는 임시 저장소 기능
    → 저장 : request.setAttribute(name, value)
    → 조회 : request.getAttribute(name)

🎈 세션 관리 기능
request.getSession(create:true)

🎃 중요

HttpServletRequest, HttpServletResponse를 사용할 때 중요한 점은 HTTP 요청및 응답 메세지를 편리하게 사용해주는 객체이기 때문에 이런 기능을 제대로 사용하려면 HTTP 스펙을 학습해야한다.

HttpServletRequest - 기본 사용법

HttpServletRequest가 제공하는 기본 기능 및 사용법을 알아보자.

RequestHeaderServlet 작성

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);
        printHeader(request);
        printHeaderUtils(request);
        printEtc(request);
        
        response.getWriter().write("ok");

    }

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

    private void printHeader(HttpServletRequest request) {
        System.out.println("--- Headers - start ---");

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

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

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

    }
}

이런 정보들을 조회할 수 있다 정도로 알고 넘어가기

🎈 웹 브라우저로 HTTP 요청 메세지 전달
http://localhost8080/request-header?username=???

HTTP 요청 데이터 - 개요

HTTP 요청 메세지를 통해 클라이언트에서 서버로 데이터를 전달하는데, 그 방법은 주로 3가지를 사용한다.

GET - Query Parameter

/url?username=hello&age=20 => /url?keyA=valueA&keyB=valueB

  • 메세지 바디 없이 URL의 쿼리 파라미터에 데이터를 포함해서 전달하는 방식
    🎈 검색, 필터, 페이징 등에서 많이 사용하는 방식

POST - HTML Form

  • content-type: application/x-www-form-urlencoded
  • 메세지 바디에 쿼리 파라미터 형식으로 전달한다.
    🎈 keyA=valueA&keyB=valueB
    → 회원 가입, 상품 주문, HTML Form 사용

HTTP 메세지 바디에 데이터를 담아 요청

  • HTTP API에서 주로 사용하며, JSON, XML, TEXT 사용
  • 데이터 형식은 주로 JSON이 사용된다.
    → POST, PUT, PATCH

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

우선 위에서 살펴본 HTTP 요청 데이터 전달 방식 중 GET Query Parameter를 사용해보자.
전달 데이터는 username, age 두개이고

  • username = hello
  • age = 20

메세지 바디 없이 URL Query Parameter를 사용해서 전달한다.
쿼리파라미터에서는 URL에 ? 를 시작으로 작성하며 하나 이상의 파라미터는 & 구분자를 통해 추가한다.
http://localhost:8080/request-param?username=hello&age=20

위 쿼리 파라미터가 포함된 URL을 통해 요청을 할 것이고 이를 받아줄 서블릿을 작성하자.

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
 */
@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("[단일 파라미터 조회] - start");
        String username = request.getParameter("username");
        String age = request.getParameter("age");
        System.out.println("username = " + username);
        System.out.println("age = " + age);
        System.out.println("[단일 파라미터 조회] - end");
        System.out.println();

        response.getWriter().write("ok");
    }
}
  • 실행 결과
[전체 파라미터 조회] - start
username=hello
age=20
[전체 파라미터 조회] - end
[단일 파라미터 조회]
request.getParameter(username) = hello
request.getParameter(age) = 20
[이름이 같은 복수 파라미터 조회]
request.getParameterValues(username)
username=hello
  • getParameter()메서드를 통해 단일 파라미터 조회가 가능하다
  • getParamterNames()메서드는 전달받은 쿼리 스트링의 모든 파라미터의 key값을 이터레이터로 반환한다.

🎈 복수 파라미터 조회

username=hello&username=kim 과 같이 파라미터 이름은 하나인데, 값이 중복이면 어떻게 될까?
request.getParameter() 는 하나의 파라미터 이름에 대해서 단 하나의 값만 있을 때 사용해야 한다.
지금처럼 중복일 때는 request.getParameterValues() 를 사용해야 한다.
참고로 이렇게 중복일 때 request.getParameter() 를 사용하면 request.getParameterValues() 의
첫 번째 값을 반환한다.

HTTP 요청 데이터 - POST HTML Form

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

특징

  • content-type: application/x-www-form-urlencoded
  • 메세지 바디에 쿼리 파라미터 형식으로 데이터를 전달한다.

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 형식도 둘 다 지원한다.


정리하면, getParameter()메서드는 GET URL 쿼리 파라미터 및 POST HTML Form 형식 모두 지원한다.

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

HTTP API에서 주로 사용하는 방식으로 HTTP message body에 개발자가 직접 데이터를 담아서 요청하는 방식이다. JSON, XML, TEXT가 주로 사용되는 포맷 형식인데, 최근에 들어서는 거진 JSON이 사용된다.

POST, PUT, PATCH 사용에 주로 사용된다.

이번 챕터에서는 단순한 텍스트 메세지를 HTTP message body에 담아 전송하고 읽어보자

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

HTTP 메세지 바디는 InputStream을 사용해서 직접 읽을 수 있다.

  • StreamUtil 클래스를 통해 InputStream을 String으로 편하게 바꿀 수 있는데 파라미터에 문자표를 지정해줘야 한다. 여기서는 UTF-8

URL은 POST이고 http://localhost:8080/request-body-string
content-type은 text/plain
message body는 raw의 text로 설정한 뒤 hello 작성

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

이번에는 주로 쓰이는 JSON으로 HTTP API를 작성해 요청해보자.

HelloData

JSON 형식을 사용할 때는 해당 데이터를 파싱해서 객체화 해줄 객체를 만들어줄 필요가 있다.

username, age를 담을 수 있는 JSON parse 객체를 만들어주자

package hello.servlet.basic;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class HelloData {
    private String username;
    private int age;

}
  • 롬복을 사용해서 getter, setter, toString 메서드를 생략할 수 있다.

RequestBodyJsonServlet

JSON 방식의 API와 매핑해줄 서블릿 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 = " + helloData);
    }
}

StreamUtil 클래스로 InputStream을 읽어와서 이를 스프링부트에서 기본으로 제공하는 Jackson이라는 json 라이브러리를 사용해서 이 값을 객체로 읽어올 수 있다.
objectMapper.readValue(messageBody, HelloData.class)

🎈 Postman 전송 양식

{
	"username" : "hello",
    "age" : 20
}

HttpServletResponse - 기본 사용법

지금까지 요청에 대해 알아보았고 이제 응답메세지에 대해 알아보자

🎈 HttpServletResponse 객체 역시 요청객체인 HttpServletRequest와 같은 목적으로 개발자가 HTTP 응답 메세지를 직접 작성하려면 너무 번거롭다보니 이를 자동으로 작성해주기위한 편의 객체이다.

Response 객체를 통해 응답 메세지 생성을 하기 위해서는

  • HTTP 응답 코드 지정
  • 헤더 생성
  • 바디 생성
    을 해주면 되며, 모두 지원을 해주고 편의 기능도 제공해준다.
  • content-Type, 쿠키, Redirect

HttpServletResponse

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;

@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-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 편의 메서드]
        cookie(response);

        //[redirect 편의 메서드]
        redirect(response);

        //[message body]
        PrintWriter writer = response.getWriter();
        writer.println("성공");
    }

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

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

🎃 HttpServletResponse.SC_OK

  • 서블릿에서는 HttpServletResponse에서 상태코드를 의미있는 상수로 작성해놔서 이를 사용하는 것이 혹시 모를 오타를 방지하고 가독성을 높힐 수 있다.

🎃 response.setHeader(..)

  • 예제 코드에 작성된 헤더 내용은 캐시를 완전히 무효화 하겠다는 내용이고, 주석된 내용은 ContentType 편의 메서드인 content(response)에서 작성을 해줬기에 주석처리 해준다.

redirect 편의 메서드 redirect(response)

리다이렉트 메서드만 살펴보면,

리다이렉트 편의 메서드를 사용하지 않고 리다이렉트를 유도하려면 response의 상태코드를 SC_FOUND(302)를 설정한 뒤 Header에 Location의 값으로 리다이렉션 해야하는 경로를 입력해줘야한다.
하지만, 편의메서드인 sendRedirect를 사용하며 리다이렉션할 경로만 파라미터로 넣어주면, 자동으로 302 상태코드와 리다이렉션할 경로를 응답메세지에 설정해준다.

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

응답 데이터도 요청과 비슷하지만, 응답 내용을 분류하면 단순하게 텍스트 응답, HTML응답, HTTP API 응답이 있다.
그래서 이 응답 데이터를 이용해 사용자에게 HTML 페이지를 보여주기도하고 데이터를 가공해서 DOM 조작을 통해 페이지의 내용을 변경시킬수도 있다.

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

🎈 HTML 으로 반환한다는 내용을 setContentType으로 content-type을 지정해줘야 웹 브라우저에서는 정상적으로 해당 응답 결과 message body를 html으로 렌더링해서 사용자에게 보여준다.
🎈 서블릿을 통해 html 소스를 작성해주었으며, 동적으로 내용이 추가되거나 분기를 해야하는 경우 해당 메서드에서 작업을 수행하면 된다.
⇒ EX: writer.println("

안녕하세요."+ username +"님
");

HTTP 응답 데이터 - API JSON

응답 메세지 분류 중 API JSON 타입에 대해서 알아보자

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.setContentType("application/json");
        response.setCharacterEncoding("utf-8");

        HelloData helloData = new HelloData();
        helloData.setUsername("catsbi");
        helloData.setAge(20);

        String result = objectMapper.writeValueAsString(helloData);

        response.getWriter().write(result);

    }
}

🎈 ObjectMapper를 사용해서 HelloData 객체를 json으로 만들어주고있다.
🎈 content-type을 application/json으로 지정해야 한다.
🎈 application/json은 기본이 utf-8로 charset이 설정되있기에 setCharacterEncoding을 해주지 않아도 된다.
🎈 objectMapper.writeValueAsString() 메서드를 사용하면 객체를 JSON 문자로 변경할 수 있다.

이렇게 설정 후 JSON 웹브라우저에서 URL http://localhost:8080/response-json으로 요청하면 JSON 포맷으로 데이터가 정상적으로 응답되는 것을 확인할 수 있다.

이렇게 서블릿에 대해서 알아봤는데, 뒤로 갈수록 내용 이해가 편할 것이니 계속 나아가 보자.

profile
back-end, 지속 성장 가능한 개발자를 향하여

0개의 댓글