본문 바로가기

Web Sever 개발과 CS 기초/스프링

HTTP 서버를 편리하게 만들 수 있는 HttpServlet 이해와 사용법

개요 목적

Java로 직접 구현하는 HTTP Sever 해당 프로젝트에서는 http 관련 클래스 없이 java 코드만으로 HTTP 프로토콜 서버를 구현했다. 그러다보니 Client Request Message를 일일이 분석해서 비지니스 코드에 적용해야 하고 그 결과를 Response 형식에 맞춰서 보내줘야 하는 동작을 반복해야 했다.

이러한 HTTP 서버를 만들 때 반복되는 동작들을 일관적으로 대신 처리해주는 것이 WAS에서 사용하는 서블릿이다. 자바는 HttpServlet 인터페이스를 사용하여 HTTP 서버를 비지니스 로직에만 집중하며 만들 수 있다.

이번 시간에는 Spring에서도 사용하는 서블릿에 대한 이해와 HttpServlet의 기본 사용법을 알아보겠다.

서블릿이란

서블릿은 개발자가 HTTP 스펙 사용을 매우 편리하게 도와준다. url이 들어오면 urlPatterns 을 지정한 컨트롤러가 동작이 된다. 해당 메소드 안에서 HTTP 요청 정보를 편리하게 사용할 수 있는 HttpServletRequest와 HTTP 응답 정보를 편리하게 제공할 수 있는 HttpServletResponse를 사용해서 편리하게 비지니스 로직을 작성할 수 있다.

@WebServlet(name = "helloServlet", urlPatterns = "/hello") 
public class HelloServlet extends HttpServlet { 
    @Override 
    protected void service(HttpServletRequest request, HttpServletResponse response){ 
        //애플리케이션 로직
}

톰캣처럼 서블릿을 지원하는 WAS를 서블릿 컨테이너가 부른다.

HTTP 요청이 오면 서블릿 컨테이너가 제공하는 Reqeust 객체에 담긴 HTTP 요청 정보를 사용할 수 있고, 결과 값을 Response 객체에 담으면 HTTP 응답 정보를 만들어준다.

HttpServletRequest 기본 사용법

서블릿은 HTTP 요청 메세지를 편리하게 사용할 수 있도록 정보를 파싱하여 HttpServletRequest 객체에 담아서 제공한다.

- Start Line(HTTP 메소드, URL, 쿼리 스트링, 스키마, 프로토콜)
- Header(각종 Client가 작성한 header 정보들)
- Body( form 파라미터 형식 조회, message body 데이터 직접 조회)

Start Line 정보 조회

//이런식으로 HttpServletRequest 객체를 받아서 그 안에 있는 메소드로 
//파싱한 HTTP 정보를 얻을 수 있다.
private void printStartLine(HttpServletRequest request) {

    System.out.println("request.getMethod() = " + request.getMethod()); 
}
request.getMethod() = GET
request.getProtocol() = HTTP/1.1
request.getScheme() = http
request.getRequestURL() = <http://localhost:8080/request-header>
request.getRequestURI() = /request-header
request.getQueryString() = username=hello
request.isSecure() = false

Header 정보 조회

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

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //모든 헤더 조회
        request.getHeaderNames().asIterator()
            .forEachRemaining(headerName -> System.out.println(headerName + ": " + headerName));

        //Host 헤더 정보
        System.out.println("request.getServerName() = " + request.getServerName());
        System.out.println("request.getServerPort() = " + request.getServerPort());

        //Accept-Language 편의 조회
        request.getLocales().asIterator()
            .forEachRemaining(locale -> System.out.println("locale = " + locale));
        System.out.println("request.getLocale() = " + request.getLocale());

        //Cookie 편의 조회
        if (request.getCookies() != null) {
            for (Cookie cookie : request.getCookies()) {
                System.out.println(cookie.getName() + ": " + cookie.getValue());
            }
        }

        //Content 편의 조회
        System.out.println("request.getContentType() = " + request.getContentType());
        System.out.println("request.getContentLength() = " + request.getContentLength());
        System.out.println("request.getCharacterEncoding() = " + request.getCharacterEncoding());

        //Remote 정보
        System.out.println("request.getRemoteHost() = " + request.getRemoteHost());
        System.out.println("request.getRemoteAddr() = " + request.getRemoteAddr());
        System.out.println("request.getRemotePort() = " + request.getRemotePort());

        //Locale 정보
        System.out.println("request.getLocalName() = " + request.getLocalName());
        System.out.println("request.getLocalAddr() = " + request.getLocalAddr());
        System.out.println("request.getLocalPort() = " + request.getLocalPort());
    }
}

요청 데이터 조회

<파라미터 요청 조회>

@WebServlet(name = "requestParamServlet", urlPatterns = "/request-param")
public class RequestParamServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //파라미터 데이터 조회
        //1 전체 파라미터
        request.getParameterNames().asIterator()
                .forEachRemaining(paramName -> System.out.println(paramName + "=" + request.getParameter(paramName)));
        //2 단일 파라미터 조회
        String username = request.getParameter("username");
        String age = request.getParameter("age");

        //3 이름이 같은 파라미터 조회
        String[] usernames = request.getParameterValues("username");       
    }
}

<message Body 직접 조회>

@WebServlet(name = "requestBodyStringServlet", urlPatterns = "/request-body")
public class RequestBodyStringServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1 String 타입 조회
        ServletInputStream inputStream = request.getInputStream();
        String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);

        //2 json 타입 조회
        HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);
    }
}

HttpServletResponse 기본 사용법

HttpServletResponse를 사용해서 HTTP 응답코드 지정, 헤더 생성, 바디 생성을 손쉽게 할 수 있다.

@WebServlet(name = "responseHeaderServlet", urlPatterns = "/response")
public class ResponseHeaderServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1 응답 코드 지정하기
        response.setStatus(HttpServletResponse.SC_OK);

        //2 헤더 지정하기
        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");

        //3 json response body 지정하기
        response.setHeader("content-type", "application/json");
        response.setCharacterEncoding("utf-8");
        HelloData data = new HelloData();
        data.setUsername("kim");
        data.setAge(20);
        String result = objectMapper.writeValueAsString(data);
        response.getWriter().write(result);
    }
}