백엔드/Spring

[Spring] 서블릿

happy_life 2022. 6. 9. 11:27

목차

1.서블릿이란

2. HttpServletRequest

3. Http 요청 데이터

4. HttpServletResponse

5. Http 응답 데이터

 

 

1. 서블릿이란

HTTP 통신 request, response를 보면 알겠지만, 정보가 매우 많고 파싱이 어렵다. 이를 하나하나 파싱하여 코드로 구현해도 되지만, 시간도 오래걸리고 비효율적일 것이다. 따라서 이러한 과정을 대체해주고, 개발자가 온전히 비즈니스 로직에만 집중할 수 있도록 해주는 것이 등장했는데 이것이 바로 서블릿이다.

 

1) 서블릿의 작동 과정

1. 개발자가 서블릿 클래스에 코드를 작성한다.

2. 서블릿 소스는 컴파일되며 서블릿 클래스(.class)가 된다.

3. 이것이 톰캣과 같은 내장서버 컨테이너에 등록된다.

4. 클라이언트가 특정 경로에 대한 HTTP Request를 보낸다.

5. HTTP Request, Response에 해당하는 객체가 생성된다.

6. Request를 바탕으로 한 로직이 Response응답 객체에 담기고, 클라이언트에게 전달된다.

 

코드를 예를 들어 보자.

@WebServlet(name = "Servlet", urlPatterns = "/servlet")
public class Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("Servlet.service");

        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        writer.println("<html>");
        writer.println("<body>");
        writer.println("    <div>1. url에 get 방식으로 데이터 전달하기 /get-request</div>");
        writer.println("    <div>2. get 방식으로 바디에 JSON 데이터 전달하기 /request-body-json</div>");
        writer.println("</body>");
        writer.println("</html>");
    }
}

개발자가 위와 같은 코드를 서블릿 클래스에 작성하였다. 이후 코드가 돌아가면서 컴파일 되고, 톰켓 내장 서버가 올라간다. 이제 클라이언트가  urlPatterns 에 있는 url에 요청을 보내면 아래와 같은 결과가 나온다.

 

 

2. HttpServletRequest 

1) 개요

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

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

 

2) 기본 사용법

get으로 원하는 HTTP정보를 가져올 수 있다.

 

간단한 코드 예제

@WebServlet(name = "requestHeaderServlet", urlPatterns = "/request-header")
public class RequestHeaderServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        System.out.println("request.getMethod() = " + request.getMethod()); //GET
        System.out.println("request.getProtocol() = " + request.getProtocol()); //HTTP/1.1
        
    }

 

 

3. Http 요청 데이터

1) 개요

HTTP 요청 메시지를 통해 클라이언트에서 서버로 데이터를 전달하는 방법에는 주로 3가지가 있다.

 

 

2) 요청 방법

1) GET - 쿼리 파라미터

예시: /url?name=hello&age=20

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

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

 

간단한 코드 예제

@WebServlet(name = "getRequestServlet", urlPatterns = "/get-request")
public class GetRequestServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("GetRequestServlet.service");
//
        req.getParameterNames().asIterator()
                .forEachRemaining(paramName -> System.out.println(paramName + " = " + req.getParameter(paramName)));

        resp.setContentType("text/html");
        resp.setCharacterEncoding("UTF-8");

        PrintWriter writer = resp.getWriter();
        req.getParameterNames().asIterator()
                .forEachRemaining(paramName -> writer.write(paramName + " = " + req.getParameter(paramName) + "\n"));
    }
}

 

코드 설명

getParameterNames()를 호출하면 url에 넣어둔 파라미터인 name, age가 꺼내진다.

getParam(key값)을 호출하면 url에 넣어두었던 값인 hello, 20가 꺼내진다.

 

 

2) POST - HTML Form

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

메시지 바디에 쿼리 파라미터 형식으로 전달 username=hello&age=20

ex) 회원 가입, 상품 주문, HTML Form 사용

Form 형식을 활용해 전송 버튼을 누르면 웹 브라우저가 오른쪽과 같은 요청 HTTP 메시지를 생성해 서버에 전달한다.

Content-Type의 application/x-www-form-urlencoded는 애플리케이션에서의 form으로부터 온 컨텐츠라는 의미이다. 한편 application/x-www-form-urlencoded 형식은 앞서 GET에서 살펴본 쿼리 파라미터의 형식과 같아 파라미터 조회 메서드를 그대로 사용할 수 있다. 클라이언트 입장에서는 두 방식이 차이가 있지만, 서버 입장에서는 둘의 형식이 동일하므로, request.getParameter()로 편리하게 조회할 수 있는 것이다.

 

간단한 코드 예제

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>

request-param.class

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

 

 

위와 같은 코드를 통한 html에서 값을 입력하고 전송을 누르면 request-param이란 url로 post 방식의 요청 메시지가 전달되게 된다. 

 

http://localhost:8888/basic/hello-form.html

 

이후 전송된 값들은 servlet 객체에 담기고 이를 GET방식과 똑같은 방법으로 찾을 수 있다.

결과값

 

한편, 이런 post 방식을 하기 위해 html을 계속 생성해주기란 여간 귀찮은 일이 아니다. 이를 간단히 할 수 있게 도와주는 postman 애플리케이션이 있으니 참고하자.

 

 

 

3) HTTP 요청 데이터 - API 메시지 바디

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

대부분 JSON이라는 데이터 형식을 사용한다.

 

ServeltInputStream으로 요청 메시지의 Body를 바이트 단위로 읽어올 수 있고, 이를 StreamUtils을 통해 String으로 치환하면 Body 요청 메시지를 볼 수 있다. 참고로 InputStream은 읽기 전용의 바이트 코드를 의미한다. 아래는 InputStream의 간단한 예제 코드이다.

 

 ServletInputStream inputStream = request.getInputStream();
 String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);

 

JSON 형식으로 데이터 전달하기 예제

보통 JSON형식은 그대로 쓰지 않고, 객체로 바꿔서 쓴다.  

물론 JSON도 문자이므로 StreamUtils를 사용해 결과값을 출력할 수 있긴 하다.

하지만 보통 객체로 바꿔서 쓰는데 그 사용법을 간단한 예제를 통해 알아보자.

 

포스트맨 요청 

 

 

 

간단한 코드 예제

ServletData.class

@Getter @Setter
public class Data {

    private String username;
    private int age;
}

 

* 참고

@Getter @Setter는 Lombok 라이브러리를 활용한 get, set 이다.

 

PostRequestBodyJsonServlet.class

@WebServlet(name = "postRequestBodyJsonServlet", urlPatterns = "/request-body-json")
public class PostRequestBodyJsonServlet extends HttpServlet {

    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("GetRequestBodyJsonServlet.service");

        ServletInputStream inputStream = req.getInputStream();
        String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
        // json 형식 출력
        System.out.println("messageBody = " + messageBody);

        // json 형식 변환
        ServletData servletData = objectMapper.readValue(messageBody, ServletData.class);
        System.out.println("name = " + servletData.getName());
        System.out.println("age = " + servletData.getAge());

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

결과값

스프링에서는 ObjectMapper를 통해 JSON을 객체 형식으로 바꿀 수 있다.

 

4. HttpServletResponse 

1) 기본 사용법

간단한 예제 코드

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

 

응답 메시지에 필요한 부분을 set 메서드를 통해 만들 수 있다.

 response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
 response.setHeader("Pragma","no-cache");
 response.setHeader("my-header", "hello");

하지만 일일이 위와 같이 코드를 짜는 것은 매우 불편하고, 이를 해결하기 위해 편의 제공 메서드가 존재한다. 편의 제공 메서드를 통해 좀 더 간편하게 HTTP 응답 메시지를 작성할 수 있다. 이를 f12를 눌러 확인해보면 우리가 만든 대로 응답이 제공되는 것을 볼 수 있다.

 

 

5. HTTP 응답 데이터

1) HTML로 응답하기

간단한 예제 코드

@WebServlet(name = "Servlet", urlPatterns = "/servlet")
public class Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("Servlet.service");

        // HTML로 응답
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        
        writer.println("<html>");
        writer.println("<body>");
        writer.println("    <div>1. url에 get 방식으로 데이터 전달하기 /get-request</div>");
        writer.println("    <div>2. Post 방식으로 바디에 JSON 데이터 전달하기 /request-body-json</div>");
        writer.println("</body>");
        writer.println("</html>");
    }
}

Content-type을 "text/html"로 지정하고 Encoding을 "utf-8"로 한 뒤 writer로 html 코드를 작성하면 된다.

이후 url의 페이지 소스 보기를 해보면 아래와 같이 된다.

 

 

2) JSON

간단한 예제 코드

@WebServlet(name = "responseJsonServlet", urlPatterns = "/response-json")
public class ResponseJsonServlet extends HttpServlet {
    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("application/json");
        resp.setCharacterEncoding("utf-8");

        //직접 값 넣기
        ServletData servletData = new ServletData();
        servletData.setName("kim");
        servletData.setAge(20);

        //{"name": "kim", "age": 20}
        String result = objectMapper.writeValueAsString(servletData);
        resp.getWriter().write(result);
    }
}

objectMapper를 사용해 객체에 들어있는 정보들을 Json 형식으로 반환해줄 수 있다. JSON을 전달할 때는 ContentType이 'application/json" 이어야 한다.

 

본 포스팅은 김영한님 인프런 강의내용을 바탕으로 복습을 위해 작성하였습니다. 강의를 통해 배운 개념을 바탕으로 추가적으로 공부한 부분과 간단한 코드 예제를 작성하였습니다. 코드 전체를 복사한 것이 아니라 임의로 수정하거나 생략하였습니다.