빙응의 공부 블로그
[Spring]스프링 MVC 1편 - 스프링 MVC 기본 기능(요청) 본문

📝요청 매핑
스프링 컨트롤러는 다양한 방법으로 요청을 매핑할 수 있다.
기본 요청
/**
* 기본 요청
* 둘다 허용 /hello-basic, /hello-basic/
* HTTP 메서드 모두 허용 GET, HEAD, POST, PUT, PATCH, DELETE
*/
@RequestMapping("/hello-basic")
public String helloBasic() {
log.info("helloBasic");
return "ok";
}
HTTP 메서드 매핑
/**
* method 특정 HTTP 메서드 요청만 허용
* GET, HEAD, POST, PUT, PATCH, DELETE
*/
@RequestMapping(value = "/mapping-get-v1", method = RequestMethod.GET)
public String mappingGetV1() {
log.info("mappingGetV1");
return "ok";
}
@GetMapping(value = "/mapping-get-v2")
public String mappingGetV2() {
log.info("mapping-get-v2");
return "ok";
}
PathVariable(경로 변수) 매핑
들어오는 url 경로를 이용해서 파라미터를 받을 수 있다.
/**
* PathVariable 사용
* 변수명이 같으면 생략 가능
* @PathVariable("userId") String userId -> @PathVariable String userId
*/
@GetMapping("/mapping/{userId}")
public String mappingPath(@PathVariable("userId") String data) {
log.info("mappingPath userId={}", data);
return "ok";
}
/**
* PathVariable 사용 다중
*/
@GetMapping("/mapping/users/{userId}/orders/{orderId}")
public String mappingPath(@PathVariable String userId, @PathVariable Long orderId) {
log.info("mappingPath userId={}, orderId={}", userId, orderId);
return "ok";
}
들어오는 파라미터 조건 매핑
특정 파라미터 값을 검사하여 매핑을 진행할 수 있다.
/**
* 파라미터로 추가 매핑
* params="mode",
* params="!mode"
* params="mode=debug"
* params="mode!=debug" (! = )
* params = {"mode=debug","data=good"}
*/
@GetMapping(value = "/mapping-param", params = "mode=debug")
public String mappingParam() {
log.info("mappingParam");
return "ok";
}
들어오는 헤더 조건 매핑
특정 헤더 값을 검사하여 매핑을 진행할 수 있다.
/**
* 특정 헤더로 추가 매핑
* headers="mode",
* headers="!mode"
* headers="mode=debug"
* headers="mode!=debug" (! = )
*/
@GetMapping(value = "/mapping-header", headers = "mode=debug")
public String mappingHeader() {
log.info("mappingHeader");
return "ok";
}
미디어 타입 조건 매핑 Content-Type, consume, Accept
타입들은 헤더로 검사가 가능하지만 추가 로직이 필요하여 따로 다르게 해야한다.
/**
* Content-Type 헤더 기반 추가 매핑 Media Type
* consumes="application/json"
* consumes="!application/json"
* consumes="application/*"
* consumes="*\/*"
* MediaType.APPLICATION_JSON_VALUE
*/
@PostMapping(value = "/mapping-consume", consumes = "application/json")
public String mappingConsumes() {
log.info("mappingConsumes");
return "ok";
}
/**
* Accept 헤더 기반 Media Type
* produces = "text/html"
* produces = "!text/html"
* produces = "text/*"
* produces = "*\/*"
*/
@PostMapping(value = "/mapping-produce", produces = "text/html")
public String mappingProduces() {
log.info("mappingProduces");
return "ok";
}
📝요청 매핑 - API 예시
회원 관리를 HTTP API로 만든다 생각하고 매핑을 어떻게 하는지 알아보자.
데이터 전달은 안한다.
- 회원 목록 조회 : GET /users
- 회원 등록 : POST /users
- 회원 조회 : GET /users/{userId}
- 회원 수정 : PATCH /users/{userId}
- 회원 삭제 : DELETE /users/{userId}
@RestController
@Slf4j
@RequestMapping("/mapping/users")
public class MappingClassController {
@GetMapping
public String user(){
return "get users";
}
@PostMapping
public String addUser(){
return "post user";
}
@GetMapping("{userId}")
public String findUser(@PathVariable String userId){
return "get userId=" + userId;
}
@PatchMapping("{userId}")
public String updateUser(@PathVariable String userId){
return "update userId="+userId;
}
@DeleteMapping("{userId}")
public String deleteUser(@PathVariable String userId){
return "delete userId="+userId;
}
}
📝HTTP 요청 - 기본, 헤더 조회
애노테이션 기반의 스프링 컨트롤러는 다양한 파라미터를 지원한다.
이번 시간에는 HTTP 헤더 정보를 조회하는 방법을 알아보자
@RequestMapping("/headers")
public String headers(HttpServletRequest request,
HttpServletResponse response,
HttpMethod httpMethod,
Locale locale,
@RequestHeader MultiValueMap<String, String>
headerMap,
@RequestHeader("host") String host,
@CookieValue(value = "myCookie", required = false)
String cookie
) {
log.info("request={}", request);
log.info("response={}", response);
log.info("httpMethod={}", httpMethod);
log.info("locale={}", locale);
log.info("headerMap={}", headerMap);
log.info("header host={}", host);
log.info("myCookie={}", cookie);
return "ok";
}
}
위 코드의 파라미터들은 다음과 같다.
- HttpServletRequest
- HttpServlertResponse
- HttpMethod : HTTP 메소드를 조회한다.
- Locale : Locale 정보를 조회(ko,US 등)
- @RequestHeader MultiValueMap headerMap : 모든 HTTP 헤더를 조회
- RequestHeader("host") String host : 특정 HTTP 헤더를 조회
- @CookieValue(value = "myCookie", required = false) String cookie : 특정 쿠키를 조회
MultiValueMap
MAP과 유사한데, 하나의 키에 여러 값을 받을 수 있다.
HTTP header, HTTP 쿼리 파라미터와 같이 하나의 키에 여러 값을 받을 때 사용한다.
🚩참고
@Controller 사용 가능 파라미터 목록
Method Arguments :: Spring Framework
Method Arguments :: Spring Framework
JDK 8’s java.util.Optional is supported as a method argument in combination with annotations that have a required attribute (for example, @RequestParam, @RequestHeader, and others) and is equivalent to required=false.
docs.spring.io
@Controller 사용 가능 응답 값 목록
Return Values :: Spring Framework
Return Values :: Spring Framework
A single value type, e.g. Mono, is comparable to returning DeferredResult. A multi-value type, e.g. Flux, may be treated as a stream depending on the requested media type, e.g. "text/event-stream", "application/json+stream", or otherwise is collected to a
docs.spring.io
📝HTTP 요청 - 쿼리 파라미터, HTML Form
서블릿에서 학습했던 HTTP 요청 데이터를 조회하는 방법을 생각해보자
- GET - 쿼리 파라미터
- /url **?username=hello&age=20
- 메시지 바디 없이, URL의 쿼리 파라미터에 데이터를 포함해서 전달
- 예) 페이징, 검색, 필터
- POST - HTML Form
- content-type: application/x-www-form-urlencoded
- 메시지 바디에 쿼리 파라미터 형식으로 전달 username = hello&age=20
- 예) 회원 가입, 상품 주문
- HTTP message body에 데이터를 직접 담아서 요청
- HTTP API에서 주로 사용, JSON, XML, TEXT
서블릿 방식으로 조회하기
HttpServletRequest의 request.getParameter()을 통해서 두가지 요청 모두 조회할 수 있다.
GET, 쿼리 파라미터 전송
예시
http://localhost:8080/request-param?username=hello&age=20
POST, HTML Form 전송
예시
POST /request-param
content-type: application/x-www-form-urlencoded
username=hello&age=20
/**
* 반환 타입이 없으면서 이렇게 응답에 값을 직접 집어넣으면, view 조회X
*/
@RequestMapping("/request-param-v1")
public void requestParamV1(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");
}
애노테이션 방식으로 조회하기
스프링이 제공하는 @RequestParam을 사용하면 정말 편하게 사용할 수 있다.
@ResponseBody
@RequestMapping("/request-param-v2")
public String requestParamV2(
@RequestParam("username") String memberName,
@RequestParam("age") int memberAge) {
log.info("username={}, age={}", memberName, memberAge);
return "ok";
}
/**
* @RequestParam 사용
* HTTP 파라미터 이름이 변수 이름과 같으면 @RequestParam(name="xx") 생략 가능
*/
@ResponseBody
@RequestMapping("/request-param-v3")
public String requestParamV3(
@RequestParam String username,
@RequestParam int age) {
log.info("username={}, age={}", username, age);
return "ok";
}
/**
* @RequestParam 사용
* String, int 등의 단순 타입이면 @RequestParam 도 생략 가능
*/
@ResponseBody
@RequestMapping("/request-param-v4")
public String requestParamV4(String username, int age) {
log.info("username={}, age={}", username, age);
return "ok";
}
이렇게 점점 생략이 가능하다.
- 그러나 내 생각은 너무 생략하는 것은 가독성을 해친다 생각한다.
파라미터 필수 여부로 조회하기
아래 코드처럼 파라미터가 없을 시 오류가 나올 수 있게 할 수 있다.
/**
* @RequestParam.required
* /request-param-required -> username이 없으므로 예외
*
* 주의!
* /request-param-required?username= -> 빈문자로 통과
*
* 주의!
* /request-param-required
* int age -> null을 int에 입력하는 것은 불가능, 따라서 Integer 변경해야 함(또는 다음에 나오는
defaultValue 사용)
*/
@ResponseBody
@RequestMapping("/request-param-required")
public String requestParamRequired(
@RequestParam(required = true) String username,
@RequestParam(required = false) Integer age) {
log.info("username={}, age={}", username, age);
return "ok";
}
참고로 기본 값 설정도 가능하다
@RequestParam(required = true, defaultValue = "guest") String username
파라미터를 Map으로 조회하기
/**
* @RequestParam Map, MultiValueMap
* Map(key=value)
* MultiValueMap(key=[value1, value2, ...]) ex) (key=userIds, value=[id1, id2])
*/
@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";
}
객체로 조회하기 - @ModelAttribute
실제 개발을 하면서 요청 파라미터를 받아서 필요한 객체를 만들고 그 객체에 값을 넣어주어야 한다.
스프링은 이 과정을 완전히 자동화 해주는 @ModelAttribute 기능을 제공한다.
/**
* @ModelAttribute 사용
* 참고: model.addAttribute(helloData) 코드도 함께 자동 적용됨, 뒤에 model을 설명할 때 자세히
설명
*/
@ResponseBody
@RequestMapping("/model-attribute-v1")
public String modelAttributeV1(@ModelAttribute HelloData helloData) {
log.info("username={}, age={}", helloData.getUsername(),
helloData.getAge());
return "ok";
}
스프링 MVC는 다음과 같은 실행을 한다.
- HelloData 객체를 생성
- 요청 파라미터의 이름으로 HelloData 객체의 프로퍼티를 찾아 Setter을 호출해서 입력한다.
📝HTTP 요청 - HTTP message body
우리는 바로 직전에 HTTP Form과 쿼리 파라미터를 보았다 남은 HTTP message body 데이터를 알아보자
- HTTP message body에 데이터를 직접 담아서 요청
- HTTP API에서 주로 사용, JSON, XML, TEXT
- POST, PUT, PATCH
요청 파라미터와 다르게, HTTP 메시지 바디를 통해 데이터가 직접 넘어오는 경우 @RequestParam, @ModelAttribute를 사용할 수 없다.
서블릿 방식으로 조회
@PostMapping("/request-body-string-v1")
public void requestBodyString(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");
}
inpustStream을 통해서 받을 수 있다.
Input, Output 스트림으로 조회
/**
* InputStream(Reader): HTTP 요청 메시지 바디의 내용을 직접 조회
* OutputStream(Writer): HTTP 응답 메시지의 바디에 직접 결과 출력
*/
@PostMapping("/request-body-string-v2")
public void requestBodyStringV2(InputStream inputStream, Writer responseWriter) throws IOException {
String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
log.info("messageBody={}", messageBody);
responseWriter.write("ok");
}
스프링 컨트롤러는 input과 output 모두 지원하기 때문에 HttpServlet을 생략할 수 있다.
HttpMessageConverter으로 조회
/**
* HttpEntity: HTTP header, body 정보를 편리하게 조회
* - 메시지 바디 정보를 직접 조회(@RequestParam X, @ModelAttribute X)
* - HttpMessageConverter 사용 -> StringHttpMessageConverter 적용
*
* 응답에서도 HttpEntity 사용 가능
* - 메시지 바디 정보 직접 반환(view 조회X)
* - HttpMessageConverter 사용 -> StringHttpMessageConverter 적용
*/
@PostMapping("/request-body-string-v3")
public HttpEntity<String> requestBodyStringV3(HttpEntity<String> httpEntity) {
String messageBody = httpEntity.getBody();
log.info("messageBody={}", messageBody);
return new HttpEntity<>("ok");
}
- HttpEntitiy를 상속받는 다음 객체들도 있다.
- RequestEntity
- HttpMethod, url 저옵를 추가, 요청에서 사용
- ResponseEntity
- HTTP 상태 코드 설정 가능, 응답에서 사용
- RequestEntity
@RequestBody로 조회
/**
* @RequestBody
* - 메시지 바디 정보를 직접 조회(@RequestParam X, @ModelAttribute X)
* - HttpMessageConverter 사용 -> StringHttpMessageConverter 적용
*
* @ResponseBody
* - 메시지 바디 정보 직접 반환(view 조회X)
* - HttpMessageConverter 사용 -> StringHttpMessageConverter 적용
*/
@ResponseBody
@PostMapping("/request-body-string-v4")
public String requestBodyStringV4(@RequestBody String messageBody) {
log.info("messageBody={}", messageBody);
return "ok";
}
- @RequestBody
- HTTP 메시지 바디 정보를 편리하게 조회할 수 있다. 참고로 헤더 정보가 필요하면 HttpEntity를 사용하거나 @RequestHeader을 사용하면 된다.
🚩정리
- 요청 파라미터 조회 : @RequestParam, @ModelAttribute
- HTTP 메시지 바디를 직접 조회하는 기능 : @RequestBody
📝HTTP 요청 메시지 - JSON
이번에는 HTTP API에서 주로 사용하는 JSON 데이터 형식을 조회해보자
들어오는 객체는 다음과 같다.
{"username":"hello", "age":20}
/**
* @RequestBody 생략 불가능(@ModelAttribute 가 적용되어 버림)
* HttpMessageConverter 사용 -> MappingJackson2HttpMessageConverter (content-type:
application/json)
*
* @ResponseBody 적용
* - 메시지 바디 정보 직접 반환(view 조회X)
* - HttpMessageConverter 사용 -> MappingJackson2HttpMessageConverter 적용(Accept:
application/json)
*/
@ResponseBody
@PostMapping("/request-body-json-v5")
public HelloData requestBodyJsonV5(@RequestBody HelloData data) {
log.info("username={}, age={}", data.getUsername(), data.getAge());
return data;
}
결론부터 말하자면 기본 택스트와 비슷하게 동작한다.
- @RequestBody를 요청
- JSON 요청 -> HTTP 메시지 컨버터 -> 객체로 바뀌어 컨트롤러가 사용할 수 있게한다.
- @ResponseBody로 응답
- 객체 -> HTTP 메시지 컨버터 -> JSON 응답로 바뀌어 응답으로 나가게 된다.
'Spring > 인프런_개념' 카테고리의 다른 글
| [Spring]스프링 MVC 2편 - 메시지, 국제화 (2) | 2024.01.24 |
|---|---|
| [Spring]스프링 MVC 1편 - 스프링 MVC 기본 기능(응답) (0) | 2024.01.15 |
| [Spring]스프링 MVC 1편 - 구조 이해2 (0) | 2024.01.12 |
| [Spring]스프링 MVC 1편 - 구조 이해 (1) | 2024.01.11 |
| [Spring]스프링 MVC 1편 - 프론트 컨트롤러 (0) | 2024.01.09 |