스프링 MVC 주요 기능

Lilac-_-P·2023년 4월 16일
0

스프링 MVC

목록 보기
6/15

스프링 MVC가 제공하는 주요기능들에 대해 정리해보자.
개발적인 측면에서 자주 사용할 수 밖에 없는 기능들을 위주로 정리하겠다.

@Controller 와 @RestController

스프링 MVC에서 Controller의 역할을 하는 클래스에 붙이는 annotation이다.
이 annotation이 붙어있는 클래스를 스프링 MVC에서 annotation 기반 컨트롤러로 인식한다.

조금 더 구체적으로 얘기하자면, DispatcherServlet에서는 HTTP 요청의 특정 URI 경로에 맞는 컨트롤러를 찾기 위해 핸들러 매핑을 사용하는데, 여러 핸들러 매핑 중 "RequestMappingHandlerMapping"은 스프링 빈중에서 @Controller이 클래스 레벨에 붙어있는 경우를 매핑 정보로 인식한다.

참고.
RequestMappingHandlerMapping은 @Controller가 붙어있는 클래스들만을 대상으로 @RequestMapping을 추가적으로 탐색하여 annotation의 URI 값을 확인한다. 그렇게 함으로써 @Controller 가 붙은 클래스의 수많은 메서드 중에 어떤 메서드가 호출되어야할지 알 수 있다.

  • 추가. 애플리케이션 로딩 시점에 모든 @Controller가 붙은 클래스의 @RequestMapping를 읽어서 RequestMappingInfo 클래스 객체로 만들어놓고 이를 mappingRegistry에 저장해놓는다. 실제 요청이 들어올 때는 만들어놓은 mappingRegistry에서 요청 경로를 이용하여 알맞는 RequestMappingInfo를 찾고, 이를 통해 어떤 메서드가 호출되어야하는지 결정한다.

@Controller 는 반환 값이 String이면 이 값을 뷰의 이름으로 인식한다. 그래서 반환된 String 값으로 뷰를 찾고(뷰 리졸버가 뷰를 찾는다) 뷰가 렌더링 된다.

그에 반해 @RestController는 반환 값으로 뷰를 찾는 것이 아니라, HTTP 메시지 바디에 바로 입력한다.
@RestController의 내부를 보면 @Controller + @Response 임을 알 수 있는데, 후술할 @Response에서 좀 더 자세한 내용을 설명한다.

@RequestMapping

스프링 MVC의 Controller에서 클래스 레벨이나 메서드 레벨에 붙이는 annotation이다.

이 annotation이 있기 때문에 DispatcherServlet은 특정 HTTP 요청이 들어왔을 때, 요청이 들어온 URI 경로에 따라 선택적으로 메서드를 호출할 수 있다.

@RequestMapping은 method 속성으로 HTTP 메서드를 지정하지 않으면, HTTP 메서드와 무관하게 호출된다. 따라서, method 속성으로 어떤 HTTP 메서드를 이용한 호출인지 명시해주거나, HTTP 메서드를 축약한 annotation인 @GetMapping, @PostMapping, @PutMapping, @PatchMapping, @DeleteMapping 을 사용하면 된다.

다양한 옵션 설정 가능

요청을 매핑하는 annotation에는 설정할 수 있는 다양한 옵션들이 있다. 옵션마다 자세한 설명은 하지 않고, 단순히 나열만 하겠다. 나중에 필요한 경우 찾아볼 것.

  1. 특정 파라미터 조건 매핑 : params = "?"
  2. 특정 헤더 조건 매핑 : headers = "?"
  3. 미디어 타입 조건 매핑(Content-Type) : consumes = "?"
    • 서버 입장에서 클라이언트로부터 특정 데이터 형식을 요구할 때, 이를 지정하는 용도로 사용
  4. 미디어 타입 조건 매핑(Accept) : produces = "?"
    • 클라이언트 입장에서 서버에게 특정 데이터 형식을 요구할 때, 이를 지정하는 용도로 사용

@PathVariable

Controller에서 @RequestMapping의 값으로 들어가는 URL 경로를 템플릿화 하는데 사용할 수 있는 annotation이다.
아래의 코드 예시처럼 사용할 수 있다.

@RequestMapping("example/{ex}")
public String ex1(@PathVariable("ex") String ex){
	return ex;
}

@RequestMapping("example/{ex}")
public String ex2(@PathVariable String ex){
	return ex;
}

@PathVariable("이름")을 메서드의 파라미터 앞에 붙여주면, 해당 파라미터의 값으로 URL 경로의 {이름}에 해당하는 값이 들어간다. 만약 @PathVariable의 이름과 파라미터 이름이 같으면(ex2 메서드) 생략할 수 있다.

참고.
기본적으로 @RequestMapping의 경로값은 String인데, @PathVariable이 붙은 메서드의 파라미터의 자료형을 String이 아닌 다른 자료형(Long, Integer 등등)으로 받을 수 있다. 이는 스프링 내부적으로 컨트롤러에 파라미터를 넘겨줄 때 작동하는 ArgumentResolver 덕분인데, 이에 대한 설명은 후술할 내용을 참고하자.

참고.
컨트롤러의 메서드에서 @PathVariable을 이용하여 매핑된 값은 redirect에서도 사용할 수 있다.
redirect:/basic/items/{itemId} 와 같이 사용하면 {itemId} 는 @PathVariable Long itemId를 그대로 사용한다.

HTTP 요청 헤더 조회 - @RequestHeader

HTTP 요청의 헤더 정보를 조회할 수 있는 annotation으로, Controller의 메서드의 파라미터에 붙여서 사용한다.
아래의 코드 예시처럼 사용할 수 있다.

@RequestMapping
public String ex1(@RequestHeader("ex") String ex){
	return ex;
}

@RequestMapping
public String ex2(@RequestHeader MultiValueMap<String, String> headerMap){
	return ex;
}

ex1 과 같이 특정 헤더 1개를 조회할 때는 @RequestHeader에 찾고자하는 헤더의 이름을 명시하면 된다.
ex2 와 같이 MultiValueMap 형식을 이용해 모든 HTTP 헤더를 조회할 수도 있다.

특정 HTTP 헤더 1개를 조회할 경우, 해당 헤더가 필수값으로 있어야하는지 여부와, 없을 경우의 default 값을 설정할 수 있다. 필수값 여부는 required = true/false 로, default값은 defaultValue ="?"로 명시하면 된다.

참고.
헤더를 조회할 때 MultiValueMap을 사용하는 이유는 한 개의 헤더에 여러 개의 값이 있을 수 있기 때문이다.

쿠키값 조회 - @CookieValue

@RequestHeader를 이용해 HTTP 요청 헤더를 조회하는 것처럼, @CookieValue를 이용하여 쿠키값을 메서드의 파라미터로 받을 수 있다.
아래의 코드 예시처럼 사용하면 된다.

@RequestMapping
public String ex1(@CookieValue(value = "myCookie") String ex){
	return ex;
}

위의 코드와 사용시 파라미터 ex에는 "myCookie" 쿠키의 값이 들어간다.
@CookieValue 도 @RequestHeader 처럼 required와 defaultValue를 사용할 수 있다.

HTTP 요청 데이터

HTTP 요청 메시지를 통해 Client에서 Server 데이터를 전달하는 방법은 크게 3가지가 있다.

GET - 쿼리 파라미터 : 메시지 바디 없이, URL의 쿼리 파라미터에 데이터를 포함시켜 전달
POST - HTML Form : 메시지 바디에 쿼리 파라미터 형식으로 전달(x-www-form-urlencoded)
HTTP Message Body에 데이터를 직접 담기 : JSON과 같은 형태로 데이터를 메시지 바디에 직접 담아서 전달

위의 3가지 방법으로 서버로 데이터가 전달될 때, 스프링은 전달되는 데이터들을 효율적이고 쉽게 받을 수 있는 기능들을 제공한다.

쿼리 파라미터의 형식으로 데이터가 전달되는 경우

@RequestParam

쿼리 파라미터 형식으로 전달되는 데이터를 받을 수 있는 annotation이다. Controller의 메서드의 파라미터에 붙여 특정 파라미터의 값을 받는다.
아래의 코드 예시처럼 사용하면 된다.

@RequestMapping
public String ex1(@RequestParam('ex') String ex){
	return ex;
}

@RequestMapping
public String ex2(@RequestParam String ex){
	return ex;
}

@RequestMapping
public String ex2(@RequestParam MultiValueMap<String, Object> paramMap){
	return ex;
}

@RequestParam("이름")을 메서드의 파라미터 앞에 붙여주면, 해당 파라미터의 값으로 HTTP 요청으로 전달된 쿼리 파라미터 형식의 데이터에서 "이름"을 키로 가지는 값이 들어간다. 만약 @RequestParam의 이름과 쿼리 파라미터의 이름이 같으면(ex2 메서드) 생략할 수 있다. 또한, @RequestHeader처럼 Map 또는 MultiValueMap 형식을 이용해 모든 쿼리 파라미터 데이터를 한번에 조회할 수도 있다.

쿼리 파라미터 형식으로 전달되는 데이터와 Controller의 메서드의 파라미터를 바인딩해주는 것이다.

참고.
@RequestParam가 붙은 메서드의 파라미터의 타입이 String, int 등의 단순 타입이라면, @RequestParam도 생략할 수 있다. 다만, 필자의 경우 @ModelAttribute 와의 혼동 가능성 때문에 명시하는 것을 선호한다.

@RequestParam 에서도 required와 defaultValue 를 사용할 수 있다.

주의할 점이 두가지 있는데,

첫번째는 쿼리 파라미터 형식의 데이터에서 파라미터 이름만 있고 값이 없는 경우, 빈문자로 통과된다는 점이고,
두번째는 @RequestParam의 required를 false로 설정한다면, int와 같은 primitive 타입에 null이 입력된다는 것이다.

위의 두 가지 경우를 방지하기 위해서, defaultValue를 사용하여 기본값을 적용하면 된다.
defaultValue는 두 경우 모두 커버가능하다.

@ModelAttribute

@RequestParam을 이용했을 때는, 쿼리 파라미터 형식의 데이터를 Controller의 메서드의 파라미터로 바인딩할 수 있었지만, 단순 타입(String, int 등)으로의 바인딩만 했었다.

실제 개발시에는 필요한 객체를 만들고 쿼리 파라미터 형식의 데이터를 받아서 객체 내에 값을 넣어주어야하는 경우가 대부분이다. 이 과정을 자동화해주는 것이 @ModelAttribute annotation이다.

아래의 코드 예시처럼 사용한다.

public class ExClass{
	private String ex1;
    private int ex2;
}

@RequestMapping
public String ex1(@ModelAttribute ExClass exClass){
	return ...
}

HTTP 요청에 쿼리 파라미터 형식으로 데이터가 전달되는 경우, 스프링 MVC는 @ModelAttribute가 있으면 다음을 실행한다. 쿼리 파라미터 형식으로 전달되는 데이터를 요청 파라미터라고 하겠다.

  1. @ModelAttribute가 붙은 클래스 객체를 생성한다.
  2. 요청 파라미터의 이름으로 1번에서 만든 객체의 프로퍼티를 찾는다. 그리고 해당 프로퍼티의 setter를 호출해서 파라미터의 값을 바인딩한다. (중요!)

여기에서 2번 과정이 꽤나 중요한데, 요청 파라미터의 이름으로 객체의 프로퍼티를 찾기 때문에, 요청 파라미터의 이름과 객체의 프로퍼티 이름을 일치시켜야한다. 또, setter를 이용하여 요청 파라미터의 값을 객체의 프로퍼티 값으로 바인딩하기 때문에 @ModelAttribute가 붙은 클래스는 필수적으로 setter 함수가 정의되어 있어야한다.

! 2023.04.23 추가 -> @ModelAttribute가 붙은 메서드 파라미터를 만들 때, 먼저 생성자를 통해 객체를 만들어내는데, 이 때 생성자가 아무것도 없는 기본 생성자가 아닌 프로퍼티를 초기화하는 생성자가 있다면, 생성자를 통해 객체를 생성하는 시점에 프로퍼티의 값을 세팅하고 setter를 호출하지 않는다. 따라서, setter가 정의되어 있지 않더라도, 생성자를 통해 요청 파라미터와 메서드 파라미터의 값을 바인딩한다. 생성자를 통해서 초기화되지 않은 프로퍼티가 있을 경우에만 setter를 호출해서 추가 바인딩을 수행한다.

참고.
@ModelAttribute는 생략할 수 있다. 하지만 위에서 @RequestParam도 생략할 수 있다고 했었다.
이런 혼동이 생길 수 있기 때문에 스프링은 생략시에 다음과 같은 규칙을 적용한다.

  • String, int, Integer 같은 단순타입 -> @RequestParam을 생략한 것으로 적용
  • 나머지 -> @ModelAttribute를 생략한 것으로 적용(ArgumentResolver로 지정한 타입 외)

참고.
@ModelAttribute 를 사용하면, @ModelAttribute로 지정한 객체를 Model에 자동으로 넣어준다.
따라서, model.addAttribute() 와 같은 코드를 작성해줄 필요가 없다.

ArgumentResolver라는 녀석이 또 나오는데, ArgumentResolver는 스프링 MVC의 Controller에 선언된 메서드에게 전달되는 파라미터들을 만들어주는 역할을 하는 요소이다. 단지 어떤 annotation이 붙었냐에 따라서 다른 종류의 ArgumentResolver가 작동하는 것이다. ArgumentResolver에 대한 추가 설명은 후술한다.

HTTP Message Body로 직접 데이터가 전달되는 경우

요청 파라미터와 다르게, HTTP 메시지 바디를 통해 데이터가 직접 넘어오는 경우에는 위에 있는 @RequestParam 이나 @ModelAttribute를 사용할 수 없다.

대신, 아래와 같은 방법들을 사용할 수있다.
1. HttpServletRequest에서 InputStream, OutputStream을 통해 바이트 데이터를 읽거나 쓰기
2. HttpEntity 클래스 사용하기 (RequestEntity, ResponseEntity)
3. @RequestBody, @ResponseBody 사용하기

1번의 경우에는 거의 사용할 일이 없기 때문에, 2번과 3번만 설명한다. 위의 3가지 방법은 요청 파라미터(쿼리 파라미터 형식의 데이터가 전달되는 경우)를 조회하는 기능과 전혀 관계가 없으니 헷갈리지 말것. 이 경우에는 Controller의 메서드로 반환되는 값이 String 타입이더라도 view를 조회하지 않고, HTTP 응답 메시지 바디에 반환 값을 전달한다.

HttpEntity (RequestEntity, ResponseEntity)

HttpEntity는 HTTP 요청 또는 응답을 개발자가 편리하게 사용할 수 있게 해주는 클래스이다. 스프링에 의해 제공되는 클래스이기 때문에, 스프링 MVC Controller의 메서드의 파라미터나 반환값으로 사용할 수 있고, 이를 통해 개발자는 HTTP 메시지의 헤더나 바디를 편리하게 조회하거나 작성할 수 있다.

아래의 코드처럼 사용하면 된다. 아래의 코드처럼 HTTP 메시지 바디에 들어가는 내용을 String 같은 단순 타입을 사용할 수 도 있고, 클래스 타입을 사용할 수도 있다.

@RequestMapping
public HttpEntity<String> ex1(HttpEntity<String> httpEntity){
	return HttpEntity<>("OK");
}

@RequestMapping
public ResponseEntity<String> ex1(RequestEntity<String> httpEntity){
	return ResponseEntity<>("OK");
}

===============================================================================
public class ExClass{
	private String ex1;
    private int ex2;
}
@RequestMapping
public HttpEntity<ExClass> ex1(HttpEntity<ExClass> httpEntity){
	return HttpEntity<>(new ExClass());
}

@RequestMapping
public ResponseEntity<ExClass> ex1(RequestEntity<ExClass> httpEntity){
	return ResponseEntity<>(new ExClass());
}

참고.
HttpEntity를 사용하는 경우, @RequestBody나 @ResponseBody를 추가적으로 적어주지 않더라도, 메시지 바디 정보를 직접 조회하고, 메시지 바디 정보를 직접 반환한다. 조회 또는 반환되는 데이터는 단순타입, 클래스 타입을 둘다 사용할 수 있는데, 이를 가능하게 하는 HTTP 메시지 컨버터라는 기능이 스프링 MVC 내부에 존재하는데, HTTP 메시지 컨버터에 대해서는 좀 더 아래에서 설명한다.

@RequestBody, @ResponseBody

HttpEntity와 비슷하게 @RequestBody와 @ResponseBody를 사용하면, 편리하게 HTTP 요청 메시지의 바디에 들어있는 내용을 조회하거나, HTTP 응답 메세지의 바디에 들어갈 내용을 전달할 수 있다.

아래와 코드같이 사용한다.

@ResponseBody
@RequestMapping
public String ex1(@RequestBody String messageBody){
	return messageBody;
}

===============================================================================
public class ExClass{
	private String ex1;
    private int ex2;
}

@ResponseBody
@RequestMapping
public ExClass ex1(@RequestBody ExClass exClass){
	return exClass;
}

참고.
@RequestBody나 @ResponseBody 또한 조회/반환되는 데이터에 단순타입, 클래스 타입을 둘다 사용할 수 있다.
HTTP 요청 헤더가 필요할 경우 HttpEntity나 @RequestHeader + @RequestBody를 사용하면 된다.

주의!
다시한번 강조하지만, 메시지 바디를 직접 조회하는 기능은 요청 파라미터를 조회하는 @RequestParam, @ModelAttribute와 전혀 관계가 없다. 그렇기 때문에 @RequestBody는 생략할 수 없다. 만약 @RequestBody를 생략할 경우, @ModelAttribute가 적용되어 버린다.

JSON 과 HTTP API

보통 HTTP API를 개발할 때, 상호간의 통신을 위해 HTTP 요청, 응답의 메시지 바디에 JSON 같은 형식으로 데이터를 실어서 보낸다. 이 JSON 데이터를 프로그램 코드에서 조작하기 위해서는 개발자가 JSON 형태의 데이터를 파싱하여 객체로 변환해서 사용하거나, 프로그램 코드에서 조작하던 객체를 JSON 형태의 데이터로 변환하여 응답으로 반환해야한다.(생략했지만 바이트 데이터 <-> JSON 형식의 String 데이터 변환도 있다)

JSON -> 객체, 객체 -> JSON 의 과정이 매번 모든 코드에 존재한다면, 이는 반복적이고 귀찮은 작업임이 틀림없다.

스프링 MVC에서는 이 모든 과정을 개발자가 신경쓰지 않고 개발할 수 있도록 HTTP 메시지 컨버터라는 기능을 제공하는데, 이 HTTP 메시지 컨버터는 HTTP 메시지 바디의 내용을 우리가 원하는 문자나 객체 등으로 변환해준다. 또, HTTP 메시지 컨버터는 JSON 데이터도 객체로 변환해준다.(당연히 반대 방향의 변환도 지원한다)

HTTP 메시지 컨버터(Message Converter)

HTTP 메시지 컨버터에 대해서 좀 더 자세히 알아보자.

위의 HttpEntity, @RequestBody, @ResponseBody를 사용할 때, 개발자가 HTTP 메시지 바디에 있는 데이터를 직접 조작한적이 없는데, 이게 어떻게 가능했던 것일까?

스프링 MVC는 아래와 같은 경우에 HTTP 메시지 컨버터를 적용하여 데이터를 변환한다.

  • HTTP 요청 : @RequestBody, HttpEntity(RequestEntity)
  • HTTP 응답 : @ReponseBody, HttpEntity(ResponseEntity)

아래는 HttpMessageConverter 인터페이스 소스 코드이다.

public interface HttpMessageConverter<T> {

	boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);

	boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
	List<MediaType> getSupportedMediaTypes();

	default List<MediaType> getSupportedMediaTypes(Class<?> clazz) {
		return (canRead(clazz, null) || canWrite(clazz, null) ?
				getSupportedMediaTypes() : Collections.emptyList());
	}

	T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
			throws IOException, HttpMessageNotReadableException;
	
    void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException;
            
}

인터페이스의 각 함수의 기능은 다음과 같다

  • canRead(), canWrite() : 메시지 컨버터가 해당 클래스, 미디어타입을 지원하는지 체크하는 함수
  • read(), write() : 메시지 컨버터를 통해서 HTTP 메시지를 읽고 쓰는 기능

즉, HTTP 메시지 컨버터를 이용하면, HTTP 메시지 바디에 있는 데이터를 자동으로 변환하여 읽고 쓸 수 있는 것이다.

스프링은 자체적으로 다양한 메시지 컨버터를 제공하는데, 대상 클래스 타입(1순위)와 미디어 타입(2순위) 둘을 체크해서 어떤 HTTP 메시지 컨버터를 사용할지를 결정한다.

좀 더 구체적으로 얘기해보자.
HTTP 메시지 바디의 데이터가 직접적으로 프로그램 코드와 연결되는 곳은 Controller의 메서드의 파라미터와 반환값 2군데이다. HTTP 요청 메시지 바디의 데이터가 변환되어 메서드의 파라미터로 들어가거나, 메서드에서 반환되는 값이 HTTP 응답 메시지 바디의 데이터로 들어가기전에 변환될 것이다.

그러므로, HTTP 메시지 컨버터는 변환되어야하는 메서드 파라미터나 반환값의 클래스 타입을 1순위로 체크하고, 두번째로는 HTTP 메시지 바디에 들어가는 데이터가 어떤 형식인지를 나타내는 미디어 타입을 2순위로 체크한다.

즉, HTTP 메시지 바디에 들어가는 데이터의 타입과 프로그램 코드에서 사용되는 데이터 타입, 2가지를 고려하는 것이다.

참고.
HTTP 메시지 바디에 들어가는 데이터가 어떤 형식인지를 나타내는 미디어 타입은
HTTP 요청의 경우, Content-Type 헤더로 확인할 수 있고, HTTP 응답의 경우, Accept 헤더로 확인할 수 있다.
스프링 MVC Controller에서는 @RequsetMapping에 consumes과 produces로 이를 지정해줄 수 있다.

그렇다면, HTTP 메시지 컨버터는 스프링 MVC의 어디쯤에서 사용되는 것일까?
이전 글의 Spring MVC 구조의 그림에서는 보이지 않는다.

RequestMappingHandlerAdapter 와 ArgumentResolver

모든 비밀은 annotation 기반의 컨트롤러, 즉, @RequestMapping을 처리하는 핸들러 어댑터인 RequestMappingHandlerAdapter에 있다. 엄밀히 말하면, RequestMappingHandlerAdapter가 내부적으로 사용하는 ArgumentResolver에 있다고 보는 것이 맞다.

생각해보면, annotation 기반의 Controller의 메서드들은 매우 다양한 타입의 파라미터를 사용할 수 있었다.
위에 설명했었던 @RequestParam, @ModelAttribute, @RequestHeader, @RequestBody, @PathVariable 같은 annotation이 붙은 파라미터들 뿐만 아니라 HttpEntity, HttpServletRequest, HttpServlet, Model 등 (추가로 더 많음) 많은 파라미터들을 처리할 수 있었다. 이렇게 다양한 종류들의 파라미터를 유연하게 처리할 수 있는 이유가 바로 ArgumentResolver 덕분이다.

RequestMappingHandlerAdapter는 컨트롤러의 메서드 호출하기 전에, 호출할 컨트롤러의 메서드가 필요한 파라미터 정보와 메서드에 붙어있는 annotation을 먼저 검사한다. 그리고 이렇게 검사한 정보를 기반으로 각 파라미터를 생성할 수 있는 ArgumentResolver를 호출하여 파라미터의 값(객체)을 생성한다.

필요한 파라미터의 값이 모두 준비되면, RequestMappingHandlerAdapter는 컨트롤러의 메서드를 호출하면서 만들어놓은 파라미터 값들을 넘겨준다.

RequestMappingHandlerAdapter와 ArgumentResolver 덕분에 개발자는 컨트롤러의 메서드에서 편리하게 파라미터들을 사용할 수 있었던 것이다. 참 좋은 프레임워크이다.

ArgumentResolver 인터페이스의 소스코드를 한번 봐보자.
정확히는 HandlerMethodArgumentResolver인데 줄여서 ArgumentResolver라 부른다.

public interface HandlerMethodArgumentResolver {

	boolean supportsParameter(MethodParameter parameter);

	@Nullable
	Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
            
}

인터페이스에 정의된 함수가 매우 심플하다. supportParameter()를 통해 이 ArgumentResolver가 해당 파라미터를 지원하는지 체크하고, 지원한다면 resolveArgument()를 호출해서 실제 객체를 생성한다. 이렇게 생성된 객체가 컨트롤러의 메서드 호출시 파라미터의 값으로 넘어가는 것이다.

인터페이스이기 때문에, 원하는 ArgumentResolver를 만들어서 사용할 수도 있다.

참고.
HandlerMethodReturnValueHandler를 줄여서 ReturnValueHandler라고 부른다.
이 ReturnValueHandler는 ArgumentResolver와 비슷한데, 컨트롤러의 메서드 반환값을 변환하는 역할을 한다.

자 갑자기 ArgumentResolver 얘기를 많이해서 머리가 아프겠지만, 거의 다 왔다.

HTTP 메시지 컨버터는 바로 HttpEntity, @RequestBody와 @ResponseBody를 처리하는 ArgumentResolver와 ReturnValueHandler에서 사용되는 것이다.

스프링 MVC는 @RequestBody와 @ResponseBody가 있으면 RequestResponseBodyMethodProcessor를 사용하고(두 종류를 하나로 처리함), HttpEntity가 있으면 HttpEntityMethodProcessor를 사용한다.

스프링은 ArgumentResolver, ReturnValueHandler, HttpMessageConverter를 모두 인터페이스로 제공하기 때문에, 필요하면 언제든지 기능을 확장할 수 있다.

하지만, 스프링이 필요한 대부분의 기능을 제공하기에 실제 기능을 확장할 일이 많지는 않다.
기능 확장은 WebMvcConfigurer를 상속받아서 구현한 클래스를 스프링 빈으로 등록하면 된다.

profile
열심히 하자

0개의 댓글