[Spring] SpringMVC 의 세부적인 구조 (핸들러, 핸들러 어댑터, 메시지 컨버터)

728x90

 

SpringMVC 전체 구조

DispatcherServlet

- SpringMVC 의 프런트 컨트롤러

- HttpServlet 을 상속받아 사용하고, 서블릿으로 동작

- 스프링 부트가 DispatcherServlet 을 서블릿으로 자동 등록하며 모든 경로 (urlParrerns) 에 대하여 매핑

 

SpringMVC 구조

    핸들러 매핑        
   
① 핸들러 조회
      핸들러 어댑터 목록
         ↗ 
  ② 핸들러 어댑터 조회
 ↗ 
   
클라이언트 HTTP 요청
→ → → →
Dispatcher
Servlet
③ handle(handler)
→ → → → → →
← ← ← ← ← ←
⑤ModelAndView 반환
핸들러
어댑터
④ 핸들러 호출
→ → → →
핸들러
(컨트롤러)
 
⑧ render(model)
호출
 ↖ ↘ ⑥ ViewResolver 호출
     ↖ ↘
         ↖ ⑦ View 반환
            ViewResolver
   
HTML 응답
← ← ← ←
View        

 

1. 핸들러 조회 : 핸들러 매핑을 통해 요청 URL 에 매핑된 핸들러를 조회

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

3. 핸들러 어댑터 실행 : 핸들러 어댑터를 실행

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

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

6. ViewResolver 호출 : 뷰 리졸버를 찾고 실행

( JSP 의 경우, InternalResourceViewResolver 자동 등록 )

7. View 반환 : 뷰 리졸버가 뷰 논리 이름을 물리 이름으로 바꾸고, 렌더링 역할을 담당하는 View 객체 반환

( JSP 의 경우, InternalResourceView(JstlView) 반환, 내부에 forward()` 로직 )

8. 뷰 렌더링 : 뷰를 통해 뷰를 렌더링

 

핸들러 매핑 / 핸들러 어댑터

HandlerMapping

0 = RequestMappingHandlerMapping

  : 애노테이션 기반의 컨트롤러인 @RequestMapping 에서 사용

1 = BeanNameUrlHandlerMapping 

  : 스프링 빈 이름으로 핸들러 찾음

( 생략 )

 

HandlerAdapter

0 = RequestMappingHandlerAdapter

  : 애노테이션 기반의 컨트롤러인 @RequestMapping 에서 사용

1 = HttpRequestHandlerAdapter

  : HttpRequestHandler 처리

2 = SimpleControllerHandlerAdapter

  : Controller 인터페이스 (애노테이션 X, 과거에 사용) 처리

( 생략 )

 

HandlerAdapter 의 supports() 를 순서대로 호출하여 핸들러 어댑터를 조회한다.

 

Controller 인터페이스 (과거에 사용)

@Component("/springmvc/old-controller")
public class OldController implements Controller {

  @Override
  public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
    return null;
  }
}

/springmvc/old-controller 이름으로 스프링 빈이 등록되었다.

빈 이름으로 URL 을 매핑한다.

 

1. HandlerMapping 을 순서대로 실행하여 핸들러를 찾는다.

2. 빈 이름으로 핸들러를 찾는 BeanNameUrlHandlerMapping 이 실행에 성공하고 핸들러를 반환한다.

3. HandlerAdapter 의 supports() 를 호출하여 핸들러 어댑터를 찾는다.

4. 찾은 SimpleControllerHandlerAdapter 로 핸들러를 실행하고, 결과를 반환한다.

 

HttpRequestHandler 인터페이스 (과거에 사용)

@Component("/springmvc/request-handler")
public class MyHttpRequestHandler implements HttpRequestHandler {
  
  @Override
  public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  }
}

/springmvc/request-handler 이름으로 스프링 빈이 등록되었다.

빈 이름으로 URL 을 매핑한다.

 

1. HandlerMapping 을 순서대로 실행하여 핸들러를 찾는다.

2. 빈 이름으로 핸들러를 찾는 BeanNameUrlHandlerMapping 이 실행에 성공하고 핸들러를 반환한다.

3. HandlerAdapter 의 supports() 를 호출하여 핸들러 어댑터를 찾는다.

4. 찾은 HttpRequestHandlerAdapter 로 핸들러를 실행하고, 결과를 반환한다.

 

@RequestMapping (실무)

실무에서 사용하는 애노테이션 기반의 컨트롤러를 지원하는 매핑과 어댑터인

RequestMappngHandlerMappingRequestMappingHandlerAdapter 를 가장 많이 사용한다.

 

- @Controller 

  : 내부의 @Component 애노테이션으로 인해 컴포넌트 스캔의 대상이 되어 스프링 빈으로 등록

  : 스프링 MVC 에서 애노테이션 기반 컨트롤러로 인식

- @RequestMapping 

  : 요청 정보를 매핑하여 해당 URL 이 호출되면 메서드가 호출된다.

  (애노테이션 기반이기 때문에 메서드 이름 임의로 지정 가능)

 

@Controller
public class SpringMemberFormControllerV1 {
  
  @RequestMapping("/springmvc/v1/members/new-form")
  public ModelAndView process() {
    return new ModelAndView("new-form");
  }
}

 

1. HandlerMapping 을 순서대로 실행하여 핸들러를 찾는다.

2. RequestMappingHandlerMapping 이 실행에 성공하여  @RequestMapping("url") 이 설정된 컨트롤러를 찾아 반환한다.

4. HandlerAdapter 의 supprots() 를 호출하여 핸들러 어댑터를 찾는다.

5. 찾은 RequestMappingHandlerAdpater 로 컨트롤러의 @RequestMapping 메서드를 실행하고, 결과를 반환한다.

애노테이션 기반 컨트롤러

특징

- 컨트롤러 통합

  : @RequestMapping 는 클래스 단위가 아닌 메서드 단위에 적용되었기 때문에, 유연하게 하나로 통합할 수 있다.

- Model 을 사용한 데이터 관리

  : Model 객체를 이용하여 데이터를 저장/조회 기능을 제공한다.

- ViewName 직접 반환

  : ModelAndView 객체를 반환하는 것이 아니라, String 형식의 뷰의 논리 이름을 반환해도 된다.

 

RequestMappingHandlerAdater

애노테이션 기반의 컨트롤러의 @RequestMapping 을 처리하는 핸들러 어댑터 구조를 자세히 살펴보자

   

ArgumentResolver
 
DispatcherServlet → → → RequestMapping
HandlerAdapter
→ → → → → → 핸들러
(컨트롤러)
   

ReturnValueHandler


 

① 컨트롤러의 파라미터, 애노테이션 정보를 기반으로 전달 데이터 생성 

  ex. HttpServletRequest, Model, @RequestParam, @ModelAttribute, @ReuqestBody, HttpEntity

호출

③ 컨트롤러의 반환값을 변환

  ex. ModelAndView, @ResponseBody, HttpEntity

 

ArgumentResolver (HandlerMethodArgumentResolver)

- 다양한 방식의 컨트롤러의 파라미터를 생성하는 역할

- 인터페이스로 제공하여 확장 가능

 

ReturnValueHandler (HandlerMethodReturnValueHandler)

- 컨트롤러가 반환한 데이터를 적절한 HTTP 응답 형태로 변환하는 역할

- 인터페이스로 제공하여 확장 가능

 

HTTP 메시지 컨버터 (HttpMessageConverter)

- ArgumentResolverReturnValueHandler 가 데이터를 변환할 때 핵심적인 변환 기능을 제공

- 인터페이스로 제공하여 확장 가능

 

HTTP 메시지 컨버터

HTTP 요청 : @RequestBody, HttpEntity, RequestEntity

HTTP 응답 : @ResponseBody, HttpEntity, ResponseEntity

 

사용 시에 HTTP 메시지 컨버터가

요청/응답 데이터의 타입미디어 타입(Content-Type, Accept)을 확인하여 변환한다.

 

기본 메시지 컨버터

0 = ByteArrayHttpMessageConverter

  : byte 처리

1 = StringHttpMessageConverter

  : 기본 문자 처리

2 = MappingJackson2HttpMessageConverter

  : 기본 객체 처리

( 생략 )

 

대상 클래스 타입, 미디어 타입을 체크해서 사용여부를 결정한다.

 

ByteArrayHttpMessageConverter

- byte[ ] 데이터를 처리

- 클래스 타입 : byte[ ]

- 미디어 타입 : */*

ex. 요청 @RequestBody byte[] data

ex. 응답 @ResponseBody return byte[] 쓰기 미디어타입 application/octet-stream 로 자동 설정

 

StringHttpMessageConverter

- String 문자로 데이터를 처리

- 클래스 타입 : String

- 미디어 타입 : */*

ex. 요청 @RequestBody String data

ex. 응답 @ResponseBody return "ok" 쓰기 미디어타입 text/plain 로 자동 설정 

 

MappingJackson2HttpMessageConverter

- application/json 처리

- 클래스 타입 : 객체 or HashMap

- 미디어 타입 : application/json 관련

ex. 요청 @RequestBody HelloData data

ex. 응답 @ResponseBody return helloData 쓰기 미디어타입 application/json 관련으로 자동 설정

 

HTTP 요청 데이터 읽기

1. HTTP 요청이 오고, 컨트롤러에서 @RequestBody, HttpEntity, RequestEntity 파라미터를 사용한다.

2. 메시지 컨버터가 메시지를 읽을 수 있는지 확인하기 위해 canRead() 를 호출한다.

  > 대상 클래스 타입을 지원하는가 ?

    ex. @RequestBody 의 대상 클래스 (byte[], String, HelloData)

  > HTTP 요청의 Content-Type 미디어 타입을 지원하는가 ?

    ex. text/plain, application/json, */*

3. 위 조건으로 canRead() 를 만족하면, read() 를 호출하여 객체를 생성하고, 반환한다.

 

HTTP 응답 데이터 생성

1. 컨트롤러에서 @ResponseBody, HttpEntity, ResponseEntity 로 값이 반환된다.

2. 메시지 컨버터가 메시지를 쓸 수 있는지 확인하기 위해 canWrite() 를 호출한다.

  > 대상 클래스 타입을 지원하는가 ?

    ex. return 의 대상 클래스 (byte[], String, HelloData)

  > HTTP 요청의 Accept 미디어 타입을 지원하는가 ?

    ex. text/plain, application/json, */*

3. 위 조건으로 canWrite() 를 만족하면, write() 를 호출하여 HTTP 응답 메시지 바디에 데이터를 생성한다.

 

 

 

 

 

 

 

 

 

 

 

 

 

출처 | 스프링 MVC 1(김영한) - 인프런

728x90