주로 사용하는 방법 3가지
- 쿼리 파라미터 - GET 방식
- HTML Form - POST 방식
- HTTP Message Body - POST 방식
* 데이터 형식은 주로 JSON 사용 (레거시 코드는 간혹 XML을 사용하지만 최근에는 대부분 JSON)
1. 쿼리 파라미터 - GET 방식
- 메시지 바디 없이 URL에 쿼리 파라미터를 추가해서 데이터를 전달하는 방식 (URL에 그대로 노출되므로 주로 검색, 필터, 페이징 등에 사용)
- URL에 ?을 시작으로 보내고 &를 통해 추가 파라미터를 구분
- ex) http://localhost:8080/example?id=jaimemin&blog=tistory
- 쿼리 파라미터로 전달된 내용 -> id = jaimemin, blog = tistory
1.1 Servlet에서 조회하는 방법
@WebServlet(name = "ExampleServlet", urlPatterns = "/example") | |
public class ExampleServlet 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)) | |
); | |
// 단일 파라미터 조회 | |
String id = request.getParameter("id"); // jaimemin | |
String blog = request.getParameter("blog"); // tistory | |
// 파라미터를 이용하는 로직 중략 | |
} | |
} |
* 쿼리 파라미터를 통해 넘어온 파라미터명은 같은데 값이 여러 개일 경우에는 어떻게 처리를 할까?
- 예를 들자면, http://localhost:8080/example?id=jaimemin&blog=tistory&id=gudetama
- 상단 코드에서처럼 request.getParameter("id")를 통해 값을 받을 경우에는 첫 번째로 넘어온 jaimemin
- 둘 다 받고 싶은 경우에는 String [] ids = request.getParameterValues("id")와 같이 사용
1.2 Spring MVC Controller 방식
@Slf4j | |
@Controller | |
public class ExampleRequestParamController { | |
@RequestMapping("/request-param-version1") | |
public void requestParamVersion1(HttpServletRequest request, HttpServletResponse response) throws IOException { | |
String username = request.getParameter("username"); | |
int age = Integer.parseInt(request.getParameter("age")); | |
log.info("username = {}, age = {}", username, age); | |
response.getWriter().write("ok"); | |
} | |
@ResponseBody | |
@RequestMapping("/request-param-version2") | |
public String requestParamVersion2(@RequestParam("username") String memberName | |
, @RequestParam("age") int memberAge) { | |
log.info("username = {}, age = {}", memberName, memberAge); | |
return "ok"; | |
} | |
@ResponseBody | |
@RequestMapping("/request-param-version3") | |
public String requestParamVersion3(@RequestParam String username | |
, @RequestParam int age) { | |
log.info("username = {}, age = {}", username, age); | |
return "ok"; | |
} | |
@ResponseBody | |
@RequestMapping("/request-param-version4") | |
public String requestParamVersion4(String username, int age) { | |
log.info("username = {}, age = {}", username, age); | |
return "ok"; | |
} | |
@ResponseBody | |
@RequestMapping("/request-param-required") | |
public String requestParamRequired(@RequestParam(required = true) String username | |
, @RequestParam(required = false) int age) { | |
log.info("username = {}, age = {}", username, age); | |
return "ok"; | |
} | |
@ResponseBody | |
@RequestMapping("/request-param-default") | |
public String requestParamDefault(@RequestParam(defaultValue = "guest") String username | |
, @RequestParam(required = false, defaultValue = "-1") int age) { | |
log.info("username = {}, age = {}", username, age); | |
return "ok"; | |
} | |
@ResponseBody | |
@RequestMapping("/request-param-map") | |
public String requestParamMap(@RequestParam Map<String, Object> paramMap) { | |
log.info("username = {}, age = {}", paramMap.get("username"), paramMap.get("age")); | |
return "ok"; | |
} | |
@ResponseBody | |
@RequestMapping("/model-attribute-version1") | |
public String modelAttributeVersion1(@ModelAttribute Example example) { | |
log.info("example = {}", example); | |
return "ok"; | |
} | |
@ResponseBody | |
@RequestMapping("/model-attribute-version2") | |
public String modelAttributeVersion2(Example example) { | |
log.info("example = {}", example); | |
return "ok"; | |
} | |
} |
* @RequestParam: 파라미터 이름으로 바인딩
* @ResponseBody: HTTP message body에 직접 해당 내용 입력 (@Controller 내 해당 어노테이션이 없을 경우 ok라는 뷰를 조회)
* HTTP 파리미터명과 변수명이 같을 경우 @RequestParam 내 name 필드 생략 가능
* String, Integer와 같이 단순 타입일 경우 @RequestParam 어노테이션 생략 가능 (개인적으로는 명시하는 편이 좋을 듯)
* @RequestParam 내 required 필드는 디폴트가 true 즉, 쿼리 파라미터로 필수적으로 넘어와야 함 (required = false일 경우 필수가 아니어도 되고 defaultValue라는 키워드를 통해 넘어오지 않았을 때 디폴트 값으로 세팅 가능)
* 쿼리 파라미터를 Map 혹은 MultiValueMap으로 조회 가능하고 쿼리 파라미터의 key와 value가 1:1로 매칭 한다는 보장이 없을 경우 MultiValueMap을 사용하는 것을 권장
* @ModelAttribute: 쿼리 파라미터로 넘어온 값을 객체에 바인딩해주는 어노테이션 (실행 순서는 객체를 생성한 후 쿼리 파라미터의 이름으로 객체의 property를 찾아 값을 바인딩)
* 앞서 언급한 것처럼 String, Integer와 같은 단순 타입은 어노테이션 생략 시 @RequestParam 어노테이션이 자동으로 붙고 ArgumentResolver를 지정해둔 타입을 제외한 나머지 타입은 어노테이션 생략 시 @ModelAttribute 어노테이션이 자동으로 붙음
2. HTML Form - POST 방식
- HTML의 Form을 사용해서 데이터를 전송하는 방법
- HTTP Body 내 Form에 작성된 데이터를 포함해서 전송하기 때문에 content-type 설정 필요
- content-type은 application/x-www-form-urlencoded
- 메시지 바디 내 쿼리 파라미터 형식으로 데이터 전달
2.1 example.html
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="UTF-8"> | |
<title>HTML Form Example</title> | |
</head> | |
<body> | |
<form action="/example" method="post"> | |
id: <input type="text" name="id"/> | |
blog: <input type="text" name="blog"/> | |
<button type="submit">데이터 전송</button> | |
</form> | |
</body> | |
</html> |
2.2 ExampleServlet.java
@WebServlet(name = "ExampleServlet", urlPatterns = "/example") | |
public class ExampleServlet 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)) | |
); | |
// 단일 파라미터 조회 | |
String id = request.getParameter("id"); // jaimemin | |
String blog = request.getParameter("blog"); // tistory | |
// 파라미터를 이용하는 로직 중략 | |
} | |
} |
* 메시지 바디 내 쿼리 파라미터 형식으로 보내기 때문에 쿼리 파라미터에서 사용한 코드를 동일하게 사용하면 됩니다. * * 서버 입장에서는 둘 다 동일
* 즉, 정리하자면 request.getParameter() 메서드는 GET 방식과 POST 방식 둘 다 지원 (즉, 쿼리 파라미터 방식 내 Spring MVC 예제 코드(1.2)를 동일하게 사용 가능)
3. HTTP Message Body - POST 방식
- HTTP message body에 데이터를 직접 담아서 요청하는 방식
- HTTP Api에 주로 사용
- 대표적인 형식은 JSON (이외 XML, Text도 가능)
- POST, PUT, PATCH 방식 지원
- HTTP 메시지 바디 내 데이터를 InputStream을 통해 조회 가능
3.1 ExampleBodyTextServlet.java (Text 방식)
- content-type: text/plain
- message body 내 text 형식으로 데이터 전달
@WebServlet(name = "exampleBodyStringText", urlPatterns = "/example-body-text") | |
public class ExampleBodyTextServlet 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); | |
// messageBody를 활용하여 로직 작성 | |
} | |
} |
3.2 Spring MVC Controller 방식 (Text 방식)
@Slf4j | |
@Controller | |
public class ExampleRequestBodyStringController { | |
@PostMapping("/request-body-string-version1") | |
public void requestBodyStringVersion1(HttpServletRequest request, HttpServletResponse response) | |
throws IOException { | |
ServletInputStream inputStream = request.getInputStream(); | |
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8); | |
log.info("messageBody = {}", messageBody); | |
response.getWriter().write("ok"); | |
} | |
@PostMapping("/request-body-string-version2") | |
public void requestBodyStringVersion2(InputStream inputStream, Writer responseWriter) | |
throws IOException { | |
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8); | |
log.info("messageBody = {}", messageBody); | |
responseWriter.write("ok"); | |
} | |
@PostMapping("/request-body-string-version3") | |
public HttpEntity<String> requestBodyStringVersion3(HttpEntity<String> httpEntity) | |
throws IOException { | |
String messageBody = httpEntity.getBody(); | |
log.info("messageBody = {}", messageBody); | |
return new HttpEntity<>("ok"); | |
} | |
@ResponseBody | |
@PostMapping("/request-body-string-version4") | |
public String requestBodyStringVersion4(@RequestBody String messageBody) { | |
log.info("messageBody = {}", messageBody); | |
return "ok"; | |
} | |
} |
* InputStream(Reader): HTTP 요청 내 바디의 내용을 직접 조회
* OutputStream(Writer): HTTP 응답 메시지의 바디에 직접 결과 출력
* HttpEntity: HTTP header, body 정보를 편리하게 조회 (쿼리 파라미터 조회하는 기능과 무관)
* HttpEntity는 응답 메시지의 헤더와 바디에 직접 결과 출력도 가능 (View 조회는 불가)
* RequestEntity: HttpEntity를 상속받고 HTTP 메서드, URL 정보 추가 및 요청에 사용
* ResponseEntity: HttpEntity를 상속받고, HTTP 상태 코드 설정 가능 및 응답에 사용
* RequestBody: HTTP 메시지 바디 정보를 편리하게 조회 가능
3.3 ExampleBodyJsonServlet.java (Json 방식)
- content-type: application/json
- message body 내 json 형식으로 데이터 전달
- JSON을 파싱해서 자바 객체로 변환하기 위해서는 라이브러리가 필요한데 Spring Boot 내에는 Jackson 라이브러리가 포함되어있음 (ObjectMapper)
@WebServlet(name = "exampleBodyJsonServlet", urlPatterns = "/example-body-json") | |
public class ExampleBodyJsonServlet 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); | |
ExampleCriteria exampleCriteria = objectMapper.readValue(messageBody, ExampleCriteria.class); | |
// exampleCriteria를 활용한 로직 | |
} | |
} | |
@Getter | |
@Setter | |
public class ExampleCriteria { | |
private String id; | |
private String blog; | |
} |
3.4 Spring MVC Controller 방식 (JSON 방식)
@Slf4j | |
@Controller | |
public class ExampleRequestBodyJsonController { | |
private ObjectMapper objectMapper = new ObjectMapper(); | |
@PostMapping("/request-body-json-version1") | |
public void requestBodyJsonVersion1(HttpServletRequest request, HttpServletResponse response) | |
throws IOException { | |
ServletInputStream inputStream = request.getInputStream(); | |
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8); | |
log.info("messageBody = {}", messageBody); | |
HelloData helloData = objectMapper.readValue(messageBody, HelloData.class); | |
log.info("username = {}, age = {}", helloData.getUsername(), helloData.getAge()); | |
response.getWriter().write("ok"); | |
} | |
@ResponseBody | |
@PostMapping("/request-body-json-version2") | |
public String requestBodyJsonVersion2(@RequestBody String messageBody) | |
throws IOException { | |
log.info("messageBody = {}", messageBody); | |
Example example = objectMapper.readValue(messageBody, Example.class); | |
log.info("username = {}, age = {}", example.getUsername(), example.getAge()); | |
return "ok"; | |
} | |
@ResponseBody | |
@PostMapping("/request-body-json-version3") | |
public String requestBodyJsonVersion3(@RequestBody Example example) { | |
log.info("username = {}, age = {}", example.getUsername(), example.getAge()); | |
return "ok"; | |
} | |
@ResponseBody | |
@PostMapping("/request-body-json-version4") | |
public String requestBodyJsonVersion4(HttpEntity<Example> data) { | |
Example example = data.getBody(); | |
log.info("username = {}, age = {}", example.getUsername(), example.getAge()); | |
return "ok"; | |
} | |
@ResponseBody | |
@PostMapping("/request-body-json-version5") | |
public HelloData requestBodyJsonVersion5(@RequestBody Example example) { | |
log.info("username = {}, age = {}", example.getUsername(), example.getAge()); | |
return helloData; | |
} | |
} |
* @RequestBody: HttpEntity, @RequestBody를 사용할 경우 HTTPMessageConverter가 HTTP 메시지 바디의 내용을 사용자의 편의대로 문자나 객체 등으로 변환해줌
* HTTP 요청 시에 content-type이 application/json이어야 JSON 처리할 수 있는 HTTPMessageConverter 실행 가능
* @RequestParam, @ModelAttribute와 달리 @RequestBody는 생략 불가능 (생략할 경우 @ModelAttribute가 적용되어 버림)
* @RequestBody 요청 과정: JSON 요청 -> HTTPMessageConverter -> 객체
* @ResponseBody 응답 과정: 객체 -> HTTPMessageConverter -> JSON 응답
XML 방식은 생략
참고
인프런 스프링 MVC 1편 (김영한 강사님)
'Spring' 카테고리의 다른 글
HttpMessageConverter 간단 정리 (0) | 2021.06.11 |
---|---|
Spring MVC 구조 정리 (0) | 2021.06.09 |
빈 스코프 (Bean Scope) (0) | 2021.05.26 |
스프링 빈 생명주기 (Spring Bean Life Cycle) (0) | 2021.05.25 |
@Autowired - 의존관계 주입 (2) | 2021.05.18 |