Spring MVC (Spring Web MVC)
Spring MVC(또는 Spring Web MVC)는 서블릿 API를 기반으로 구축된 웹 프레임워크다. 여기서 서블릿 API란 자바를 사용하여 클라이언트의 요청을 처리하고 동적인 웹 컨텐츠를 생성하여 응답하는데 사용되는 인터페이스를 말한다.
Spring MVC는 프론트 컨트롤러 패턴을 활용한다. 프론트 컨트롤러 패턴이란 클라이언트의 모든 요청을 단일 진입점(프론트 컨트롤러)에서 처리하고, 요청에 따라 적절한 핸들러로 분배하는 방식을 말한다. 스프링에서 모든 요청을 처리하는 단일 진입점은 DispatcherServlet으로, Spring Context 내에서 가장 먼저 클라이언트의 요청을 받아 처리한다.
DispatcherServlet의 동작 과정
- 디스패처 서블릿 (DispatcherServlet)
- (1) : 스프링에서 클라이언트의 모든 요청을 처리하는 단일 진입점 역할을 하는 서블릿 API.
- (2): 핸들러 매핑 (Handler Mapping)
- 클라이언트의 요청 URL을 분석하여 해당 요청을 처리할 컨트롤러(핸들러)를 찾는다
- (3): 핸들러 어댑터 (Handler Adapter)
- (4, 5) : 핸들러 매핑을 통해 찾은 컨트롤러를 실행하는 역할을 한다.
- (6) : 컨트롤러는 내부 비즈니스 로직을 수행하고 결과를 반환한다.
- (7) : 컨트롤러의 실행 결과를 디스패처 서블릿이 이해할 수 있는 형태로 변환한다.
- (8) : 최종 응답을 클라이언트에게 반환
핸들러 매핑 (Handler Mapping)
[HandlerMapping 인터페이스 구조]
public interface HandlerMapping {
String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler";
@Deprecated
String LOOKUP_PATH = HandlerMapping.class.getName() + ".lookupPath";
String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";
String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";
String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";
String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";
String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";
default boolean usesPathPatterns() {
return false;
}
@Nullable
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
핸들러 매핑 과정은 디스패처 서블릿이 클라이언트의 요청을 처리할 컨트롤러(핸들러)를 찾는 과정이다. 클아이언트의 HTTP 요청 관련 정보를 담은 HttpServletRequest 객체를 파라미터로 받아 getHandler 메서드를 호출해, 해당 요청을 처리할 핸들러 정보를 가져온다. 이 과정이 어떻게 수행되는지 가장 대중적으로 사용되는 @RequsetMapping 애너테이션을 기반으로 살펴보자.
[@RequestMapping 애너테이션 구조]
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
@Reflective({ControllerMappingReflectiveProcessor.class})
public @interface RequestMapping {
String name() default "";
@AliasFor("path")
String[] value() default {};
@AliasFor("value")
String[] path() default {};
RequestMethod[] method() default {};
String[] params() default {};
String[] headers() default {};
String[] consumes() default {};
String[] produces() default {};
}
@RequestMapping 애너테이션의 내부 구조를 살펴보면 `@Reflective({ControllerMappingReflectiveProcessor.class})` 애너테이션을 활용하는 것을 볼 수 있다. @Reflective 애너테이션은 특정 클래스나 메서드에 리플렉션(런타임에 클래스, 인터페이스, 메서드 등의 정보를 동적으로 접근하고 조작하는 기술) 처리가 필요함을 컴파일러나 런타임에 알린다. 해당 애너테이션을 통해 ControllerMappingReflectiveProcessor는 런타임 환경에서 요청 URL, HTTP 메서드, 파라미터 정보 등을 추출하여 디스패처 서블릿이 클라이언트의 요청을 적절한 컨트롤러 메서드에 매핑할 수 있도록 핸들러 매핑 정보를 생성한다. 이러한 과정을 통해 @RequestMapping 애너테이션이 붙은 컨트롤러 메서드에 대한 핸들러 매핑이 수행된다.
참고로 @GetMapping, @PostMapping과 같은 애너테이션은 내부적으로 @RequestMapping을 활용한다.
[@GetMapping 애너테이션 구조]
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(
method = {RequestMethod.GET}
)
public @interface GetMapping {
// ...
}
핸들러 어댑터 (Handler Adapter)
[HandlerAdapter 인터페이스 구조]
public interface HandlerAdapter {
// 해당 핸들러가 요청이 처리 가능한지에 대한 결과를 반환
boolean supports(Object handler);
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
/** @deprecated */
@Deprecated
long getLastModified(HttpServletRequest request, Object handler);
}
디스패처 서블릿에서 핸들러 매핑을 통해 찾은 컨트롤러(핸들러)에 맞는 핸들러 어댑터를 선택한다.
선택된 핸들러 어댑터는 supports 메서드를 호출하여 해당 컨트롤러를 실행할 수 있는지 확인하고, 해당 메서드의 반환 결과가 true라면 handle 메서드를 호출하여 컨트롤러를 실행한다.
handle 메서드는 디스패처 서블릿으로 ModelAndView 객체를 반환하고, 디스패처 서블릿에서는 뷰 리졸버(ViewResolver)를 통해 뷰를 선택하고 전달받은 ModelAndView 정보를 활용하여 뷰를 랜더링한다.
RESTful API와 Spring MVC
@RestController(또는 @ResponseBody) 애너테이션이 붙은 경우에는 ModelAndView를 반환하지 않는다. 이러한 경우는 어떻게 처리되는걸까?
@RestController(또는 @ResponseBody) 애너테이션이 붙은 경우에는 HandlerAdapter의 구현체 중 하나인 RequestMappingHandlerAdapter 를 사용한다. 이 어댑터에서 @RestController(또는 @ResponseBody) 애너테이션이 붙은 메서드를 실행하고 HTTP 메세지 컨버터를 이용하여 응답 데이터를 JSON 또는 XML과 같은 형식으로 반환하여 클라이언트에 전달한다.
'spring' 카테고리의 다른 글
[Spring] SpringSecurity 동작 과정 (0) | 2025.03.28 |
---|---|
[Spring] @SpringBootApplication (0) | 2025.03.26 |
[Spring] ApplicationContext (0) | 2025.03.23 |
[Spring] 필터와 인터셉터 (0) | 2025.03.19 |
[Spring] 스프링 의존성 주입 (0) | 2025.03.18 |