2-2. HTTP 요청 데이터

shin·2025년 3월 30일

Spring MVC

목록 보기
9/25
post-thumbnail

😊 개요


  • HTTP 요청 메시지를 통해 클라이언트에서 서버로 데이터를 전달하는 방법을 알아봄

* 주로 사용하는 3가지 방법

(1) GET - 쿼리파라미터

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

(2) POST - HTML Form

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

(3) HTTP Message body에 데이터를 직접 담아서 요청

  • HTTP API에서 주로 사용
    • JSON, XML, TEXT
  • 데이터 형식은 주로 JSON 사용
    • POST, PUT, PATCH

테스트는 Postman으로 진행

  • POST 전송시
    • Body -> x-www-form-urlencoded 선택
    • Headers에서 content-type : application/x-www-form-urlencoded로 지정된 부분 꼭 확인


1. GET 쿼리 파라미터


전달 데이터

  • username=hello
  • age=20
  • 메시지 바디 없이, URL의 쿼리 파라미터를 사용해서 데이터를 전달

  • 예) 검색, 필터, 페이징 등에서 많이 사용하는 방식

  • 쿼리 파라미터는 URL에 다음과 같이 ?를 시작으로 보낼 수 있음

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


1) 쿼리 파라미터 조회 메서드

String username = request.getParameter("username"); //단일 파라미터 조회
Enumeration<String> parameterNames = request.getParameterNames(); //파라미터 이름들 모두 조회
Map<String, String[]> parameterMap = request.getParameterMap(); //파라미터를 Map으로 조회
String[] usernames = request.getParameterValues("username"); //복수 파라미터 조회

2) RequestParamServlet

package hello.servlet.basic.request;
 
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;

 /**
 * 1. 파라미터 전송 기능
 * http://localhost:8080/request-param?username=hello&age=20
 * <p>
 * 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");
        
 		/*
        Enumeration<String> parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String paramName = parameterNames.nextElement();
            System.out.println(paramName + "=" + 
								request.getParameter(paramName));
        }
        */
        
        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");
 		System.out.println("request.getParameter(username) = " + username);
        
 		String age = request.getParameter("age");
 		System.out.println("request.getParameter(age) = " + age);
 		System.out.println();
        
 		System.out.println("[이름이 같은 복수 파라미터 조회]");
 		System.out.println("request.getParameterValues(username)");
 		String[] usernames = request.getParameterValues("username");
        
 		for (String name : usernames) {
 			System.out.println("username=" + name);
 		}
        
        response.getWriter().write("ok");
    }
}

실행(1) - 파라미터 전송

http://localhost:8080/request-param?username=hello&age=20

결과

[전체 파라미터 조회] - start
username=hello
age=20
[전체 파라미터 조회] - end

[단일 파라미터 조회]
request.getParameter(username) = hello
request.getParameter(age) = 20

[이름이 같은 복수 파라미터 조회]
request.getParameterValues(username)
username=hello

실행(2) - 이름이 같은 동일 파라미터 전송

http://localhost:8080/request-param?username=hello&username=kim&age=20

결과

[전체 파라미터 조회] - start
username=hello
age=20
[전체 파라미터 조회] - end

[단일 파라미터 조회]
request.getParameter(username) = hello
request.getParameter(age) = 20
 
[이름이 같은 복수 파라미터 조회]
request.getParameterValues(username)
username=hello
username=kim

💡 복수 파라미터에서 단일 파라미터 조회

  • username=hello&username=kim과 같이 파라미터 이름은 하나인데, 값이 중복이면 어떻게 될까?

  • request.getParameter()은 하나의 파라미터 이름에 대해서 단 하나의 값만 있을 때 사용해야 함

    • 지금처럼 중복인 경우에는 request.getParameterValues()를 사용해야 함
  • 중복인 경우 request.getParameter()을 사용하면 request.getParameterValues()의 첫 번째 값을 반환함



2. POST HTML Form


  • HTML의 Form을 사용해서 클라이언트에서 서버로 데이터를 전송
  • 주로 회원 가입, 상품 주문 등에서 사용하는 방식

특징

  • Content-Type : application/x-www-form-urlencoded
  • 메시지 바디에 쿼리 파라미터 형식으로 데이터를 전달함
    • username=hello&age=20

src/main/webapp/basic/hello-form.html 생성

 <!DOCTYPE html>
 <html>
 <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>

실행

  • POST의 HTML Form을 전송하면 웹 브라우저는 다음 형식으로 HTTP 메시지를 만듦
    (웹 브라우저 개발자 모드 확인)
  • application/x-www-form-urlencoded 형식은 앞서 GET에서 살펴본 쿼리 파라미터 형식과 같음

    • 따라서 쿼리 파라미터 조회 메서드를 그대로 사용하면 됨

    • 클라이언트(웹 브라우저) 입장에서는 두 방식에 차이가 있지만, 서버 입장에서는 둘의 형식이 동일함

    • 따라서 request.getParameter()로 편리하게 구분없이 조회할 수 있음

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


주의사항

  • 웹 브라우저가 결과를 캐시하고 있어서, 과거에 작성했던 html 결과가 보이는 경우도 있음
    • 이때는 웹 브라우저의 새로 고침을 직접 선택해주면 됨
    • 물론 서버를 재시작하지 않아서 그럴 수도 있음

참고

  • content-type은 HTTP 메시지 바디의 데이터 형식을 지정함

  • GET URL 쿼리 파라미터 형식으로 클라이언트에서 서버로 데이터를 전달할 때는 HTTP 메시지 바디를 사용하지 않기 때문에 content-type이 없음

  • POST HTML Form 형식으로 데이터를 전달하면 HTTP 메시지 바디에 해당 데이터를 포함해서 보내기 때문에 바디에 포함된 데이터가 어떤 형식인지 content-type을 꼭 지정해야 함

    • 이렇게 폼으로 데이터를 전송하는 형식을 application/x-www-form-urlencoded라고 함

HTML 코드를 직접 작성해보긴 했지만, Postman으로 테스트를 수행하는 것이 편함



3-1. API Message Body - 단순 텍스트


  • HTTP message body에 데이터를 직접 담아서 요청

    • HTTP API에서 주로 사용
      • JSON, XML, TEXT
    • 데이터 형식은 주로 JSON 사용
    • POST, PUT, PATCH
  • 가장 단순한 텍스트 메시지를 HTTP 메시지 바디에 담아서 전송하고 읽는 예제 수행


RequestBodyStringServlet

package hello.servlet.basic.request;

import org.springframework.util.StreamUtils;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

@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);
 
 		System.out.println("messageBody = " + messageBody);
        
        response.getWriter().write("ok");
    }
}
  • HTTP 메시지 바디의 데이터를 InputStream을 사용해서 직접 읽을 수 있음
    • InputStreambyte 코드를 반환함
    • byte 코드를 우리가 읽을 수 있는 문자(String)로 보려면 문자표(Charset)를 지정해주어야 함
    • 따라서 여기서는 UTF-8 Charset을 지정해줌

문자 전송

결과:

messageBody = hello


3-2. API Message Body - JSON


  • 이번에는 HTTP API에서 주로 사용하는 JSON 형식으로 데이터 전달

1) JSON 형식 전송

결과:

messageBody = {"username": "hello", "age": 20}

2) JSON 형식 파싱 추가

  • JSON 형식으로 파싱할 수 있게 객체를 하나 생성

hello.servlet.basic.HelloData

package hello.servlet.basic;

import lombok.Getter;
import lombok.Setter;

@Getter @Setter
public class HelloData {

 	private String username;
 	private int age;
}
  • lombok이 제공하는 @Getter, @Setter 덕분에 아래 코드가 자동으로 추가됨
package hello.servlet.basic;
 
public class HelloData {

 	private String username;
 	private int age;

 	//==== lombok 생성 코드 ====//
 	public String getUsername() {
 		return username;
    }
    
 	public void setUsername(String username) {
 		this.username = username;
    }
 
 	public int getAge() {
 		return age;
    }
    
 	public void setAge(int age) {
 		this.age = age;
    }
 }

RequestBodyJsonServlet

package hello.servlet.basic.request;

import com.fasterxml.jackson.databind.ObjectMapper;
import hello.servlet.basic.HelloData;
import org.springframework.util.StreamUtils;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
 
/**
 * http://localhost:8080/request-body-json
 *
 * JSON 형식 전송
 * content-type: application/json
 * message body: {"username": "hello", "age": 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);
		System.out.println("helloData.username = " + helloData.getUsername());
		System.out.println("helloData.age = " + helloData.getAge());
        
        response.getWriter().write("ok");
    }
 }
  • JSON 결과를 파싱해서 사용할 수 있는 자바 객체로 변환하려면 Jsckson, Gson 같은 JSON 변환 라이브러리를 추가해서 사용해야 함
import com.fasterxml.jackson.databind.ObjectMapper;
  
HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);
  • 스프링 부트로 Spring MVC를 선택하면 기본적으로 Jackson 라이브러리(ObjectMapper)를 함께 제공함

실행

결과

messageBody={"username": "hello", "age": 20}
data.username=hello
data.age=20

참고

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


강의 출처 : 인프런 - 스프링 MVC 1편

profile
Backend development

0개의 댓글