BackEnd/Spring MVC

[Spring] 서블릿 - 2

샤아이인 2022. 2. 14.

내가 공부한것을 올리며, 중요한 단원은 저 자신도 곱씹어 볼겸 상세히 기록하고 얕은부분들은 가겹게 포스팅 하겠습니다.

 

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

이번 시간에는 HTML의 Form을 이용해서 클라이언트에서 서버로 데이터를 전달하는 방법을 공부하였다.

 

● 특징

- content-type: application/x-www-form-urlencoded

- 메시지 바디에 쿼리 파리미터 형식으로 데이터를 전달한다. username=hello&age=20

 

우선 간단한 HTML 하나를 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 로 접속해 보자.

username 과 age 를 입력하는 부분이 있다. 각각 test, 25라 적은후 전송 버튼을 눌러주었다.

 

전송 버튼을 누르면 다음과 같은 HTTP 메시지를 서버에 전달해주게 된다.

- 요청 URL: http://localhost:8080/request-param

- content-type: application/x-www-form-urlencoded

- message body: username=test&age=25

 

그림으로 보면 다음과 같다.

POST 방식은 GET과는 다르게 context-type: application/x-www-form-urlencoded 이 추가되어 있다.

또한 message body부분에는 쿼리 파라티터같은 형식으로 데이터가 담겨있다.

=> username=test&age=25

 

따라서 쿼리 파라미터 조회 메서드를 그대로 재사용할 수 있다.

 

클라이언트 입장에서는 보낼때 사용하는 GET, POST 둘간에 차이가 있지만, 서버 입장에서는 둘의 형식이 동일하므로request.getParameter()를 사용해서 인자를 추출할수 있다.

 

이제 메시지를 전달받을 /request-param 의 코드를 살펴보자.

@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);
        System.out.println();

        System.out.println("이름이 같은 복수 파라미터 조회");
        String[] usernames = request.getParameterValues("username");
        for (String name : usernames) {
            System.out.println("username = " + name);
        }

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

결과는 다음과 같다.

원하는 결과를 출력받을 수 있게되었다.

 

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

GET URL 쿼리 파라미터 형식으로 클라이언트에서 서버로 데이터를 전달할 때는 HTTP 메시지 바디를 사용하지 않기 때문에 content-type이 없다.

POST HTML Form 형식으로 데이터를 전달하면 HTTP 메시지 바디에 해당 데이터를 포함해서 보내기 때문에 바디에 포함된 데이터가 어떤 형식인지 content-type을 꼭 지정해야 한다. 이렇게 폼으로 데이터를 전송하는 형식을 application/x-www-form-urlencoded 라 한다.

 

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

● HTTP message body에 데이터를 직접 담아서 요청한다.

- HTTP API에서 주로 사용, JSON, XML, TEXT

- 데이터 형식은 주로 JSON 사용

- POST, PUT, PATCH

 

일반적으로 단순 텍스트 보단 JSON 형태로 데이터를 보내지만, 우선 TEXT를 보내는것부터 확인해 보자.

@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(); // body 데이터를 바이트로 추출
        String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);

        System.out.println("messageBody = " + messageBody);

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

request.getInputStream()메서드는 byte 코드를 반환한다.

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

 

이제 Postman을 사용하여 데이터를 보내보자.

간단하게 hello! 라는 문자열을 보냈다.

 

콘솔창에 결과로 messageBody = hello 가 출력되었다.

 

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

드디어 JSON 형태로 데이터를 전송해보는 시간이다!

 

● JSON 형식 전송

- POST방식으로 http://localhost:8080/request-body-json 에 데이터를 보내면 된다.

- content-type: application/json

- message body: {"username": "hello", "age": 20} => 메시지 바디 부분에 포함된 JSON 데이터

- 결과: messageBody = {"username": "hello", "age": 20}

 

● JSON 형식 파싱 추가

JSON 형식의 데이터가 오면, 이 데이터를 객체로 바꾸기 위한 class를 하나 만들자.

import lombok.Getter;
import lombok.Setter;

@Getter @Setter
public class HelloData {

    private String username;
    private int age;
}
 

lombok이 제공하는 @Getter, @Setter 를 사용했다. getter 와 setter를 애노테이션 프로세스를 통하여 자동 생성해 준다.

코드가 정말 간단해졌다.

 

이제 JSON을 받은 서블릿 코드를 작성해 보자.

@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.getUsername = " + helloData.getUsername());
        System.out.println("helloData.getAge = " + helloData.getAge());

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

위에서 데이터를 저장할 HelloData class를 만들었다. 어떻게 HelloData 객체에 전달받은 데이터를 저장할까?

=> ObjectMapper()를 이용하면 된다.

 

이전과 같이 getInputStream()으로 바이트 코드를 전달받은 후, StreamUtils를 통하여 String으로 전달받는다.

이 String 정보를 objectMapper.readValue() 메서드의 인자로 전달하여 helloData 객체를 생성하면 된다.

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

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

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

 

이번에는 HTML 이 아닌! Postman을 사용하여 데이터를 보내보자!

- POST http://localhost:8080/request-body-json

- content-type: application/json (Body -> raw, 가장 오른쪽에서 JSON 선택)

- message body: {"username": "hello", "age": 20}

결과는 다음과 같다.

추가로 HTML Form 데이터도 메시지 body를 통하여 데이터를 전송하기 때문에 직접 읽을 수 있다.

하지만 이미 이를 위한 getParameter()라는 편리한 메서드가 제공하기 때문에 이를 사용하면 된다.

 

8. HttpServletResponse - 기본 사용법

● HTTP 응답 메시지 작성하기

- HTTP 응답코드 지정

- 헤더 생성

- 바디 생성

 

● 편의 기능 제공

- Content-type, 쿠기, Redirect

 

우선 가장 간단하게 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);

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

        // message body
        PrintWriter writer = response.getWriter();
        writer.println("OK");
    }
}
 

반환받은 메시지의 Header를 열어보면 다음과 같다.

위 코드에서 추가했던 부분들이 모두 Header 정보에 들어갔다.

또한 우리가 정의한 my-header 또한 추가된것을 확인할수 있다.

 

● content()라는 메서드를 만들어서 확인해 보자.

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

        // message body
        PrintWriter writer = response.getWriter();
        writer.println("OK");
    }

    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); //(생략시 자동 생성)
    }
}
 

content()메서드 안에서 setContentType()으로 contentType을 지정하였으며,

setCharacterEncoding() 메서드를 통해서 인코딩을 지정해 주었다.

따라서 Service()메서드에서 원래 지정하던 부분은 주석처리 하였다.

// response.setHeader("Content-Type", "text/plain;charset=UTF-8");
 

추가적으로 ContentLength는 생략하면 자동으로 생성하여 추가해 준다. 원한다면 직접 추가할 수 있다.

response.setContentLength(2); //(생략시 자동 생성)
 

● Cookie() 메서드를 추가해 보자.

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

        // message body
        PrintWriter writer = response.getWriter();
        writer.println("OK");
    }

    private void content(HttpServletResponse response) {
        // 생략
    }

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

반환받은 response 메시지를 보면 다음과 같다.

Cookie가 설정된것을 알수 있다. 우리가 지정한 myCookie가 설정되어있다.

 

이후 새로고침을 하여 request를 보내게 되면 request정보에 Cookie가 전달되는것을 알 수 있다.

 

● redirect()메서드를 추가했다

다음 코드를 살펴보자.

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

        // message body
        PrintWriter writer = response.getWriter();
        writer.println("OK");
    }

    private void content(HttpServletResponse response) {
        // 생략
    }

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

"/response-header" 로 접속하면 sendRedirect()메서드를 통하여 "hello-form.html"로 이동하게 된다.

 

처음 /response-header로 요청후, response 응답으로 302를 받게된다. 다음 사진을 확인해 보자.

또한 Location으로 리다이렉트할 URL이 포함되어 있다.

이 메세지를 받은 Client는 리다이렉트 하게된다.

 

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

 

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

- 단순 텍스트 응답 => 앞에서 살펴봄 ( writer.println("ok"); )

- HTML 응답

- HTTP API - MessageBody JSON 응답

 

우선 HTML응답부터 확인해 보자. 다음 코드를 살펴보자.

@WebServlet(name = "responseHtmlServlet", urlPatterns = "/response-html")
public class ResponseHtmlServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // Content-type
        response.setContentType("text/html");
        response.setCharacterEncoding("utf-8");

        PrintWriter writer = response.getWriter();
        writer.println("<html>");
        writer.println("<body>");
        writer.println(" <div>안녕?</div>");
        writer.println("</body>");
        writer.println("</html>");
    }
}
 

결과는 다음과 같다.

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

HTML이 정상적으로 출력되었다.

 

10. HTTP 응답 데이터 - API JSON

이번에는 HTTP응답 데이터를 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("zbqmgldjfh");
        data.setAge(20);

        //{"username":"zbqmgldjfh","age":20}
        String result = objectMapper.writeValueAsString(data);
        response.getWriter().write(result);
    }
}
 

위의 코드를 보면 이전에 사용한 HelloData 클래스 객체를 생성한 후, 값을 지정하고, ObjectMapper를 이용하여 알맞은 JSON string문자열로 바꿔준다.

 

이후 이를 출력하면 다음과 같은 결과를 얻을 수 있다.

 

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

[Spring] MVC 프레임워크 만들기 - 1  (0) 2022.02.19
[Spring] 서블릿, JSP, MVC 패턴  (0) 2022.02.17
[Spring] 서블릿 - 1  (0) 2022.02.13
[Spring] 웹 애플리케이션 이해  (0) 2022.02.12
[Spring] 스프링 MVC 프레임워크  (0) 2022.01.31

댓글