서블릿이 제공하는 요청에 대한 정보를 담고 있는 HttpServletRequest 객체에 대해서 더 자세히 알아보자.
1. Header 정보
Header Start Line 정보
HttpServletRequest에서는 아래와 같은 다양한 메서드를 제공한다. 브라우저에서 localhost:8080/request-header?username=charlie 로 접속하여 결과를 출력해보자. 그리고 출력 함수를 [Ctrl + Alt + m] 단축키로 printStartLine이라는 메서드로 뽑아내자.
RequestHeaderServlet 파일 일부 - printStartLine 메서드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
@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 START---");
System.out.println("request.getMethod = " + request.getMethod());
System.out.println("request.getProtocol = " + request.getProtocol());
System.out.println("request.getScheme = " + request.getScheme());
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());
System.out.println("---REQUEST END ---");
System.out.println();
}
|
cs |
Header 정보
header 정보도 뽑아낼 수 있다. getHeaderNames와 getHeader 메서드를 이용하면 된다. 이 내용은 이전 글에서 살펴봤던 스프링부트에서 제공하는 logging ...=debug의 기능 중 일부와 같다고 할 수 있다. asIterator(), forEachRemaining() 메서드도 아래와 같이 사용할 수 있다는 것을 기억해두자.
RequestHeaderServlet - printHeaders 메서드
1
2
3
4
5
6
|
private void printHeaders(HttpServletRequest request) {
Enumeration<String> headerNames = request.getHeaderNames();
headerNames.asIterator().forEachRemaining(headerName -> {
System.out.println(headerName + ": " + request.getHeader(headerName));
});
}
|
cs |
Header에 담긴 모든 정보가 순차적으로 출력되는 것을 확인할 수 있다.
Header 편의 정보
다음으로 Header 편의 정보이다. Accept-Language는 개발자 도구에서 ko-KR, en-US 등이 순서대로 나열되어 있는 것을 볼 수 있는데, 이것은 사용자가 브라우저에서 선호하는 언어를 설정해놓으면 우선순위대로 표시하는 것이다.
RequestHeaderServlet - printHeaderUtils 메서드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
private void printHeaderUtils(HttpServletRequest request) {
System.out.println("--- Header 편의 조회 ---");
System.out.println("[Host]");
System.out.println("request.getServerName() = " + request.getServerName());
System.out.println("request.getServerPort() = " + request.getServerPort());
System.out.println();
System.out.println("[Accept-Language]");
request.getLocales().asIterator().forEachRemaining(locale -> {
System.out.println("locale = " + locale);
});
System.out.println();
System.out.println("[cookie]");
Cookie[] cookies = request.getCookies();
if(cookies != null) {
for (Cookie cookie : cookies) {
System.out.println(cookie.getName() + ": " + cookie.getValue());
}
}
System.out.println();
System.out.println("[Content]");
System.out.println("request.getContentType() = " + request.getContentType());
System.out.println("requesst.getContentLength() = " + request.getContentLength());
System.out.println("request.getCharacterEncoding() = " + request.getCharacterEncoding());
System.out.println();
System.out.println("--- Header 편의 조회 ---");
}
|
cs |
그리고 단순 GET request로는 Content 관련 내용을 출력할 수 없으므로 Postman을 통해서 POST request를 전송해보자.
기타 정보
마지막으로 기타 정보를 조회해보자. 네트워크 커넥션의 정보를 바탕으로 Remote는 요청이 오는 곳의 정보, Local은 구동 중인 내 서버의 정보를 표시해준다.
RequestHeaderServlet - printEtc 메서드
1
2
3
4
5
6
7
8
9
10
11
12
13
|
private void printEtc(HttpServletRequest request) {
System.out.println(" --- 기타 조회 --- ");
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("[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(" --- 기타 조회 --- ");
}
|
cs |
2. 대표적인 HTTP 요청 데이터 3가지
HttpServletRequest 객체를 잘 이용하기 위해서는, HTTP의 요청 데이터에 대해서 잘 이해하고 있어야 한다. HTTP 요청 데이터의 종류와 상세 내용들에 대해서 알아보자. HTTP 요청 데이터는 크게 3가지로 구분할 수 있다.
1. GET - 쿼리 파라미터
검색, 필터, 페이징 등에서 많이 사용하는 방식이다.
HTTP 요청의 body 부분없이 url상 쿼리 파라미터만 추가하여 전달한다.
(ex) url : www.charlie.com?username=charlie&phone=010-1234-5678
2. POST - HTML Form
회원 가입, 상품 주문, 양식 제출 등에서 많이 사용한다.
body에 쿼리파라미터 형식으로 전달한다. content-type이 application/x-www.form-urlencoded 이다. body에 HTML 형식으로 작성할 뿐, 데이터의 형식은 GET 쿼리 파라미터와 유사하다.
(ex)
<form action = "/save" method ="post">
<input type = "text", name = "username" />
<input type = "text", phone= "010-1234-5678" />
<button type = "submit"> 제출 </button>
</form>
3. POST, PUT, PATCH - HTTP message body 직접 이용
데이터를 직접 전달하는 HTTP API 방식을 사용한다.
데이터 형식은 주로 JSON을 사용한다.
{"username":"charlie", "phone":"010-1234-5678"}
3. GET 쿼리 파라미터
다음 메서드들을 이용하여 URL 상의 쿼리 파라미터 값들을 조회할 수 있다.
- getParameterNames() : 전체 파라미터를 조회한다.
- getParameter() : key값인 parameterName을 인자로 받아서 value값을 반환한다.
- getParameterValues() : 중복된 key값이 있는 경우, 예를 들어 username=charlie&username=bravo로 오면 value값인 charlie, bravo를 모두 출력해준다.
참고로 중복된 key값이 있는 경우는 거의 없고 혹시라도 이런 경우라면 개발자가 반드시 인지하고 있어야 한다. 이렇게 중복된 key값이 있는 경우에는 getParameter()를 사용하면 맨 앞에 있는 파라미터 value가 반환된다.
RequestParamServlet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
@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("[이름이 같은 복수 파라미터 조회");
String[] usernames = request.getParameterValues("username");
for (String name : usernames) {
System.out.println("name = " + name);
}
response.getWriter().write("ok");
}
}
|
cs |
4. POST HTML Form
Form 형식의 데이터를 요청해보자. 우선 아래와 같이 <form> 태그를 포함한 HTML 파일을 만든다.
java/webapp/basic/hello-form.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/request-param" method="post">
username: <input type="text" name="username"/>
age : <input type="text" name="age"/>
<button type="submit">전송</button>
</form>
</body>
</html>
|
cs |
이 때, 해당 파일의 접속 주소는 webapp 디렉토리 아래의 디렉토리명을 포함한다. basic 디렉토리 아래에 hello-form.html 파일을 만들었으므로, 접속 주소도 localhost:8080/basic/hello-form.html 이 된다.
서버에서는 GET 쿼리 파라미터 형식이나, POST HTML Form 형식이 같다
위에서 생성한 html form 양식 데이터에 값을 입력하고 개발자 도구 - Network 탭에서 확인해보면, Content-type ; x-www-form-urlencoded이고, form data로 message body가 입력됨을 확인할 수 있다.
그리고 서버의 콘솔창에서도 위 'GET 쿼리 파라미터' 섹션에서 작성하였던 결과가 출력되는 것을 확인할 수 있다. 이것은 form 태그의 action 값이 이전에 지정했던 /request-param과 동일하기 때문이다. 그리고 request.getParameter(), request.getParameterNames() 등의 메서드가 똑같이 적용되기 때문이다.
※postman 사용
위와 같이 html 파일을 만들고, form 태그를 활용해도 되지만, postman을 이용하여 x-www-form-urlencoded 타입으로 POST 메시지를 보내면 손쉽게 이런 작업을 할 수 있다.
5. API 메시지 바디 - JSON
HTTP 메시지의 바디 부분에 데이터를 직접 넣어주는 방식이다. 원래는 XML 형식 등도 사용했었지만, 이제는 대부분이 JSON 방식을 사용한다. content-type:application/json 이며, POST 방식으로 요청을 보낸다. 아래와 같이 Servlet을 하나 만들자.
JSON 데이터 받아보기
java/hello/servlet/basic/request/RequestBodyJsonServlet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
@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);
String username = helloData.getUsername();
int age = helloData.getAge();
System.out.println("helloData.username = " + username);
System.out.println("helloData.age = " + age);
response.getWriter().write("ok");
}
}
|
cs |
ServletInputStream
request.getInputStream()을 통해 바이트 형태로 넘어온 정보를 ServletInputStream 타입으로 저장할 수 있다. 그리고 StreamUtils.copyToString()으로 바이트를 String으로 인코딩하여 표시할 수 있다. copyToString() 메서드에서 두 번째 파라미터로 인코딩 형식을 지정해주어야 함을 기억하자.
그리고 나서 postman을 이용해서 JSON 형식의 body가 담긴 HTTP 요청을 전송해보자.
콘솔창에는 다음과 같이 body내의 JSON 정보가 출력되는 것을 확인할 수 있다.
클래스 필드값에 요청 body 정보 받아오기
요청상의 파라미터, 즉 username과 age를 특정 클래스의 필드값으로 지정하면 해당 클래스에 요청 body의 정보값을 받아올 수 있다. 우선 HelloData.class를 만들어서 username과 age를 받아올 수 있도록 하자. @Getter, @Setter 어노테이션을 적용하여 요청 정보값을 set할 수 있게 만들어주어야 한다.
java/hello/servlet/basic/request/HelloData.java
1
2
3
4
5
6
|
@Getter
@Setter
public class HelloData {
private String username;
private int age;
}
|
cs |
스프링은 JSON 라이브러리를 바탕으로 jackson이라는 라이브러리를 사용한다. 여기서 ObjectMapper.readValue() 메서드를 사용하여 요청 정보를 특정 클래스의 필드값 형태로 받아올 수 있게 된다. Jasckson 라이브러리 외에 Gson 라이브러리도 사용할 수 있지만, 스프링부트의 Spring MVC를 사용하면 기본적으로 Jasckson 라이브러리를 함께 제공한다.
java/hello/servlet/basic/request/RequestBodyJsonServlet.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
@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);
String username = helloData.getUsername();
int age = helloData.getAge();
System.out.println("helloData.username = " + username);
System.out.println("helloData.age = " + age);
response.getWriter().write("ok");
}
}
|
cs |
참조
1. 인프런_스프링 MVC 1편 - 백엔드 웹개발 핵심 기술_김영한 님 강의
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1
'Programming-[Backend] > Spring' 카테고리의 다른 글
[스프링 웹MVC] 5. 서블릿, JSP 로 회원관리 웹앱 만들기 (0) | 2021.07.24 |
---|---|
[스프링 웹MVC] 4. HTTP 응답 정보 : 서블릿 - HttpServletResponse (0) | 2021.07.19 |
[스프링 웹MVC] 2. 프로젝트 생성과 웹서블릿 기본 개념 (0) | 2021.07.18 |
[스프링 웹MVC] 1. 웹 애플리케이션 이해 (0) | 2021.07.17 |
[스프링 기초] 17. 빈 스코프 : 웹 스코프와 프록시 (0) | 2021.07.16 |