Spring Boot

서블릿(Servlet) 이란? - 2 (HttpServletRequest, HttpServletResponse)

작은별._. 2024. 1. 1. 20:00
728x90

HttpServletRequest와 HttpServletResponse 모두 서블릿(Servlet)이 Http 요청 및 응답 메시지를 간편하게 파싱하고 구성할 수 있도록 사용하는 객체입니다. 

 

서블릿 관련한 기본 내용은 아래 포스팅에 작성하였으니 참고해 주세요!

 

서블릿(Servlet) 이란?

서블릿은 웹 서버를 구현할 때 필요한 TCP/IP 연결, HTTP 메시지 파싱, HTML 생성 등의 역할을 대신해주어 개발자가 비즈니스 로직에만 집중할 수 있도록 해줍니다. 서블릿은 아래와 같이 생겼습니다

silver-programmer.tistory.com


HttpServletRequest

HttpServletRequest를 사용하면 아래와 같이 개발자가 HTTP 요청 메시지를 편리하게 조회할 수 있습니다.


POST /save HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded

username=kim&age=20

 

HTTP 요청 메시지 구성요소는 다음과 같이 정리할 수 있습니다. (참고하면 좋은 포스팅)

  • START LINE
    • HTTP 메서드
    • URL
    • Query String(쿼리 스트링)
    • Scheme, Protocol
  • Header
    • 헤더 조회
  • Body
    • form 파라미터 형식 조회
    • message body 데이터 직접 조회

 

HttpServletRequest 객체는 추가로 여러 가지 부가기능도 제공합니다. 예를 들어, 특정 HTTP 요청이 발생했을 때, 해당 요청의 시작부터 끝날 때까지 유지되어야 하는 정보를 저장하는 임시 저장소 기능을 제공합니다. 또한, 세션 관리 기능도 제공합니다.

임시 저장소 기능

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

세션 관리 기능

  • request.getSession(create: true)

HttpServletRequest 기본 사용법

HttpServletRequest가 제공하는 기본 기능들을 코드로 작성하였습니다.


//  http://localhost:8080/request-header/username=kim
@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);
    }

    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-header
        System.out.println("request.getRequestURI() = " + request.getRequestURI());
        //username=kim
        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 - start ---");

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

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

        System.out.println("--- Headers - 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();
    }

    //기타 정보
    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();
    }
}

 

 

해당 코드를 실행하면 HTTP를 이용하여 보낸 요청에 관한 헤더 정보와 쿼리 스트링 정보를 편리하게 조회할 수 있습니다.

 

만약, 쿼리 스트링에 전달된 파라미터 값들을 직접 꺼내어 확인하고 싶을 경우는 아래의 코드와 같이 getParameter()를 사용하여 조회합니다.


// http://localhost:8080/request-param?username=hello&username=kim&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("[단일 파라미터 조회]");
        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");
    }
}

 

 

해당 URL로 접속한 후 콘솔 창을 확인하면 아래와 같은 결과를 얻을 수 있습니다.


 


query parameter로 데이터를 전달하지 않고, message body에 JSON 형태로 request를 주는 경우가 실무에서는 더 많습니다. JSON으로 구성된 message body를 가지는 요청 데이터를 어떻게 파싱하여 이용할 수 있을지는 아래 코드를 통해 확인할 수 있습니다.

 

JSON과 상호작용할 클래스


// Request에서는 해당 클래스는 JSON 형식을 받을 객체로 역할을 합니다.
// Response에서는 해당 클래스는 JSON 형식으로 보낼 객체로 역할을 합니다.
@Getter
@Setter
public class MyData {

    private String username;
    private int age;

}

 

 

위 객체를 JSON으로 매핑


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

		// message body: {"username": "kim", "age": 20}
        System.out.println("messageBody = " + messageBody);

        MyData myData = objectMapper.readValue(messageBody, MyData.class);

        System.out.println("myData.username = " + myData.getUsername()); // kim
        System.out.println("myData.age = " + myData.getAge()); // 20

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

 


HttpServletResponse 기본 사용법

HttpServletResponse는 HTTP 응답 메시지를 생성하는 역할을 합니다. 

  • HTTP 응답코드 지정
  • Header 생성
  • Body 생성
  • Content-Type, 쿠키, Redirect 등 편의 기능 제공

HttpServletResponse 기본 사용법


// http://localhost:8080/response-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); // status 지정

        //[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.write("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");
    }
}

 


 

서블릿을 이용하여 HTML 응답 혹은 Message Body에 JSON 형태로 응답을 내려줄 수 있습니다. HTML 응답의 경우, service() 메서드 코드 내부에 직접 HTML 문법에 맞게 하나하나 작성해야 하는 번거로움이 있습니다. JSON 형태로 응답을 내려주면 ObjectMapper 클래스를 통해 객체를 JSON 문자로 변경하는 기능을 사용함으로써 조금 더 편리하게 응답을 구성할 수 있습니다.

 

HTML 응답


    @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>Hello World!!</div>");
        writer.println("</body>");
        writer.println("</html>");
    }

 


JSON 응답


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

        MyData myData = new MyData();
        myData.setUsername("kim");
        myData.setAge(20);

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

 


[참고자료]

김영한, 스프링 MVC 1편", 인프런

728x90
반응형