스프링 MVC 기본 기능
1. 요청 매핑
■@RestController
@Controller는 반환 값이 String이면 뷰 이름으로 인식되고 이를 통해 뷰가 렌더링 된다.
반면 @RestController는 반환 값을 HTTP 메시지 바디에 바로 입력한다.
[@RestController 사용]
@RestController
@Slf4j
public class MappingController {
@GetMapping("/mapping-get-v2")
public String helloBasic() {
log.info("helloBasic");
return "ok";
}
}
반환 값 "ok"는 HTTP 메시지 바디에 바로 입력된다.
■ 메서드 매핑
@RequestMapping에 method 속성이 있을 경우 HTTP 메서드와 맞지 않으면 405 상태 코드가 반환된다.
@RequestMapping에 method 속성을 추가하기보단 HTTP 메서드 이름을 축약한 애노테이션이 주로 사용된다.
ex) @GetMapping, PostMapping, DeleteMapping ...
■ PathVariable(경로 변수)
[경로 변수 사용]
@RestController
@Slf4j
public class MappingController {
@GetMapping("/mapping/{userId}")
public String mappingPath(@PathVariable("userId") String data) {
log.info("mappingPath userId={}", data);
return "ok";
}
}
@RequestMapping은 URL 경로를 템플릿화 할 수 있는데, @PathVariable을 사용하면 매칭 되는 부분을 편리하게 조회할 수 있다.
- @PathVariable은 다중으로 사용할 수 있다.
- @PathVariable의 이름과 파라미터 이름이 같으면 생략할 수 있다.
■ 특정 조건에 따른 매핑
[특정 파라미터 조건 매핑]
@GetMapping(value = "/mapping-param", params = "mode=debug")
public String mappingParam() {
log.info("mappingParam");
return "ok";
}
params : 특정 파라미터가 있거나 없는 조건을 추가할 수 있다.
[특정 HTTP 헤더 조건 매핑]
@GetMapping(value = "/mapping-header", headers = "mode=debug")
public String mappingHeader() {
log.info("mappingHeader");
return "ok";
}
header : 특정 HTTP 헤더가 있거나 없는 조건을 추가할 수 있다.
[특정 미디어 타입 조건 매핑 - Content-Type]
@PostMapping(value = "/mapping-consume", consumes = "application/json")
public String mappingConsumes() {
log.info("mappingConsumes");
return "ok";
}
consumes : 특정 미디어 타입(Content-Type) 조건을 추가한다.
[특정 미디어 타입 조건 매핑 - Accept]
@PostMapping(value = "/mapping-produce", produces = "text/html")
public String mappingProduces() {
log.info("mappingProduces");
return "ok";
}
HTTP 요청의 Accept 헤더를 기반으로 미디어 타입으로 매핑한다.
2. HTTP 요청 - 기본, 헤더 조회
애노테이션 기반의 스프링 컨트롤러는 다양한 파라미터를 지원한다.
[기본, 헤더 조회]
@Slf4j
@RestController
public class RequestHeaderController {
@RequestMapping("/headers")
public String header(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";
}
}
· HttpMethod : HTTP 메서드를 조회한다.
· Local : Local 정보를 조회한다.
· @RequestHeader MultiValueMap<String, String> : 모든 헤더를 MutiValueMap 형식으로 조회한다.
- MultiValueMap : 기본적으로 Map과 같지만, 하나의 키에 여러 값을 받을 수 있다.
· @RequestHeader("host") : 특정 HTTP 헤더를 조회한다.
- required : 필수 값 여부
- defaultValue : 기본 값 속성
· @CookieValue(value = "myCookie", required = false) : 특정 쿠키를 조회한다.
- required : 필수 값 여부
- defaultValue : 기본 값
3. HTTP 요청 파라미터 - 쿼리 파라미터, HTML From
스프링이 제공하는 @RequestParam 을 사용하면 요청 파라미터를 매우 편리하게 사용할 수 있다.
[@RequestParam 사용]
@Slf4j
@Controller
public class RequestParamController {
@ResponseBody
@RequestMapping("/request-param-v2")
public String requestParamV2(
@RequestParam("username") String memberName,
@RequestParam("age") int memberAge) {
log.info("username={}, age={}", memberName, memberAge);
return "ok";
}
}
· @ResponseBody : 뷰 조회를 무시하고, HTTP 메시지 바디에 직접 해당 내용 입력
· @RequestParam : 파라미터 이름으로 바인딩
- 파라미터의 key값으로 value를 조회해, 매개변수로 넘긴다.
[@RequestParam 사용 - key값 생략 가능]
@Slf4j
@Controller
public class RequestParamController {
@ResponseBody
@RequestMapping("/request-param-v3")
public String requestParamV3(
@RequestParam String userName,
@RequestParam int age) {
log.info("username={}, age={}", userName, age);
return "ok";
}
}
key값과 변수 이름이 같으면, key값을 조회하는 부분은 생략하고 @RequestParam만 사용해도 된다.
※ String , int, Interger 등 사용자가 정의한 타입이 아닌 단순 타입이면 @RequestParam도 생략 가능하다.
- 하지만 쿼리 파라미터를 매개변수로 받는지 판단하기 어렵기 때문에 권장되지는 않는다.
- @RequestParam을 생략할 경우 required=false가 적용된다.
■@RequestParam의 속성
· required
- 파라미터 필수 여부
- 기본값이 파라미터 필수(required =true)이다.
- 파라미터가 필수일 때, 파라미터가 없으면 400 예외 발생
· defaultValue
- 파라미터에 값이 없는 경우 기본 값을 적요할 수 있다.
파라미터를 Map, MultiValueMap으로 조회할 수 있다.
[Map으로 파라미터 조회]
@Slf4j
@Controller
public class RequestParamController {
@ResponseBody
@RequestMapping("/request-param-map")
public String requestParamDefault(
@RequestParam Map<String, Object> paramMap) {
log.info("username={}, age={}", paramMap.get("username"), paramMap.get("age"));
return "ok";
}
}
실제 개발을 하면 요청 파라미터를 받아서 필요한 객체를 만들고 그 객체에 값을 넣어 주어야 한다.
기본적으로 객체를 생성하고 필요한 값을 넣어주는 작업이 필요하지만, 스프링을 @ModelAttribute 기능을 제공해서 이 과정을 완전 자동화해준다.
[@ModelAttribute 사용]
@Slf4j
@Controller
public class RequestParamController {
@ResponseBody
@RequestMapping("/model-attribute-v1")
public String modelAttributeV1(@ModelAttribute HelloData helloData){
log.info("hellodata={}", helloData);
return "ok";
}
}
스프링 MVC는 @ModelAttribute가 있으면 다음을 실행한다.
· @ModelAttribute에 해당하는 객체(HelloData)를 생성한다.
· 요청 파라미터의 이름으로 객체의 프로퍼티를 찾는다. 그리고 해당 프로퍼티의 setter를 호출해서 파라미터의 값을 입력(바인딩) 한다.
@ModelAttribute는 생략할 수 있다.
스프링은 다음과 같은 규칙을 적용해서 @RequestParam과 @ModelAttribute를 구분한다.
· String, int, Integer과 같은 유저가 구현하지 않은 단순 타입은 @RequestParam으로 인식한다.
· 그 외의 나머지 경우는 @ModelAttribute를 따른다.
4. HTTP 요청 메시지 - 단순 텍스트
[HttpEntity 사용]
@Slf4j
@Controller
public class RequestBodyStringController {
@PostMapping("/request-body-string-v3")
public HttpEntity<String> requestBodyStringV3(HttpEntity<String> httpEntity) throws IOException {
String messageBody = httpEntity.getBody();
log.info("messageBody={}", messageBody);
return new HttpEntity<>("ok");
}
}
스프링 MVC는 다음 파라미터를 지원한다.
· HttpEntity : HTTP header, body 정보를 편리하게 조회
- 메시지 바디 정보를 직접 조회
- 요청 파라미터를 조회하는 기능과 관계 없음(@RequestParam, @ModelAttribute)
HTTPEntity는 응답에도 사용할 수 있다.
[@RequestBody 사용]
@Slf4j
@Controller
public class RequestBodyStringController {
@ResponseBody
@PostMapping("/request-body-string-v4")
public String requestBodyStringV4(@RequestBody String messageBody) throws IOException {
log.info("messageBody={}", messageBody);
return "ok";
}
}
@RequestBody 를 사용하면 HTTP 메시지 바디 정보를 편리하게 조회할 수 있다.
5. HTTP 요청 메시지 - JSON
[@RequestBody 사용]
@Slf4j
@Controller
public class RequestBodyJsonController {
private ObjectMapper objectMapper = new ObjectMapper();
@ResponseBody
@PostMapping("/request-body-json-v2")
public String requestBodyJsonV2(@RequestBody String messageBody) throws IOException {
log.info("messageBody={}", messageBody);
HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);
log.info("HelloData={}", helloData);
log.info("username={}, age={}", helloData.getUsername(), helloData.getAge());
return "ok";
}
}
@RequestBody를 사용해서 HTTP 메시지에서 데이터를 꺼내고 ObjectMapper를 사용해 메시지 바디의 JSON 데이터를 자바 객체로 변환한다.
[@RequestBody 사용 - ObjectMapper 생략]
@Slf4j
@Controller
public class RequestBodyJsonController {
@ResponseBody
@PostMapping("/request-body-json-v3")
public String requestBodyJsonV3(@RequestBody HelloData helloData) throws IOException {
log.info("HelloData={}", helloData);
log.info("username={}, age={}", helloData.getUsername(), helloData.getAge());
return hellodata;
}
}
@RequestBody에 직접 만든 객체를 지정할 수 있다.
응답의 경우에도 @ResponseBody를 사용하면 해당 객체를 HTTP 메시지 바디에 직접 넣어줄 수 있다.
※@RequestBody는 생략이 불가하다.
HttpEntity, @RequestBody를 사용하면 HTTP 메시지 컨버터가 HTTP 메시지 바디의 내용을 우리가 원하는 문자나 객체 등으로 변환해준다.
· @RequestBody 요청
- JSON 요청 -> HTTP 메시지 컨버터 -> 객체
· @ResponseBody 응답
- 객체 -> HTTP 메시지 컨버터 -> JSON 응답
6. HTTP 응답
응답 데이터를 만드는 방법은 크게 3가지이다.
· 정적 리소스
· 뷰 템플릿 사용
· HTTP 메시지 사용
■정적 리소스
스프링 부트는 클래스패스의 다음 디렉토리에 있는 정적 리소스를 제공한다.
/static, /public, /resources, /META-INF/resources
src/main/resource 는 리소스를 보관하는 곳이고, 클래스패스의 시작 경로이다.
즉, src/main/resource/static/basic/hello-form.html 경로에 파일이 들어있으면 웹 브라우저에서는 다음과 같이 실행하면 된다.
http://localhost:8080/basic/hello-form.html
■뷰 템플릿
뷰 템플릿을 거쳐서 HTML이 생성되고, 뷰가 응답을 만들어서 전달한다.
스프링 부트는 다음과 같은 기본 뷰 템플릿 경로를 제공한다.
src/main/resources/templates
[뷰 템플릿을 호출하는 다양한 방법]
@Controller
public class ResponseViewController {
@RequestMapping("/response-view-v1")
public ModelAndView responseViewV1() {
ModelAndView mav = new ModelAndView("response/hello")
.addObject("data", "hello!");
return mav;
}
@RequestMapping("/response-view-v2")
public String responseViewV2(Model model) {
model.addAttribute("data", "hello!");
return "response/hello";
}
@RequestMapping("/response/hello")
public void responseViewV3(Model model) {
model.addAttribute("data", "hellow!!");
}
}
· ModelAndView 반환
- ModelAndView 객체에 경로 지정
· String 반환
- @ResponseBody가 없으면 response/hello로 뷰 리졸버가 실행되어 뷰를 찾고, 렌더링 한다.
- @ResponseBody가 있으면 뷰 리졸버를 실행하지 않고, HTTP 메시지 바디에 직접 반환된 문자가 입력된다.
· Void 반환
- @Controller를 사용하고, HttpServletResponse, OutputStream(Writer) 같은 HTTP 메시지 바디를 처리하는 파라미터가 없으면 요청 URL을 참고해서 논리 뷰 일름으로 사용
ex) 요청 URL : /response/hello => 실행: template/response/hello.html
※ 이 방식은 명시성이 너무 떨어지고, 조건도 까다롭기 때문에 권장되지 않는다.
■ HTTP 응답 - HTTP API, 메시지 바디에 직접 입력
[다양한 HTTP 응답 방법 - HTTP 메시지 바디에 직접 입력]
@Slf4j
@Controller
//@RestController
public class ResponseBodyController {
@GetMapping("/response-body-string-v1")
public void responseBodyV1(HttpServletResponse response) throws IOException {
response.getWriter().write("ok");
}
@GetMapping("response-body-string-v2")
public ResponseEntity<String> responseBodyV2() {
return new ResponseEntity<>("ok", HttpStatus.OK);
}
@ResponseBody
@GetMapping("response-body-string-v3")
public String responseBodyV3() {
return "ok";
}
@GetMapping("/response-body-json-v4")
public ResponseEntity<HelloData> responseBodyJsonV1() {
HelloData helloData = new HelloData();
helloData.setUsername("userA");
helloData.setAge(20);
return new ResponseEntity<>(helloData, HttpStatus.OK);
}
@ResponseStatus(HttpStatus.OK)
@ResponseBody
@GetMapping("/response-body-json-v5")
public HelloData responseBodyJsonV2() {
HelloData helloData = new HelloData();
helloData.setUsername("userA");
helloData.setAge(20);
return helloData;
}
}
· HttpServeletResponse
- getWriter().write()를 사용해 HTTP 메시지 바디에 직접 메시지 전달
· ResponseEntity
- HttpEntity를 상속받았기 때문에 HTTP 메시지의 헤더, 바디 정보를 갖고 있다.
- HTTP 응답 코드를 설정할 수 있다.
- JSON 데이터도 입력 가능하다.
· @ResponseBody
- 객체를 반환하면 JSON 형식에 맞춰 HTTP 메시지 바디에 입력된다.
- HTTP 응답 코드를 설정할 수 있지만 애노테이션에서 설정하기 때문에 응답 코드를 동적으로 변경할 수는 없다.
· @RestController
- @Controller 대신 @RestController 애노테이션을 사용하면, 해당 컨트롤러 모두 @ResponseBody가 적용되는 효과가 있다.
■ HTTP 메시지 컨버터
HTTP 메시지 바디에 데이터를 직접 요청하거나 입력할 경우 HTTP 메시지 컨버터가 요청/응답 형식에 맞추서 데이터를 변환시켜준다.
스프링 MVC는 다음의 경우에 HTTP 메시지 컨버터를 적용한다.
· HTTP 요청 : @RequestBody, HttpEntity(RequestEntity)
· HTTP 응답 : @ResponseBody, HttpEntity(Response)
HTTP 메시지 컨버터는 다음과 같은 주요 메서드로 구성되어있다.
· canRead(), canWrite() : 메시지 컨버터가 해당 클래스, 미디어타입을 지원하는지 체크
· read(), write(): 메시지 컨버터를 통해서 메시지를 읽고 쓰는 기능
□ 스프링 부트 기본 메시지 컨버터
스프링 부트는 대상 클래스 타입과 미디어 타입에 따라서, 다음과 같은 다양한 메시지 컨버터를 제공한다.
· 0 = ByteArrayHttpMessageConverter
- byte 데이터를 처리한다.
- 클래스 타입 : byte [], 미디어타입 : */*
ex) 요청 : @RequestBody byte[] data
응답 : @ResponseBody return byte[]
쓰기 미디어 타입 : application/octet-stream
· 1= StringHttpMessageConverter
- 문자로 데이터를 처리한다.
- 클래스 타입 : String, 미디어타입 : */*
ex) 요청 : @Request String data
응답 : @ResponseBody return "ok"
쓰기 미디어타입 : text/plain
· 2 = MappingJackson2HttpMessageConverter
- JSON 형식을 처리한다.
- 클래스 타입 : 객체 또는 HashMap, 미디어타입 : application/json 관련
ex) 요청 : @RequestBody HelloData data
응답 : @ResponseBody return data
쓰기 미디어타입: application/json 관련
□ 메시지 컨버터 동작 구조
HTTP 메시지 컨버터는 @RequestMappling을 처리하는 핸들러 어뎁터 부분에서 동작한다.
[RequestMappingHandlerAdapter 동작 - Argument Resolver]
· Argument Resolver
애노테이션 기반의 컨트롤러는 매우 다양한 파라미터를 사용할 수 있었다.
이렇게 파라미터를 유연하게 처리할 수 있는 이유는 ArgumentResolver 덕분이다.
RequestMappinghandlerAdapter는 ArgumentResolver를 호출해서 핸들러(컨트롤러)가 필요로 하는 다양한 파라미터
의 값을 생성한다.
파라미터의 값이 모두 준비되면 핸들러를 호출하면서 값을 넘겨준다.
스프링은 30개가 넘는 ArgumentResolver를 기본으로 제공한다. 원한다면 직접 인터페이스
(HandlerMethodArgumentResolver)를 확장해서 원하는 ArgumentResolver를 만들 수도 있다.
· ReturnValueHandler
ReturnValueHandler는 ArgumentResolver와 비슷한데, 이것은 응답 값을 변환하고 처리한다.
스프링은 10개가 넘는 ReturnValueHandler를 지원한다.
HTTP 메시지 컨버터는 파라미터와 리턴 값을 처리하는 ArgumentResolver와 ReturnValueHandler에서 사용된다.
각각 요청과 응답에 따라 필요한 ArgumentResolver와 ReturnValueHandler를 호출하고 HTTP 메시지 컨버터를 호출해
서 요청/응답 결과를 변환한다.
'Spring > 스프링 MVC 기본' 카테고리의 다른 글
#7 스프링 MVC - 웹 페이지 만들기 (0) | 2021.07.08 |
---|---|
#5 스프링 MVC 구조 이해 (0) | 2021.06.30 |
#4 MVC 프레임워크 (0) | 2021.06.29 |
#3 JSP & MVC 패턴 적용 (0) | 2021.06.28 |
#2 서블릿 (0) | 2021.06.23 |
댓글