[Spring MVC 1편] 강의 내용 정리

HJ·2023년 1월 11일
0

Spring MVC 1편

목록 보기
8/8

김영한 님의 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 강의를 보고 작성한 내용입니다.
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1/dashboard


1. 기본 용어

  • 웹 서버 : 정적 리소스 제공

  • WAS : 프로그램 코드를 실행해서 어플리케이션 로직 수행

    • 사용자마다 서로 다른 페이지를 보여줄 수 있음

    • 동적 HTML, HTTP API( JSON ) 제공 가능

    • 서블릿, JSP, 스프링 MVC 등이 WAS에서 동작

  • JSP : 서버로부터 데이터를 받아 동적으로 화면이 그려지는 템플릿 엔진 ( SSR 기술 )

  • HTTP API : HTML이 아닌 데이터를 전달 ( 주로 JSON 형식 )




2. Servlet

2-1. 서블릿 동작 흐름

  1. WAS의 Servlet Container가 servlet 객체를 생성

    • WAS = Web Server + Servlet Container
  2. 클라이언트가 해당 servlet을 사용하는 http 요청을 하면, Servlet Container에서 request, response 객체 생성

  3. 이때, Thread가 Servlet 객체를 호출하고 request,response 객체를 Servlet 객체에게 넘겨준다

  4. request 객체를 활용해 Servlet의 비즈니스 로직 실행

  5. 응답 결과를 response 객체에 담은 후, Servlet Container에 전달

  6. Servlet Container가 http 응답 메시지 생성 후 클라이언트에게 전달


2-2. 서블릿과 WAS

  • 서블릿 : 동적 웹 페이지를 만들 때 사용되는 자바 기반의 웹 애플리케이션 프로그래밍 기술

    • 서블릿은 웹 요청과 응답의 흐름을 간단한 메서드 호출만으로 체계적으로 다룰 수 있게 해준다
  • WAS = Web Server + Servlet Container

    • WAS는 HTTP 요청 메세지를 기반으로 Request, Response 객체를 만들고 service() 를 호출하면서 객체들을 파라미터로 넘겨준다

    • request, response 객체는 요청이 올 때마다 새로 생성된다

  • Servlet Container

    • HTTP 요청 메세지를 파싱하고 HttpServltRequest 객체 생성 ( 헤더, message body에 관한 정보를 가짐 )

    • HttpServletResponse 객체로 HTTP 응답 메세지를 생성해 반환

    • 서블릿 생성 & 싱글톤으로 관리

    • 서블릿을 호출


2-3. 서블릿 라이프사이클

  1. init() : 서블릿 클래스 로드 ➜ 인스턴스 생성 ➜ init() 메서드를 호출해서 초기화

  2. service()

    • init() 메서드가 성공적으로 완료된 후에만 호출된다

    • 서블릿 컨테이너는 클라이언트에서 오는 요청을 처리하기 위해 service() 메서드를 호출하고 HTTP 요청 유형( GET , POST , PUT , DELETE )을 해석하고 doGet , doPost , doPut , doDelete 등의 메서드를 적절하게 호출한다

  3. destroy() : 서블릿 컨테이너에 의해 호출


2-4. 서블릿 관련 어노테이션

  • @ServletComponentScan

    • Servlet을 자동 등록하기 위해 main 메서드가 있는 클래스가 붙어야 하는 어노테이션

    • @WebFilter , @WebListener@WebServlet 에 대한 스캔을 활성화하기 위해 추가하는 어노테이션

  • @WebServlet() : 클래스 레벨에 붙여서 url과 Servlet을 매핑한다




3. HttpServletRequest, HttpServletResponse

3-1. HttpServletRequest

  • 서블릿은 HTTP 요청 메세지를 파싱하고 그 결과를 HttpServletRequest 객체에 담아서 제공한다

  • request-line 조회 / header 조회 / 요청 데이터 조회 / 임시 저장소 기능 / 세션 기능이 존재

  • 요청 데이터 조회는 쿼리 파라미터 조회 / HTML Form 데이터 조회 / message body 조회로 나눌 수 있다

  • 쿼리 파라미터, HTML Form 데이터 조회

    • getParameter("name"), getParameters() 를 사용한다

    • message body에 데이터가 넘어오는 경우 중 HTML Form 방식은 message body의 내용이 쿼리 파라미터 형식과 동일하므로 쿼리 파라미터 조회하는 메서드를 이용

  • message body 조회는 텍스트 형식인 경우, JSON 형식인 경우로 나뉜다

    • 텍스트 형식의 경우 : getInputStream() + StreamUtils.copyToString(inputStream,StandardCharsets.UTF_8)

    • JSON 형식의 경우 : getInputStream() + StreamUtils.copyToString(inputStream,StandardCharsets.UTF_8) + objectMapper.readValue()


3-2. HttpServletResponse

  • HTTP 응답 메세지를 작성할 때 사용

  • 상태 코드 작성 / 헤더 작성 / message body에 내용 작성을 할 수 있다

    • 헤더의 경우 쿠키 / 리다이렉트 등도 지정 가능하다

    • 헤더 작성은 setHeader()도 가능하지만 setXXX()도 가능하다 ( XXX는 헤더 이름 )

    • message body에 내용 작성을 위해 PrintWriter writer = response.getWriter(); 코드가 우선적으로 필요

  • message body에 텍스트 / html / JSON 데이터를 담을 수 있다

    • 텍스트를 담을 때 writer.println("text");

    • html을 담을 때 writer.println("html태그");를 이용하고 Content-type을 text/html로 지정하고 인코딩 방식도 지정해야함 ( utf-8 )

    • JSON 형식의 경우 objectMapper.writeValueAsString()를 통해 객체를 String으로 변환시키고, response.getWriter().write()를 통해 message body에 작성하는데 Content-Type을 application/json으로 지정하고 인코딩 방식은 따로 지정하지 않는다




4. MVC 프레임워크 만들기

@WebServlet(name = "mvcMemberSaveServlet", urlPatterns = "/servlet-mvc/members/save")
public class MvcMemberSaveServlet extends HttpServlet {

    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        
        // 요청 메세지에서 데이터 조회
        String username = request.getParameter("username");
        int age = Integer.parseInt(request.getParameter("age"));

        // 저장할 객체 ( 모델에 담을 객체 ) 생성 및 저장
        Member member = new Member(username, age);
        System.out.println("member = " + member);
        memberRepository.save(member);

        // Model에 데이터를 보관
        request.setAttribute("member", member);

        // 렌더링
        String viewPath = "/WEB-INF/views/save-result.jsp";
        RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
        dispatcher.forward(request, response);
    }
}
  • 서블릿은 호출되면 service() 메서드가 실행되기 때문에 하나의 서블릿은 하나의 url과 매핑되고, 하나의 기능만을 수행한다

  • 위의 코드를 보면 /servlet-mvc/members/save 로 요청이 들어오면 위의 서블릿 객체가 실행된다

  • service() 에 요청 메세지에서 데이터를 조회, 모델에 데이터를 저장, view 의 이름으로 랜더링을 실행하는 모든 코드가 있다

  • 이러한 기능들을 분리하기 위해 MVC 패턴, 코드의 중복 제거를 위해 Front Controller 패턴, 여러 Contoller 지원을 위해 어댑터 패턴을 사용한다

  • 나중에 나오는 내용이지만 스프링에서는 DispatcherServlet 을 통해 모든 요청을 받아들여 특정 경로와 매핑된 메서드를 실행하게 된다


V1 : FrontController 도입

  1. FrontController

    • 매핑 정보를 보고 요청 url에 맞는 Controller 추출

    • Controller 호출 ( HttpServletRequest, HttpServletResponse 도 함께 전달 )

  1. Controller

    • HttpServletRequest 에서 데이터를 추출해 객체 생성

    • 생성한 객체를 Model 에 저장

    • HttpServletRequest의 임시저장소를 Model로 활용

    • JSP 의 물리 이름( 파일 경로 + 이름 ) 을 가지고 JSP로 forward(request, response)

  • 참고 : FrontController

    • 서블릿 하나로 클라이언트의 요청을 받는다

    • 요청에 맞는 컨트롤러를 찾아서 호출한다


V2 : View 분리

  1. FrontController

    • 매핑 정보를 보고 요청 url에 맞는 Controller 추출

    • Controller 호출 ( HttpServletRequest, HttpServletResponse 도 함께 전달 )

  1. Controller

    • HttpServletRequest 에서 데이터를 추출해 객체 생성 ( member )

    • 생성한 객체( member )를 Model 에 저장

    • HttpServletRequest의 임시저장소를 Model로 활용

    • JSP 의 물리 이름( 파일 경로 + 이름 ) 을 가지고 MyView 객체를 생성해서 반환

  1. FrontController

    • 반환된 MyView 객체의 render()를 호출 ( HttpServletRequest, HttpServletResponse 도 함께 전달 )
  1. MyView

    • render() 메서드에서 forward(request, response) 수행 ( 실제 페이지를 호출 )

V3 : Model 추가

  • ModelView는 view의 논리 이름 + model 을 담기위한 Map 객체를 가짐 ( model )
  1. FrontController

    • 매핑 정보를 보고 요청 url에 맞는 Controller 추출

    • HttpServletRequest 에서 데이터를 추출해 paramMap 객체 생성

    • Controller 호출 ( paramMap을 전달 )

  1. Controller

    • 전달받은 paramMap 에 있는 정보를 꺼내 객체를 생성 ( member )

    • JSP 파일의 논리 이름( 이름만 )을 가지고 ModelView 객체를 생성

    • 생성한 ModelView 객체의 model에 생성한 객체 ( member )를 넣는다

    • ModelView 객체 반환

  1. FrontController

    • 반환된 ModelView 객체의 논리 이름을 viewResolver() 를 통해 경로 + 이름( 물리 이름 )으로 변환하고 물리이름으로 MyView 객체생성

    • 즉, viewResolver() 는 view 의 논리 이름을 받아 물리 이름으로 변환하고 물리 이름으로 MyView 객체를 반환한다

    • MyView 객체의 render() 호출 ( ModelView의 model, HttpServletRequest, HttpServletResponse 도 함께 전달 )

  1. MyView

    • render() 메서드에서 model의 정보를 HttpServlerRequest의 임시저장소에 저장

    • forward(request, response) 수행


V4 : ModelView 사용 X

  1. FrontController

    • 매핑 정보를 보고 요청 url에 맞는 Controller 추출

    • HttpServletRequest 에서 데이터를 추출해 paramMap 객체 생성

    • Controller가 model 정보를 담을 수 있도록 Map 객체를 생성 ( model )

    • Controller 호출 ( paramMap과 model을 전달 )

  1. Controller

    • 전달받은 paramMap을 활용해 객체 생성 ( member )

    • 전달받은 model을 담기위한 Map ( model )에 생성한 객체( member ) 추가

    • view의 논리 이름을 반환

  1. FrontController

    • 반환된 view의 논리 이름을 viewResolver() 를 통해 경로 + 이름( 물리 이름 )으로 변환

    • 물리이름으로 MyView 객체생성

    • MyView 객체의 render() 호출 ( model, HttpServletRequest, HttpServletResponse 객체도 함께 전달 )

  1. MyView

    • render() 메서드에서 model의 정보를 HttpServlerRequest의 임시저장소에 저장0

    • forward(request, response) 수행


V5 : 어댑터 패턴

  • 여러 버전의 Controller 호출할 수 있도록 변경 ➜ 어댑터 패턴

  • 핸들러 매핑 : url에 따른 여러 버전의 컨트롤러 정보가 있음

  • 핸들러 어댑터 매핑 : 핸들러 어댑터 정보가 있음

  1. FrontController

    • 핸들러 매핑 정보를 보고 요청 url에 맞는 핸들러 추출 ( getHandler() )

    • 핸들러 어댑터 매핑 정보를 보고 주어진 핸들러를 처리할 수 있는지 확인 ( 핸들러 어댑터의 supprot() 활용 )

    • 처리 가능한 핸들러 어댑터 반환 ( getHandlerAdapter() )

    • handle() 로 반환된 핸들러 어댑터 호출 ( HttpServletRequest, HttpServletResponse, 추출한 핸들러도 함께 전달 )

  1. 핸들러 어댑터

    • 전달받은 핸들러를 본인이 처리 가능한 핸들러로 캐스팅

    • HttpServletRequest 에서 데이터를 추출해 paramMap 객체 생성

    • 핸들러 호출 ( paramMap을 함께 전달 )

  1. 핸들러

    • 전달받은 paramMap을 활용해 객체 생성 ( member )

    • view의 논리 이름 or ModelView 객체를 생성 및 model 에 데이터를 넣고 반환

  1. 핸들러 어댑터

    • 핸들러가 반환한 것이 무엇이든 핸들러 어댑터의 인터페이스의 handle() 메서드에 맞는 반환형을 맞춰서 반환
  1. FrontController

    • 반환된 ModelView 객체의 논리 이름을 viewResolver()를 통해 물리 이름으로 변환

    • 물리 이름으로 MyView 객체생성

    • MyView 객체의 render() 호출 ( ModelView의 model, HttpServletRequest, HttpServletResponse 도 함께 전달 )

  2. MyView

    • render() 메서드에서 model의 정보를 HttpServlerRequest의 임시저장소에 저장

    • forward(request, response) 수행


최종 기능 정리

  • FrontController

    • 핸들러 매핑을 보고 핸들러 추출

    • 핸들러 어댑터 목록을 보고 핸들러 어댑터 찾기

    • 핸들러 어댑터 호출

  • 핸들러 어댑터 : 핸들러 호출

  • 핸들러

    • 로직 수행

    • view 논리 이름 or ModelView 반환

  • ModelView : view의 논리 이름과 model 를 담을 Map 객체를 가짐 ( model )

  • 핸들러 어댑터 : 핸들러가 반환한 것을 핸들러 어댑터의 인터페이스의 handle() 메서드에 맞는 반환형을 맞춰서 반환

  • FrontController

    • viewResolver() 호출

    • MyView 객체 생성

    • MyView의 render() 호출

  • viewResolver()

    • view의 논리 이름 ➜ 물리 이름

    • 물리 이름으로 MyView 객체를 생성해서 반환

  • MyView

    • view 의 물리 이름을 가짐

    • render() 메서드로 렌더링 수행




5. 스프링 MVC

5-1. 기본 구조

  • FrontController ➔ DispatcherServlet

  • handlerMappingMap ➔ HandlerMapping ( 인터페이스 )

  • MyHandlerAdapter ➔ HandlerAdapter ( 인터페이스 )

  • ModelView ➔ ModelAndView

  • viewResolver ( 메서드 ) ➔ viewResolver ( 인터페이스 )

  • MyView ( 클래스 ) ➔ View ( 인터페이스 )


5-2. 동작 순서

  1. 핸들러 조회 : 요청 URL에 매핑된 핸들러( Controller )를 조회

  2. 핸들러 어댑터 조회 : 핸들러를 실행할 수 있는 핸들러 어댑터 조회

  3. 핸들러 어댑터 실행

  4. 핸들러 실행 : 핸들러 어댑터가 실제 핸들러를 실행

  5. ModelAndView 반환 : 핸들러 어댑터는 핸들러가 반환하는 정보를 ModelAndView로 변환해서 반환

    • ModelAndView 는 view 의 이름과 ModelMap 이라는 이름의 LinkedHashMap 를 가진다
  6. viewResolver 호출

    • 템플릿 엔진마다 다른 viewResolver가 호출
    • viewResolver는 view의 논리 이름을 물리 이름으로 바꾸고 렌더링 역할을 하는 View 객체를 반환
  7. 뷰 렌더링 : view 를 통해 뷰를 렌더링 ( render() )


5-3. 핸들러 호출 방법

  • 핸들러가 호출되기 위해 HandlerMapping, HandlerAdapter가 필요

  • HandlerMapping( 핸들러 매핑 )

    • 핸들러 매핑에서 해당 컨트롤러를 찾을 수 있어야한다

    • ex> 스프링 빈의 이름으로 핸들러를 찾을 수 있는 핸들러 매핑이 필요하다

  • HandlerAdapter( 핸들러 어댑터)

    • 핸들러 매핑을 통해 찾은 핸들러를 실행할 수 있는 핸들러 어댑터가 필요

    • ex> Controller 인터페이스를 실행할 수 있는 핸들러 어댑터를 찾고 실행해야한다


5-4. 핸들러 매핑

  • 0 순위 : RequestMappingHandlerMapping

    • @RequestMapping을 사용하는 어노테이션 기반의 컨트롤러에서 사용

    • 가장 먼저 실행되는 객체

  • 1순위 : BeanNameUrlHandlerMapping

    • 스프링 빈의 이름으로 핸들러를 찾는 객체

    • URL 이름과 동일한 스프링 빈을 찾는다


5-5. 핸들러 어댑터

  • 0 순위 : RequestMappingHandlerAdapter

    • @RequestMapping을 사용하는 어노테이션 기반의 컨트롤러에서 사용
  • 1순위 : HttpRequestHandlerAdapter

    • HttpRequestHandler를 처리하는 어댑터 ( 객체 )
  • 2순위 : SimpleControllerHandlerAdapter

    • Controller 인터페이스 ( 어노테이션 X ) 처리

public class SimpleControllerHandlerAdapter implements HandlerAdapter {

	@Override
	public boolean supports(Object handler) {
		return (handler instanceof Controller);
	}

    @Override
	@Nullable
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		return ((Controller) handler).handleRequest(request, response);
	}
}
  • 위에서 핸들러 매핑을 통해 찾은 핸들러를 실행할 수 있는 핸들러 어댑터가 필요하다고 했다

  • 이를 판단하는 기준이 위의 supports() 메서드인데 instanceof 로 인해 Controller 인터페이스를 호출할 수 있는지 여부를 확인할 수 있다

  • dispatcherServlet 이 핸들러 어댑터의 handle() 메서드를 실행한다


5-6. ViewResolver

  • DispatcherServlet은 뷰 리졸버 목록을 가지고 있고, 핸들러 어댑터를 통해 반환된 논리적인 뷰 이름을 가지고, viewResolver 목록을 순회하며 view를 생성을 시도

  • 1순위 : BeanNameViewResolver

    • 빈 이름으로 뷰를 찾아서 반환
  • 2순위 : InternalResourceViewResolver

    • JSP를 처리할 수 있는 뷰를 반환

    • JSP 처럼 forward()를 호출해야 하는 경우에 사용

    • 스프링이 viewResolver를 등록할 때 application.properties에 등록한 정보를 사용해서 등록한다


  • 다른 템플릿 엔진의 경우, dependencies를 입력하면 스프링부트가 자동으로 viewResolver를 등록해준다

  • viewResolver가 상황에 맞게 위의 객체 중 하나를 선택하고 선택된 것이 View를 만들어서 반환한다

    • DispatcherServletr이 viewResolver 목록을 가지고 있고, 논리적인 view 이름을 가지고 이 목록을 순회하며 view 생성을 시도
  • JSP의 경우, forward() 실행 후 렌더링이 진행되고 다른 템플릿 엔진의 경우 바로 렌더링이 진행된다




6. 요청 매핑

6-1. @RequestMapping

  • 어노테이션에 URL 을 매핑할 수 있으며, method 를 지정할 수 있다

  • method 를 지정하지 않고 @GetMapping@PostMapping 을 사용할 수 있다

  • method 속성으로 HTTP 메서드를 지정하지 않으면 HTTP 메서드와 무관하게 무조건 호출된다


6-2. @Pathvariable

  • URL 자체에 값이 들어가 있는 경우, 파라미터에서 @PathVariable 을 사용해 꺼낼 수 있음

    • ex> @GetMapping("/mapping/{userId}") 이면 @PathVariable("userId") String userId
  • 경로 변수의 이름 ( URL을 통해 들어오는 값의 이름 )과 메서드에서 사용되는 파라미터 이름이 같으면 생략할 수 있다

    • ex> @PathVariable("userId") String userId@PathVariable String userId



7. HTTP 요청 메세지

7-1. 요청 메세지로 데이터 전달 방법 3가지

  1. 쿼리 파라미터

    • message body 없이, URL의 쿼리 파라미터에 데이터를 포함해서 전달

    • 쿼리 파라미터는 ? 로 시작하고 & 로 구분

  2. HTML Form

    • message body에 쿼리 파리미터 형식으로 전달
  3. HTTP Message Body에 데이터를 직접 담아서 요청

    • JSON 혹은 텍스트 형식의 데이터 전송

7-2. 쿼리 파라미터, HTML Form을 통해 전달된 데이터 조회

  1. HttpServletRequest : getParameters(), getParameter(), getParameterValues()

  2. @RequestParam("파라미터이름") 반환형 변수명

    • 파라미터 이름과 변수명이 동일하다면 파라미터 이름을 생략 가능

    • @RequestParam 반환형 변수명

  3. @ModelAttribute 클래스이름 객체이름 : 요청 파라미터로 받은 값을 이용해 객체를 만드는 경우에 사용

    • @RequestParam으로 받아서 직접 객체를 생성할 수 있지만 @ModelAttribute를 사용하면 이 과정을 스프링이 자동으로 처리

    • 단> 객체 클래스에 @Data 어노테이션이 붙어 있어야 가능


7-3. Message Body를 통해 전달된 데이터 조회

7-3-1. 텍스트 형식인 경우

  1. HttpServletRequest : getInputStream() + StreamUtils.copyToString()

  2. InputStream : StreamUtils.copyToString()

  3. HttpEntity : getBody()

  4. @RequestBody


7-3-2. JSON 형식인 경우

  1. HttpServletRequest : getInputStream() + StreamUtils.copyToString() + objectMapper.readValue()

  2. HttpEntity : getBody()

  3. @RequestBody


7-3-3. @RequestBody

  • @RequestBody 로 데이터를 문자로 읽어온다 ➜ 객체로 변환

  • @RequestBody 에 객체를 지정하면 HTTP 메세지 컨버터가 message body의 내용을 객체로 변환시켜준다


7-4. @ModelAttribute vs @RequestBody

  • @ModelAttribute요청 파라미터 정보로 객체를 생성

  • @RequestBodymessage body 의 정보로 객체를 생성




8. HTTP 응답 메세지

  1. 정적 리소스

  2. view template

    • HttpServletResponse : response.getWriter().println("html태그")

    • ModelAndView 반환 : view 논리 이름으로 객체 생성 + addObject()로 모델 정보 삽입

    • 파라미터로 Model 사용, view의 논리 이름( String ) 반환

  3. HTTP Message Body에 데이터를 직접 담아서 보내기

    • 텍스트 형식 전달

      • HttpServletResponse : response.getWriter().println("text")

      • ResponseEntity<String>

      • @ResponseBody : 응답 결과를 message body에 직접 담아서 전달

    • JSON 형식 전달

      • HttpServletResponse : objectMapper.writeValueAsString() 이용

      • ResponseEntity<클래스이름>

      • @ResponseBody, @ResponseStatus




9. HttpMessageConverter

  • @RequestBody, @ResponseBody 를 사용하는 경우에 viewResolver 대신 HttpMessageConverter 가 동작한다

    • 기본 문자 처리 : StringHttpMessageConverter

    • 기본 객체 처리 : MappingJackson2HttpMessageConverter

  • HttpMessageConverter 의 종류

    • 0순위 : ByteArrayHttpMessageConverter

    • 1순위 : StringHttpMessageConverter

    • 2순위 : MappingJackson2HttpMessageConverter

  • 아래 메서드를 통해 해당 메세지 컨버터를 사용 가능한지 확인할 수 있다

    • canRead() , canWrite() : 메시지 컨버터가 대상 클래스 타입과, 미디어타입을 지원하는지 체크



10. ArgumentResolver, ReturnValueHandler

10-1. ArgumentResolver

@Controller
public class RequestBodyStringController {

    @PostMapping("/request-body-string-v1")
    public void requestBodyStringV1(HttpServletRequest request, HttpServletResponse response) throws IOException { ... }

    @PostMapping("/request-body-string-v2")
    public void requestBodyStringV2(InputStream inputStream, Writer responseWriter) throws IOException { ... }

    @PostMapping("/request-body-string-v3")
    public HttpEntity<String> requestBodyStringV3(HttpEntity<String> httpEntity) throws IOException { ... }

    @ResponseBody
    @PostMapping("/request-body-string-v4")
    public String requestBodyStringV4(@RequestBody String messageBody) throws IOException { ... }
}
  • 어노테이션 기반 컨트롤러에서 파라미터로 HttpServletRequest, Model, @RequestParam, @RequestBody, HttpEntity 등을 사용했는데 이것을 사용할 수 있으려면 누군가가 데이터를 해당 파라미터에 맞게 전달해주어야 한다

  • 이런 것을 처리해주는 것이 바로 ArgumentResolver 이다

  • 어노테이션 기반 컨트롤러를 처리하는 RequestMappingHandlerAdapter 는 바로 이 ArgumentResolver 를 호출해서 컨트롤러가 필요로 하는 다양한 파라미터의 값(객체)을 생성하고 모든 파라미터의 값이 준비되면 컨트롤러를 호출하면서 값을 넘겨준다


10-2. ArgumentResolver 동작

public interface HandlerMethodArgumentResolver {

	boolean supportsParameter(MethodParameter parameter);

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

}
  • ArgumentResolver는 핸들러가 필요로 하는 객체를 생성하는 역할을 수행

  • RequestMappingHandlerAdapter가 호출함으로써 실행되고, 생성한 객체를 RequestMappingHandlerAdapter에게 넘겨준다

  • supportsParameter() : 핸들러( Controller )가 받아야 하는 파라미터 정보를 지원하는지 판단

  • 지원하는 경우, resolveArgument()를 통해 객체를 만들어서 반환한다


10-3. ReturnValueHandler

public interface HandlerMethodReturnValueHandler {

	boolean supportsReturnType(MethodParameter returnType);

	void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;

}
  • ReturnValueHandler : 핸들러가 값을 반환할 때 응답( 반환 ) 값을 변환하고 처리해준다

  • 핸들러에서 String으로 view 이름을 반환해도 동작하는 이유가 ReturnValueHandler 덕분이다


10-4. HttpMessageConverter

  • @RequestBody@ResponseBody 를 컨트롤러에서 사용하는데 이들은 모두 HttpMessageConverter 를 사용한다

  • 즉, @RequestBody, @ResponseBody, HttpEntity 를 사용하는 경우 ArgumentResolverReturnValueHandler 가 메세지 컨버터를 사용한다

  • 요청 시

    • @RequestBody, HttpEntity 등을 처리하는 서로 다른 ArgumentResolver 가 있다 ( 여러 개 존재 )

    • ArgumentResolver 들이 HTTP 메시지 컨버터를 사용해서 필요한 객체를 생성한다

  • 응답 시

    • @ResponseBody, HttpEntity 등을 처리하는 서로 다른 ReturnValueHandler 가 있다 ( 여러 개 존재 )

    • ReturnValueHandler 들이 HTTP 메시지 컨버터를 호출해서 응답 결과를 만든다

profile
공부한 내용을 정리해서 기록하고 다시 보기 위한 공간

0개의 댓글