스프링 공부 (인프런 김영한 선생님)/스프링 MVC 1편

[스프링 웹 MVC 1편] 17. 스프링 MVC - 핸들러 매핑과 어댑터 / 뷰 리졸버

2023. 5. 29. 19:00

핸들러 매핑과 핸들러 어댑터들이 어떤것들이 스프링 MVC에서 사용되는지 알아보겠습니다.

 

지금은 애노테이션 열풍이 불고 있으나,

과거에 주로 사용했던 스프링이 주로 사용했던 간단한 컨트롤러로 핸들러 매핑과 어댑터에 대해 설명해보겠습니다.

 

@FunctionalInterface
public interface Controller {

 /**
  * Process the request and return a ModelAndView object which the DispatcherServlet
  * will render. A {@code null} return value is not an error: it indicates that
  * this object completed request processing itself and that there is therefore no
  * ModelAndView to render.
  * @param request current HTTP request
  * @param response current HTTP response
  * @return a ModelAndView to render, or {@code null} if handled directly
  * @throws Exception in case of errors
  */
 @Nullable
 ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;

}
package org.springframework.web.servlet.mvc;

스프링 MVC에서 과거에 주로 사용했던 컨트롤러 중 하나를 긁어왔습니다.

보면 ModelAndView를 반환하는 것으로 보아 V3 버전과 유사한것을 알 수 있습니다.

 

참고로 Controller 클래스는 @Controller 애노테이션과는 완전히 다른 방식의 컨트롤러임을 기억합시다.

 

package hello.servlet.web.springmvc.old;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.apachecommons.CommonsLog;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

// StereoType의 컨트롤러는 애노테이션

@Component("/springmvc/old-controller") // spring bean의 이름을 urlPattern에 맞춤
public class OldController implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
        throws Exception {
        System.out.println("OldController.handleRequest");
        return null;
    }
}

implements 할때 유의할 점이 StereoType에 속한 Controller 클래스는 애노테이션 관련 컨트롤러 클래스이므로 우리가 작성할때는 이전에 패키지에서 명시했듯 MVC 패키지 내의 Controller 객체를 구현해야한다는 점입니다.

 

@Component를 통해 이 OldController 클래스는 그 뒤에 명시된 문자열로 urlPatterns가 등록된다.

빈의 이름으로 url이 매핑되는 것으로 이해하자, 저 빈의 이름은 /springmvc/old-controller가 되는 것이다.


이 컨트롤러가 호출되는 방식은 어떻게 되는 것인가?

먼저 스프링 MVC의 처리 구조를 확인하자.

 

이 컨트롤러가 호출되기 위해서는 다음 두 가지가 필요하다

1) 핸들러 매핑 - 핸들러 매핑 정보로 이 빈이 등록될 수 있어야한다

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

private void initHandlerMappingMap() {
    handlerMappingMap.put("/front-controller/v5/v3/members/new-form", new MemberFormControllerV3());
    handlerMappingMap.put("/front-controller/v5/v3/members/save", new MemberSaveControllerV3());
    handlerMappingMap.put("/front-controller/v5/v3/members/", new MemberListControllerV3());
    // V4 컨트롤러 추가
    handlerMappingMap.put("/front-controller/v5/v4/members/new-form", new MemberFormControllerV4());
    handlerMappingMap.put("/front-controller/v5/v4/members/save", new MemberSaveControllerV4());
    handlerMappingMap.put("/front-controller/v5/v4/members/", new MemberListControllerV4());
}

우리가 짠 MVC를 기준으로 이 코드에 어떻게 저 빈의 이름을 가져다가 Key, Value의 형태로 넣을 것인지 생각해봐야한다.

 

2) 핸들러 어댑터

- 핸들러 매핑을 통해서 찾은 핸들러를 실행할 수 있는 핸들러 어댑터가 필요하다 .handle() 메소드

Controller 인터페이스를 찾아서 실행할 수 있는 (.service()) 핸들러 어댑터를 찾고 실행해야 한다.

 

스프링은 이미 필요한 핸들러 매핑과 핸들러 어댑터를 대부분 이미 구현해두었다고 한다.


HandlerMapping (우선순위)

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

1 = BeanNameUrlHandlerMapping : 스프링 빈의 이름으로 핸들러를 찾는다.

 

HandlerAdapter (우선순위)

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

1 = HttpRequestHandlerAdapter : HttpRequestHandler 처리

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

 

현재 스프링은 애노테이션 방식의 @RequestMapping을 가장 많이 사용하고 있다


핸들러 매핑으로 핸들러 조회

 

1) HandlerMapping을 순서대로 실행하여, 핸들러를 찾습니다

2) 이 경우 빈 이름으로 핸들러를 찾아야하기 때문에 이름 그대로

빈 이름으로 핸들러를 찾아주는 BeanNameUrlHandlerMapping이 실행에 성공하고 핸들러인 OldController를 반환합니다.

 

2) 핸들러 어댑터 조회

Handler Adapter의 supports() 메서드를 순서대로 호출합니다.

HttpRequestHandlerAdapter는 핸들러인 MyHttpRequestHandler를 내부에서 실행하고 그 결과를 반환합니다.

-> Adapter 객체에서 handler() 실행한다는 뜻

 

3) 핸들러 어댑터 실행

DispatcherServlet(프론트 컨트롤러)에서 조회한 HttpRequestHandlerAdapter를 실행하면서, 핸들러 정보도 함께 넘겨준다.

HttpRequestHandlerAdapter는 핸들러인 MyHttpRequestHandler를 내부에서 실행하고, 그 결과를 반환한다.

-> 결과는 V에 따라 ModelAndView일수도 MyView처럼 View만 반환할수도 있다.

 

정리 - MyHttpRequestHandler를 실행하면서 사용된 객체는 다음과 같다.

HandlerMapping = BeanNameUrlHandlerMapping

HandlerAdapter = HttpRequestHandlerAdapter


뷰 리졸버에 대해 알아보자

OldController 코드를 변경하여 뷰 리졸버 동작방식에 대해서 알아보도록 하겠다

package hello.servlet.web.springmvc.old;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.apachecommons.CommonsLog;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

// SterpType의 컨트롤러는 애노테이션

@Component("/springmvc/old-controller") // spring bean의 이름을 urlPattern에 맞춤
public class OldController implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
        throws Exception {
        System.out.println("OldController.handleRequest");
        return new ModelAndView("new-form");
    }
}

이때 코드를 그냥 실행하면 WhiteLabelError페이지 오류가 발생한다.

스프링의 application.properties에 다음과 같은 코드를 추가하자.

 

logging.level.org.apache.coyote.http11=debug

spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp

뷰 리졸버는 InternalResourceViewResolver를 사용한다 (스프링 기본값)

다음과 같이 prefix와 suffix를 등록하여 사용한다.

 


뷰 리졸버 동작방식

스프링 부트가 자동으로 등록하는 뷰 리졸버에는 두가지가 있다

1 = BeanNameViewResolver(빈 이름으로 뷰를 찾아서 반환한다)

2= InternalResourceViewResolver(JSP를 처리할 수 있는 뷰를 반환한다)

 

실제 호출과정은 다음과 같다.

 

1) 핸들러 어댑터 호출

핸들러 어댑터를 통해 new-form이라는 논리 뷰 이름을 획득한다.

 

2) ViewResolver 호출

new-form이라는 뷰 이름으로 viewResolver를 순서대로 호출한다.

BeanNameViewResolver는 new-form이라는 스프링 빈으로 등록된 뷰를 찾아야하나 없다.

- 커스텀 뷰 객체를 사용해야하는 경우 사용함

InternalResourceViewResolver가 호출된다.

 

3) InternalResourceViewResolver

이 뷰 리졸버는 InternalResourceView를 반환한다

 

4) 뷰 - InternalResourceView

InternalResourceView는 JSP처럼 forward()를 호출해서 처리할 수 있는 경우에 사용한다.

 

5) view.render()

view.render()가 호출되고 InternalResourceView는 forward()를 사용해서 JSP를 실행한다.

나머지 뷰 템플릿들은 forward 과정 없이 바로 렌더링이 가능하다 (자바 코드로 렌더링)

 


그림을 약간 수정할 필요가 있겠다, 핸들러 내용이 컨트롤러에서 뷰로 넘어가는데 그렇다면 위의 그림에서 뷰 리졸버와 프론트 컨트롤러 사이에는 어댑터 그림이 하나 추가되어야 할 필요가 있겠다.

'스프링 공부 (인프런 김영한 선생님) > 스프링 MVC 1편' 카테고리의 다른 글

[스프링 웹 MVC 1편] 19. 스프링 MVC - 기본 기능 (로깅)  (0) 2023.06.01
[스프링 웹 MVC 1편] 18. 스프링 MVC - 시작  (0) 2023.05.30
[스프링 웹 MVC 1편] 16. 스프링 MVC - V1 ~ V5와 비교했을 때 (구조 이해)  (0) 2023.05.29
[스프링 웹 MVC 1편] 15. MVC 프레임워크 제작해보기 - v5  (0) 2023.05.23
[스프링 웹 MVC 1편] 14. MVC 프레임워크 제작해보기 - v4  (0) 2023.05.23
'스프링 공부 (인프런 김영한 선생님)/스프링 MVC 1편' 카테고리의 다른 글
  • [스프링 웹 MVC 1편] 19. 스프링 MVC - 기본 기능 (로깅)
  • [스프링 웹 MVC 1편] 18. 스프링 MVC - 시작
  • [스프링 웹 MVC 1편] 16. 스프링 MVC - V1 ~ V5와 비교했을 때 (구조 이해)
  • [스프링 웹 MVC 1편] 15. MVC 프레임워크 제작해보기 - v5
ProgYun.
ProgYun.
인내, 일관성, 그리고 꾸준함을 담습니다.
ProgYun.
Perseverance, Consistency, Continuity
ProgYun.
전체
오늘
어제
  • 분류 전체보기
    • 칼럼
    • 일상생활
      • 월별 회고
      • 인생 이야기 (대학생활)
      • 취준
      • 운동인증
      • 제품 사용 후기와 추천
    • 스프링 공부 (인프런 김영한 선생님)
      • 스프링 핵심원리
      • 스프링 MVC 1편
      • 스프링 MVC 2편
    • 면접 준비
    • 전공
      • OOP 정리
      • Design Pattern
    • 스터디
    • English
      • Electronics(Laptop)
      • 1일 1단어

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 코로나19
  • 해외직구
  • 피로그래밍
  • 자가격리
  • ssd
  • 일상
  • 오미크론
  • 하이닉스
  • 대학생
  • mason
  • 윈도우재설치
  • 자존감
  • p31
  • 확진자
  • 코로나
  • NVME
  • 윈도우10
  • 포맷
  • 편입생
  • 컴공

최근 댓글

최근 글

hELLO · Designed By 정상우.
ProgYun.
[스프링 웹 MVC 1편] 17. 스프링 MVC - 핸들러 매핑과 어댑터 / 뷰 리졸버
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.