[Spring MVC] Servlet

Ho·2022년 8월 8일
0

Spring MVC

목록 보기
1/4

Servlet

@ServletComponentScan
@SpringBootApplication
public class ServletApplication {

	public static void main(String[] args) {
		SpringApplication.run(ServletApplication.class, args);
	}

}

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

@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");//header
        response.getWriter().write("hello " + username);//body

    }
}
  • HttpServlet 클래스를 상속받는 클래스를 생성한다.
  • @WebServlet 을 사용
    • name: 서블릿 이름 지정
    • urlPattrerns: url 경로를 지정
  • service 메서드를 오버라이딩 하여 원하는 동작을 수행한다.
  • 지정 url로 요청이 오면 서블릿 컨테이너는 request, response 객체를 생성하고 service 메서드를 호출하면서 파라미터로 넘겨준다.

Http 요청 메시지 로그

application.properties 에 다음 설정을 추가한다.

logging.level.org.apache.coyote.http11=debug

Servlet 동작 원리

스프링부트는 내장 톰캣 서버를 실행하면서 서블릿 컨테이너를 실행하고 서블릿을 생성한다.

http 요청이 오면 request, response 객체를 생성하고 지정된 url에 해당하는 서블릿의 service 메서드를 실행한다. service 메서드 내에서 response 객체에 http응답 헤더, 바디 등의 정보를 세팅할 수 있고 종료시 was는 response 객체를 이용하여 Http응답 메시지를 생성하고 이를 전송한다.


HttpServletRequest

서블릿은 개발자가 Http 요청 메시지를 편리하게 사용할 수 있도록 개발자 대신에 Http 요청 메시지를 파싱한다. 그리고 그 결과를 HttpServletRequest 객체에 담아서 제공한다.

Http 요청 메시지 (example)

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

username=kim&age=20

HttpServletRequest 객체를 이용하여 위와 같은 요청 메시지에서 다음과 같은 정보들을 얻을 수 있다.

  • Start line
    • Http 메소드
    • URL
    • 쿼리 스트링
    • 스키마, 프로토콜
  • 헤더
    • 헤더 정보
  • 바디
    • form 파라미터 형식 조회
    • message body 데이터 직접 조회

임시 저장소 기능

  • 해당 Http 요청이 시작하고 끝날 때 가지 유지되는 저장소 기능
    • 저장: request.setAttribute(name,value)
    • 조회: request.getAttribute(name)

세션 관리 기능

  • request.getSession(create: true)

HttpServletRequest - http 요청 메시지 헤더

Start-line 정보

@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-LINE - start ---");
        System.out.println("request.getMethod()      = " + request.getMethod());     //GET
        System.out.println("request.getProtocol()    = " + request.getProtocol());   //HTTP/1.1
        System.out.println("request.getScheme()      = " + request.getScheme());     //http

        System.out.println("request.getRequestURL()  = " + request.getRequestURL());
        System.out.println("request.getRequestURI()  = " + request.getRequestURI());

        System.out.println("request.getQueryString() = " + request.getQueryString());
        System.out.println("request.isSecure()       = " + request.isSecure());      //https사용 유무
        System.out.println("--- REQUEST-LINE - end ---");
        System.out.println();

       
    }
}

헤더 정보

@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("--- Headers - start ---");
        
        request.getHeaderNames().asIterator()
                        .forEachRemaining(headerName -> System.out.println(headerName + ": " + headerName));

        System.out.println("--- Headers - end ---");
        System.out.println();

       
    }
}

헤더 주요 정보

@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("--- 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();
        System.out.println("--- Header 편의 조회 end ---");
        System.out.println();
       
    }
}


HTTP 요청 데이터 처리

HTTP 요청 메시지를 통해 클라이언트가 서버로 데이터를 전달하는 방법은 다음과 같다.

  • GET - 쿼리 파라미터
    • /url?username=sonny&age=30
    • 메시지 바디 없이 url의 쿼리 파라미터에 데이터를 포함
    • 주로 검색, 필터, 페이징 등에서 많이 사용하는 방식
  • Post - HTML Form
    • Content-Type: application/x-www-form-urlencoded
    • 메시지 바디에 쿼리 파라미터 형식으로 전달 ex)username=sonny&age=30
    • 회원가입, 상품주문, HTML Form 사용
  • HTTP message body에 데이터를 직접 담아서 요청
    • HTTP API에서 주로 사용 (JSON, XML, TEXT)
  • 데이터 형식은 주로 JSON 사용

HTML Form 예시


HTTP 요청 데이터 - GET 쿼리 파라미터

쿼리파라미터는 URL 다음에 ?로 시작하여 name과 value를 담을 수 있다. 파라미터는 &로 추가할 수 있다.
http://localhost:8080/reques-param?username=sonny&age=30

서버에서는 HttpServletRequest가 제공하는 메서드를 통해 쿼리 파라미터를 조회할 수 있다.

request.getParameterNames()

@WebServlet(name = "requestParamServlet", urlPatterns = "/request-param")
public class RequestParamServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        
        request.getParameterNames().asIterator()
                        .forEachRemaining(paramName -> System.out.println(paramName + "= " + request.getParameter(paramName)));
    }
}

request.getParameterNames()

@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String username = request.getParameter("username");
    String age = request.getParameter("age");

    System.out.println("username = " + username);
    System.out.println("age = " + age);
}


HTTP 요청 데이터 - POST HTML Form

  • Content-Type: application/x-www-form-urlencoded
  • 메시지 바디에 쿼리 파라미터 형식으로 데이터를 전달한다.
  • HTML Form 을 전소하면 웹 브라우저는 HTTP 메시지를 생성한다
  • 메시지 바디에 username=sonny&age=30 쿼리파라미터 형식의 데이터가 담긴다.

서버 입장에서는 GET-쿼리파라미터와 POST-HTML Form의 데이터의 형식이 같으므로 request.getParameter()을 이용하여 둘다 조회할 수 있다.

다시말해 request.getParameter() 는 GET url 쿼리 파라미터와 POST HTML Form 형식 둘다 지원한다.


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

메시지 바디에 text 데이터를 받는 경우

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

}

InputStream을 이용하여 직접 읽을 수 있다.
InputStream은 byte 코드를 반환한다. String으로 변환하려면 StreamUtil.copyToString을 사용하고 UTF-8 CharSet을 지정해주면된다.

Json 데이터를 받는 경우

먼저 Json 데이터를 파싱하여 매핑할 객체를 생성한다.

@Getter @Setter
public class HelloData {
    private String username;
    private int age;
}

스프링부트로 Spring MVC를 선택하면 기본으로 Jackson 라이브러리(json데이터 파싱)를 제공한다. Jackson 라이브러리의 ObjectMapper를 이용하여 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);

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

    }
}

objectMapper.readValue()의 파라미터에 inputStream에서 읽은 메시지(json)과 매핑할 객체를 전달하면 객체에 데이터를 세팅할 수 있다.


HttpServletResponse

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

  • HTTP 응답 코드 지정
  • 헤더 생성
  • 바디 생성
  • 편의 기능 제공(Content-Type, 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);//200

        //[Header]
        response.setContentType("text/plain");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
        response.setHeader("Pragma","no-cache");
        response.setHeader("my-header", "hello");

        //[Cookie]
        Cookie cookie = new Cookie("myCookie", "good");
        cookie.setMaxAge(600); //600초
        response.addCookie(cookie);
        
        //[Redirect]
        response.sendRedirect("basic/hello-form.html");

		//[Body]
        PrintWriter writer = response.getWriter();
        writer.println("ok");
    }
}

Http 응답 데이터 - 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>안녕?</div>");
    writer.println("</body>");
    writer.println("</html>");

}

http 응답으로 html을 반환할 때는 content-type을 `text/html``로 지정한다.

Http 응답 데이터 - JSON

@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("sonny");
    helloData.setAge(30);

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

}
  • http 응답으로 json을 반환할 때는 content-type을 application/json으로 지정한다.
  • objectMapper.writeValueAsString()은 객체를 JSON으로 변환한다.

0개의 댓글