TIL

스프링 MVC 기본 기능

요청 매핑

PathVariable(경로 변수) 사용

@GetMapping("/mapping/{userId}") 
public String mappingPath(@PathVariable("userId") String data)
@GetMapping("/mapping/users/{userId}/orders/{orderId}") 
public String mappingPath(@PathVariable String userId, @PathVariable Long orderId)

미디어 타입 조건 매핑 - HTTP 요청 Content-Type, consume

@PostMapping(value = "/mapping-consume", consumes = "application/json")

미디어 타입 조건 매핑 - HTTP 요청 Accept, produce

@PostMapping(value = "/mapping-produce", produces = "text/html")

Content-Type 헤더Accept 헤더 둘 다 데이터 타입(MIME)을 다루는 헤더이다. 하지만 Content-Type 헤더는 현재 전송하는 데이터가 어떤 타입인지에 대한 설명을 하는 개념이고, Accept 헤더는 클라이언트가 서버에게 어떤 특정한 데이터 타입을 보낼 때 클라이언트가 보낸 특정 데이터 타입으로만 응답을 해야한다.

HTTP 요청 - 기본, 헤더 조회

애노테이션 기반의 스프링 컨트롤러는 다양한 파라미터를 지원한다.

HTTP 요청 파라미터 - 쿼리 파라미터, HTML Form

클라이언트에서 서버로 요청 데이터를 전달할 때는 주로 다음 3가지 방법을 사용한다.

GET 쿼리 파리미터 전송 방식이든, POST HTML Form 전송 방식이든 둘 다 형식이 같으므로 구분 없이 조회할 수 있다.

이것을 간단히 요청 파라미터(request parameter) 조회라 한다.

HTTP 요청 파라미터 - @RequestParam

스프링이 제공하는 @RequestParam을 사용하면 요청 파라미터를 매우 편리하게 사용할 수 있다

@RequestMapping("/request-param-v2") 
public String requestParamV2(@RequestParam("username") String memberName,
														 @RequestParam("age") int age)

HTTP 파라미터 이름이 변수 이름과 같으면 @RequestParam(name="xx") 생략 가능

public String requestParamV4(String username, int age)

파라미터 필수 여부 - requestParamRequired

@RequestMapping("/request-param-required") 
public String requestParamRequired(@RequestParam(required = false) String username,
                                    @RequestParam(required = false) int age)

기본 값 적용 - requestParamDefault

@RequestMapping("/request-param-default") 
public String requestParamDefault(@RequestParam(defaultValue = "guest") String username, 
                                  @RequestParam(defaultValue = "-1") int age)

파라미터를 Map으로 조회하기 - requestParamMap

@RequestMapping("/request-param-map") 
public String requestParamMap(@RequestParam Map<String, Object> paramMap) {

HTTP 요청 파라미터 - @ModelAttribute

실제 개발을 하면 요청 파라미터를 받아서 필요한 객체를 만들고 그 객체에 값을 넣어주어야 한다.

스프링은 이 과정을 완전히 자동화해주는 @ModelAttribute 기능을 제공한다.

@RequestMapping("/model-attribute-v1") 
public String modelAttributeV1(@ModelAttribute HelloData helloData) {

스프링MVC는 @ModelAttribute가 있으면 다음을 실행한다.

@ModelAttribute 생략 - modelAttributeV2

public String modelAttributeV2(HelloData helloData) {

스프링은 해당 생략시 다음과 같은 규칙을 적용한다.

@ModelAttribute – model.addAttribute 생략

@PostMapping("/add") 
public String addItemV2(@ModelAttribute("item") Item item) {

HTTP 요청 메시지 - 단순 텍스트

요청 파라미터와 다르게, HTTP 메시지 바디를 통해 데이터가 직접 데이터가 넘어오는 경우는 @RequestParam , @ModelAttribute를 사용할 수 없다. (물론 HTML Form 형식으로 전달되는 경우는 요청 파라미터로 인정된다.)

@RequestBody - requestBodyStringV4

@ResponseBody @PostMapping("/request-body-string-v4") 
public String requestBodyStringV4(@RequestBody String messageBody) throws IOException {
}

@RequestBody

@ResponseBody

@ResponseBody를 사용하면 응답 결과를 HTTP 메시지 바디에 직접 담아서 전달할 수 있다. 물론 이 경우에도 view를 사용하지 않는다.

HTTP 요청 메시지 - JSON

requestBodyJsonV2 - @RequestBody 문자 변환

@ResponseBody @PostMapping("/request-body-json-v2") 
public String requestBodyJsonV2(@RequestBody String messageBody) throws IOException {
    HelloData data = objectMapper.readValue(messageBody, HelloData.class);
    return "ok"; 
}

requestBodyJsonV3 - @RequestBody 객체 변환

@ResponseBody @PostMapping("/request-body-json-v3") 
public String requestBodyJsonV3(@RequestBody HelloData helloData) throws IOException {
    return "ok"; 
}

@RequestBody 객체 파라미터

@RequestBody는 생략 불가능

@ModelAttribute에서 학습한 내용을 떠올려보자.

스프링은 @ModelAttribute , @RequestParam 해당 생략 시 다음과 같은 규칙을 적용한다.

따라서 이 경우 HelloData@RequestBody를 생략하면 @ModelAttribute가 적용되어버린다.

HelloData data -> @ModelAttribute HelloData data

따라서 생략하면 HTTP 메시지 바디가 아니라 요청 파라미터를 처리하게 된다.

requestBodyJsonV5

@ResponseBody 
@PostMapping("/request-body-json-v5")
public HelloData requestBodyJsonV5(@RequestBody HelloData data) {
    return data; 
}

@ResponseBody

응답의 경우에도 @ResponseBody를 사용하면 해당 객체를 HTTP 메시지 바디에 직접 넣어줄 수 있다

HTTP 응답 - 정적 리소스, 뷰 템플릿

스프링(서버)에서 응답 데이터를 만드는 방법은 크게 3가지이다.

정적 리소스

다음 경로에 파일이 들어있으면

src/main/resources/static/basic/hello-form.html

웹 브라우저에서 다음과 같이 실행하면 된다.

http://localhost:8080/basic/hello-form.html

정적 리소스는 해당 파일을 변경 없이 그대로 서비스하는 것이다.

뷰 템플릿

뷰 템플릿을 거쳐서 HTML이 생성되고, 뷰가 응답을 만들어서 전달한다.

일반적으로 HTML을 동적으로 생성하는 용도로 사용하지만, 다른 것들도 가능하다. 뷰 템플릿이 만들 수 있는 것이라면 뭐든지 가능하다.

String을 반환하는 경우 - View or HTTP 메시지

@RequestMapping("/response-view-v2") 
public String responseViewV1(Model model) {  
    model.addAttribute("data", "hello!");  
    return "/response/hello"; 
}

HTTP 응답 - HTTP API, 메시지 바디에 직접 입력

HTTP API를 제공하는 경우에는 HTML이 아니라 데이터를 전달해야 하므로, HTTP 메시지 바디에 JSON 같은 형식으로 데이터를 실어 보낸다

HTTP 메시지 컨버터

뷰 템플릿으로 HTML을 생성해서 응답하는 것이 아니라, HTTP API처럼 JSON 데이터를 HTTP 메시지 바디에서 직접 읽거나 쓰는 경우 HTTP 메시지 컨버터를 사용하면 편리하다 img.png

@ResponseBody 사용 원리

스프링 MVC는 다음의 경우에 HTTP 메시지 컨버터를 적용한다.

스프링 부트는 다양한 메시지 컨버터를 제공하는데, 대상 클래스 타입미디어 타입 둘을 체크해서 사용 여부를 결정한다. 만약 만족하지 않으면 다음 메시지 컨버터로 우선순위가 넘어간다.

몇 가지 주요한 메시지 컨버터

HTTP 요청 데이터 읽기

HTTP 응답 데이터 생성

요청 매핑 헨들러 어뎁터 구조

HTTP 메시지 컨버터는 스프링 MVC 어디쯤에서 사용되는 것일까?

SpringMVC 구조

img_1.png 모든 비밀은 애노테이션 기반의 컨트롤러, @RequestMapping을 처리하는 핸들러 어댑터인 RequestMappingHandlerAdapter(요청 매핑 헨들러 어뎁터)에 있다.

RequestMappingHandlerAdapter 동작 방식

애노테이션 기반의 컨트롤러는 HttpServletRequest, Model은 물론이고, @RequestParam, @ModelAttribute 같은 애노테이션 그리고 @RequestBody, HttpEntity 같은 HTTP 메시지를 처리하는 부분까지 매우 큰 유연함을 보여주었다. img.png 애노테이션 기반 컨트롤러를 처리하는 RequestMappingHandlerAdaptor는 바로 이 ArgumentResolver를 호출해서 컨트롤러(핸들러)가 필요로 하는 다양한 파라미터의 값(객체)을 생성한다.

그리고 이렇게 파리미터의 값이 모두 준비되면 컨트롤러를 호출하면서 값을 넘겨준다.

컨트롤러에서 String으로 뷰 이름을 반환해도, 동작하는 이유가 바로 ReturnValueHandler 덕분이다.

HTTP 메시지 컨버터 위치

img_1.png

HTTP 메시지 컨버터를 사용하는 @RequestBody도 컨트롤러가 필요로 하는 파라미터의 값에 사용된다.

@ResponseBody의 경우도 컨트롤러의 반환 값을 이용한다