Programming-[Backend]/Spring

[스프링 웹MVC] 3. HTTP 요청 정보 : 서블릿 - HttpServletRequest

컴퓨터 탐험가 찰리 2021. 7. 18. 18:16
728x90
반응형

 

서블릿이 제공하는 요청에 대한 정보를 담고 있는 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 정보도 뽑아낼 수 있다. getHeaderNamesgetHeader 메서드를 이용하면 된다. 이 내용은 이전 글에서 살펴봤던 스프링부트에서 제공하는 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

 

728x90
반응형