본문 바로가기
공부방

[TIL 07/04] 스프링 MVC

by hseong 2023. 7. 6.

1. Spring MVC

DispatcherServlet

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1

프론트 컨트롤러 패턴은 중앙 집중형 컨트롤러를 제일 앞단에 두고 모든 요청을 받는다. 디스패처 서블릿이 프론트 컨트롤러 패턴으로 구현되었으며 요청에 맞는 컨트롤러를 찾아서 호출하고 응답을 받아 뷰를 만든다.

이름에서 알 수 있듯이 이것 역시 하나의 서블릿이며 스프링 부트는 자동으로 서블릿 컨테이너에 등록하면서 모든 경로("/")에 대해서 맵핑한다.

Spring MVC 처리 흐름

1) DispatcherServlet에서 컨트롤러로 HTTP 요청 위임

디스패처 서블릿이 HTTP 요청을 받는다.

요청 URL 기준으로 어떤 컨트롤러에게 작업을 위임할지 파라미터 정보, HTTP 메서드 등을 참고해서 결정한다. 이 과정을 핸들러 맵핑(HandlerMapping) 이라고 한다.

이러한 맵핑을 결정하는 방법 중 가장 많이 쓰이는 것이 어노테이션 기반의 컨트롤러에서 사용되는 RequestMappingHandlerMapping이다. 스프링 빈 중에서 @RequestMapping 또는 @Controller가 클래스 레벨에 붙어 있는 경우 맵핑 정보로 인식되어 이에 의해 해당 URL이 어떤 메서드를 호출하는지 결정되게 된다.

요청을 처리할 핸들러가 결정되었다면 핸들러가 실행할 수 있는 핸들러 어댑터를 조회한다. 실제 컨트롤러를 호출하는 것은 핸들러 어댑터(HandlerAdapter) 를 통해서 수행하게 된다.

디스패처 서블릿이 요청을 핸들러 어댑터에 전달하면 어댑터가 적절히 변환하여 컨트롤러가 받을 수 있는 메서드 파라미터로 변환해서 핸들러를 실행하게 된다.

2) DispatcherServlet의 뷰 호출과 모델 참조

뷰 리졸버는 핸들러 어댑터가 반환한 정보를 가지고 어떠한 뷰를 반환할지 결정한다.

획득한 논리 뷰 이름을 가지고 디스패처 서블릿에 등록된 뷰 리졸버를 통해 매칭되는 뷰를 찾는다. 스프링 부트는 여러 뷰 리졸버를 자동 등록하였으며 순서대로 이를 호출하여 뷰를 찾는다.

디스패처 서블릿은 반환된 뷰를 렌더링하여 클라이언트에게 실제 응답을 반환하게 된다.

3) 정리

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1
  1. HTTP 요청: 디스패처 서블릿이 HTTP 요청을 받는다.
  2. 핸들러 조회: 핸들러 맵핑을 통해서 URL에 맵핑된 핸들러를 조회한다.
  3. 핸들러 어댑터 조회: 핸들러를 실행할 수 있는 핸들러 어탭터를 조회한다.
  4. 핸들러 어탭터 실행: 디스패처 서블릿이 핸들러 어댑터를 실행한다.
  5. 핸들러 실행: 핸들러 어댑터가 실제 핸들러를 실행한다.
  6. ModelAndView 반환: 핸들러 어댑터는 핸들러가 반환하는 정보를 ModelAndView로 변환해서 반환한다.
  7. 뷰 리졸버 호출: 뷰 리졸버를 찾고 실행한다.
  8. 뷰 반환: 뷰리 졸버는 뷰의 논리 이름을 물리 이름으로 바꾸고 렌더링 역할을 담당하는 뷰 객체를 반환한다.
  9. 뷰 렌더링: 뷰를 통해서 뷰를 렌더링 한다.

2. 실습

스프링 프레임워크의 @EnableWebMvc를 사용하게 되면 스프링 MVC에 필요한 빈들이 자동으로 등록된다.

@Configuration
@EnableWebMvc
public class WebConfig {
}

또한 WebMvcConfigurer 인터페이스를 구현하여 MVC 구성 정보를 설정할 수 있다.

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

}

예를 들어 configureViewResolvers()를 오버라이딩하면 JSP 뷰 파일들의 경로를 설정해주거나, 타임리프와 같은 템플릿 엔진을 사용하기 위한 뷰 리졸버를 등록할 수 있다. 또는, addFormatters()를 사용하여 컨버터를 등록하고 우리가 원하는 타입으로의 변환을 수행할 수 있다.

컨트롤러에는 다음과 같이 URL 호출 시 실행할 메서드를 작성한다. URL 맵핑을 위한 @RequestMapping을 추가하면 RequestMappingHandlerMapping은 이를 맵핑 정보로 인식한다. 디스패처 서블릿이 RequestMappingHandlerApapter를 실행하면서 핸들러 정보를 넘겨 받고 해당 핸들러를 실행하고 그 결과를 ModelAndView로 반환하게 된다.

@RequestMapping(value = "/hello", method = RequestMethod.GET)
public ModelAndView hello() {
    return new ModelAndView("hello");
}

좀 더 간단하게 하면 Model을 파라미터로 받고 반환 값을 뷰의 논리 이름으로 할 수 있다.

@GetMapping("/hello")
public String hello(Model model) {
    return "hello";
}

정적 리소스 처리하기

스프링인 정적 컨텐츠를 제공하기 위한 리소스 핸들러를 제공한다. WebMvcConfigureraddResourceHandlers()를 이용하여 이를 쉽게 추가할 수 있다.

다음은 /resources/로 시작하는 경로로 요청이 주어졌을 때 리소스 핸들러가 작동하고 /reseources 아래 디렉터리에서 리소스 파일을 찾게 된다.

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/resources/**")
            .addResourceLocations("/resources");
}

타임리프

타임리프는 자바 템플릿 엔진으로 템플릿을 파싱하여 화면을 렌더링해주는 역할을 한다. 성능상으로는 조금 느리지만 순수 HTML을 유지하여 높은 가독성과 막강한 표현식을 가진다.

순수 스프링에서 타임리프를 사용하기 위해서는 WebMvcCongiurerconfigureViewResolvers()를 통해 뷰 리졸버를 등록해줘야 한다.

타임리프는 다양한 표현식을 제공한다.

  • 변수 표현식: ${...}
  • 선택 변수 표현식: *{...}
  • 메시지 표현식: #{...}
  • 링크 URL 표현식: @{...}
  • 조각 표현식: ~{...}

학습 내용 출처

프로그래머스 데브코스 백엔드 4기

김영한님의 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술

스프링 공식문서 DispatcherServlet