[Spring-MVC] 서블릿 실습

나영·2023년 7월 1일

Spring-MVC

목록 보기
2/7
post-thumbnail

스프링 부트는 톰캣 서버를 내장하고 있으므로, 톰캣 서버를 따로 설치할 필요 없이 편리하게 서블릿 코드를 실행할 수 있다.

Hello Servlet

스프링 부트 서블릿 환경 구성

스프링 부트는 서블릿을 직접 등록해 사용할 수 있도록 @ServletComponentScan 을 지원한다.

서블릿 등록

@WebServlet 서블릿 애노테이션을 활용해 서블릿을 등록한다.

  • name : 서블릿 이름
  • urlPatterns : URL 매핑
package hello.servlet.basic;

@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);
     
     response.setContentType("text/plain");
     response.setCharacterEncoding("utf-8");
     response.getWriter().write("hello " + username);
   }
}

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

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

1. 내장 톰캣 서버 생성
스프링 부트가 내장 톰캣 서버를 띄워주고, 서블릿 컨테이너를 통해 helloServlet 이라는 서블릿을 생성해준다.

2. HTTP 요청, 응답 메시지 생성
웹 브라우저에서 생성된 HTTP 요청 메시지를 서버에 던져주면, 서버는 이를 기반으로 requestresponse 객체를 만들어서 helloServlet (service 메서드) 을 호출한다.
원하는 작업이 끝나고 종료되면서 서버에서는 HTTP 응답 메시지를 만들어서 다시 웹 브라우저에 반환한다.


HttpServletRequest

HTTP 요청 메시지를 개발자가 편리하게 사용할 수 있도록 HttpServletRequest 객체에 담아서 제공한다.

이를 사용해 다음과 같은 HTTP 요청 메시지를 조회할 수 있다.

  • start-line
    • HTTP 메서드
    • URL
    • 쿼리 스트링
    • 스키마, 프로토콜
  • header
    • 헤더 조회
  • body
    • form 파라미터 형식 조회
    • message body 데이터 직접 조회

HTTP 요청 데이터

HTTP 요청 메시지를 통해 클라이언트 -> 서버로 데이터를 전달할 수 있다.
크게 3가지 방법을 사용한다.

1. GET 쿼리 파라미터

2. POST - HTML Form

3. HTTP message body에 데이터 직접 담기

하나씩 알아보도록 하자 !

GET 쿼리 파라미터

메시지 바디 없이 URL의 쿼리 파라미터를 사용해 데이터를 전달하는 방식이다.
쿼리 파라미터는 URL에 ? 을 시작으로 보낼 수 있고, 추가 파라미터는 & 로 구분한다.

<전달 데이터>

  • username=hello
  • age=20

쿼리 파라미터 조회 메서드는 다음과 같다.

String username = request.getParameter("username"); //단일 파라미터 조회

Enumeration<String> parameterNames = request.getParameterNames(); //파라미터 이름들
모두 조회

Map<String, String[]> parameterMap = request.getParameterMap(); //파라미터를 Map
으로 조회

String[] usernames = request.getParameterValues("username"); //복수 파라미터 조회
/**
 * 1. 파라미터 전송 가능
 * http://localhost:8082/request-param?username=hello&age=20
 * 
 * 2. 동일한 파라미터 전송 가능
 * 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");
    }
}

POST HTML Form

이번에는 HTML Form 을 사용해 클라이언트 -> 서버로 데이터를 전송해보자.
주로 회원가입, 상품주문 등에서 사용되는 방식이다.

  • content-type : application/x-www-form-urlencoded
  • 메시지 바디에 쿼리 파리미터 형식으로 데이터를 전달한다. username=hello&age=20
    -> 따라서 쿼리 파라미터 조회 메서드를 그대로 사용하면 된다.

API 메시지 바디

단순 텍스트

HTTP message body 에 데이터를 직접 담아서 요청하는 방식이다.

  • HTTP API 에서 주로 사용, JSON, XML, TEXT
  • 데이터 형식은 주로 JSON 사용
  • POST, PUT, PATCH
@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); //byte code -> String

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

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

InputStream 을 사용해 HTTP 메시지 바디의 데이터를 직접 읽을 수 있다.

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

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

    }
}

JSON 결과를 파싱해서 사용할 수 있는 자바 객체로 변환하려면 Jackson, Gson 같은 JSON 변환 라이브러리를 추가해서 사용해야 하는데, 스프링 부트로 Spring MVC를 선택하면 기본으로 Jackson 라이브러리(ObjectMapper)를 함께 제공한다.


HttpServletResponse

HttpServletResponse 는 HTTP 응답 메시지를 작성하는 역할이다.

  • HTTP 응답코드 지정
  • 헤더 생성
  • 바디 생성

또한, Content-Type, 쿠키, 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);
//        cookie(response);
//        redirect(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); //(생략시 자동 생성)
    }

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

}

HTTP 응답 데이터

요청 데이터와 마찬가지로 응답 데이터도 크게 3가지이다.

단순 텍스트, HTML

단순 텍스트 응답은 위에서 봤듯이 ( writer.println("ok"); )이다.

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

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

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

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

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

HTTP 응답으로 JSON을 반환할 때는 content-type을 application/json 로 지정해야 한다. Jackson 라이브러리가 제공하는 objectMapper.writeValueAsString() 를 사용하면 객체를 JSON 문자로 변경할 수 있다.

0개의 댓글