Servlet

Jaca·2021년 10월 6일
0

순수 서블릿을 통한 개발은 JSP와 MVC 프레임워크의 발달로 인해 잘 쓰이지 않으나, 당연히 가장 기본이 중요한 것..
순수 서블릿을 통한 개발을 해보자.

간단하게 보고 가자면, 서블릿을 사용하기 위해서는 클래스에 HttpServlet 클래스로 확장하고 @WebServlet 어노테이션을 붙여준다.
그리고 service 메서드를 오버라이드를 하면 해당 urlPatterns 으로 접근시, 해당 로직이 실행된다.

urlPatterns으로 들어온 데이터를 request 매개변수를 통해 받고,
돌려줄 응답 데이터를 response 매개변수에 넣어준다.

HttpServletRequest

HttpServletRequest 를 통해, 다양한 헤더를 기본적으로 제공한다.
모두 적자니 너무 길어져 결과를 통해 어떤 정보들이 나올 수 있는지 확인하자.

Host: localhost:8080
Connection: keep-alive
sec-ch-ua: "Google Chrome";v="93", " Not;A Brand";v="99", "Chromium";v="93"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "macOS"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7

]
--- Headers - start ---
host: localhost:8080
connection: keep-alive
sec-ch-ua: "Google Chrome";v="93", " Not;A Brand";v="99", "Chromium";v="93"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "macOS"
upgrade-insecure-requests: 1
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
sec-fetch-site: none
sec-fetch-mode: navigate
sec-fetch-user: ?1
sec-fetch-dest: document
accept-encoding: gzip, deflate, br
accept-language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
--- Headers - end ---

--- REQUEST-LINE - start ---
request.getMethod() = GET
request.getProtocal() = HTTP/1.1
request.getScheme() = http
request.getRequestURL() = http://localhost:8080/request-header
request.getRequestURI() = /request-header
request.getQueryString() = null
request.isSecure() = false
--- REQUEST-LINE - end ---

--- Header 편의 조회 start ---
[Host 편의 조회]
request.getServerName() = localhost
request.getServerPort() = 8080

[Accept-Language 편의 조회]
locale = ko_KR
locale = ko
locale = en_US
locale = en
request.getLocale() = ko_KR

[cookie 편의 조회]
/* 아직 쿠키가 존재하지 않음 */

[Content 편의 조회]
request.getContentType() = null
request.getContentLength() = -1
request.getCharacterEncoding() = UTF-8
--- Header 편의 조회 end ---

--- 기타 조회 start ---
[Remote 정보]
request.getRemoteHost() = 0:0:0:0:0:0:0:1
request.getRemoteAddr() = 0:0:0:0:0:0:0:1
request.getRemotePort() = 62906

[Local 정보]
request.getLocalName() = localhost
request.getLocalAddr() = 0:0:0:0:0:0:0:1
request.getLocalPort() = 8080
--- 기타 조회 end ---

본격적인 HttpServletRequest를 통해서 HTTP 요청 데이터를 다루어보자
앞서 살펴보았듯, HTTP 요청 데이터를 통해 클라이언트에서 서버로 데이터를 전달하는 방법 주로 3가지를 사용한다.

GET - 쿼리 파라미터

  • /url?username=hello&age=20
  • 메시지 바디 없이, URL의 쿼리 파라미터에 데이터를 포함해서 전달 예)
  • 검색, 필터, 페이징등에서 많이 사용하는 방식

username=hello&username=kim 과 같이 파라미터 이름은 하나인데, 값이 중복이면 어떻게 될까?
request.getParameter() 는 하나의 파라미터 이름에 대해서 단 하나의 값만 있을 때 사용해야 한다. 지금처럼 중복일 때는 request.getParameterValues() 를 사용해야 한다.
참고로 이렇게 중복일 때 request.getParameter() 를 사용하면 request.getParameterValues() 의 첫 번째 값을 반환한다.

POST - HTML Form

  • content-type: application/x-www-form-urlencoded
  • 메시지 바디에 쿼리 파리미터 형식으로 전달, username=hello&age=20
  • 예) 회원 가입, 상품 주문, HTML Form 사용

application/x-www-form-urlencoded 형식은 앞서 GET에서 살펴본 쿼리 파라미터 형식과 같다. 따라서 쿼리 파라미터 조회 메서드를 그대로 사용하면 된다.
클라이언트(웹 브라우저) 입장에서는 두 방식에 차이가 있지만, 서버 입장에서는 둘의 형식이 동일하므로, request.getParameter() 로 편리하게 구분없이 조회할 수 있다.

정리하면 request.getParameter() 는 GET URL 쿼리 파라미터 형식도 지원하고, POST HTML Form 형식도 둘 다 지원한다.

결국 같은 코드를 통해서 소화가능하다.

API 메시지 바디

  • HTTP message body에 데이터를 직접 담아서 요청
  • HTTP API에서 주로 사용, JSON, XML, TEXT
  • 데이터 형식은 주로 JSON 사용
  • POST, PUT, PATCH

단순 텍스트

HTTP 메시지 바디의 데이터를 InputStream을 사용해서 직접 읽을 수 있다.
inputStream은 byte 코드를 반환한다. byte 코드를 우리가 읽을 수 있는 문자(String)로 보려면 문자표(Charset)를 지정해주어야 한다. 여기서는 UTF_8 Charset을 지정해주었다.

JSON

  • content-type: application/json
  • message body: {"username": "hello", "age": 20}
  • 결과: messageBody = {"username": "hello", "age": 20}

JSON 형식으로 파싱할 수 있게 DTO 처럼 객체를 하기 위해 String: usernameint: age 를 가진 HelloData를 만들었다.

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

HTML form 데이터도 메시지 바디를 통해 전송되므로 직접 읽을 수 있다. 하지만 편리한 파리미터 조회 기능( request.getParameter(...) )을 이미 제공하기 때문에 파라미터 조회 기능을 사용하면 된다.

HttpServletResponse

HttpServletResponse의 역할은 크게

  • Http 응답 메세지 생성
    • Http 응답코드 생성
    • 헤더 생성
    • 바디 생성
  • 편의 기능 제공
    • Content-Tpye, 쿠키, Redirect

가 있다.

단순 텍스트 응답

Content-Type, 쿠키, Redirect 정보를 넣어주거나,
단순 텍스트 응답 따위를 ( writer.println("ok"); ) 넣어줄 수 있다.

HTML 응답

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

결과

API JSON 응답

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

application/json 은 스펙상 utf-8 형식을 사용하도록 정의되어 있다. 그래서 스펙에서 charset=utf-8
과 같은 추가 파라미터를 지원하지 않는다. 따라서 application/json 이라고만 사용해야지

application/json;charset=utf-8 이라고 전달하는 것은 의미 없는 파라미터를 추가한 것이 된다.
response.getWriter()를 사용하면 추가 파라미터를 자동으로 추가해버린다.
이때는 response.getOutputStream()으로 출력하면 그런 문제가 없다.

profile
I am me

0개의 댓글