지난 포스팅에서 HandlerAdapter를 다루면서 @RequestMapping으로 선언된 Handler를 알아보았다.
DispatcherServlet은 4가지 HandlerAdapter에 접근 가능하다. 그중 우선순위가 가장 높은 HandlerAdapter는 RequestMappingHandlerAdapter이다. RequestMappingHandlerAdapter는 @RequestMapping 어노테이션으로 선언된 Handler(Controller)를 지원하는 Adapter이다.
@Component // Bean으로 등록!
@RequestMapping // 핸들러임을 명시!
public class SpringController {
@RequestMapping("/springmvc/new-form")
public ModelAndView process(){
// [ 비즈니스 로직 중략 ... ]
return new ModelAndView("new-form");
}
}
예시 Controller를 하나 만들어 보았다. 단순한 클래스가 Controller로 동작하려면 2가지 어노테이션이 필요하다.
1. @Component
2. @RequestMapping
@Component는 Spring Bean으로 등록하기 위한 어노테이션이다. Bean 객체로 생성되어 있어야 요청이 들어왔을 때 사용할 수 있다.
@RequestMapping
@RequestMapping은 해당 Controller가 RequestMappingHandlerAdapter가 지원하는 Handler임을 명시한다. 지난 포스팅에서 말했듯, DispatcherServlet은 Controller의 비즈니스 로직을 실행할 권리를 Adapter에게 위임했다. Handler를 지원하는 적합한 HandlerAdapter가 있어야 Controller의 비즈니스 로직이 실행될 수 있다. 그러므로 Controller는 어노테이션으로 RequestMappingHandlerAdapter가 실행할 수 있는 Controller임을 표시해야 한다. 이때 사용되는 어노테이션이 바로, @RequestMapping이다.
@RequestMapping의 가장 큰 장점은 하나의 Controller에 여러 개의 비즈니스 로직을 처리하는 메소드를 둘 수 있다는 점이다. 과거에 요청을 처리하는 Handler 역할을 하던 HandlerFunction 인터페이스, HttpRequestHandler 인터페이스, Controller 인터페이스는 모두 함수형 인터페이스이다. 메소드가 단 하나뿐이다. 그래서 구현체로 생성된 Handler도 메소드가 하나 뿐이라 하나의 비즈니스 로직만 처리 가능했다.
@RequestMapping을 사용하면 하나의 핸들러에 다수의 메소드를 선언할 수 있다.
@Component
@RequestMapping("/springmvc/members")
public class SpringController {
private final MemberRepository memberRepository = MemberRepository.getInstance();
@RequestMapping(value = "/new-form")
public String newForm(){
// [ 비즈니스 로직 1 ]
return "new-form";
}
@RequestMapping
public String members(Model model){
// [ 비즈니스 로직 2 ]
return "members";
}
@RequestMapping(value = "/save")
public String save(@RequestParam("username") String username, @RequestParam("age") int age, Model model){
// [ 비즈니스 로직 3 ]
return "save-result";
}
}
위 코드처럼 @RequestMapping을 메소드에 선언하고 세부URL을 명시하면 된다.
정리하면, 요청URL이 들어오면 DispatcherServlet은 URL에 매핑되는 Handler를 가져온다. DispatcherServlet은 Handler를 지원하는 HandlerAdapter를 가져오고 Handler 실행을 명령한다. HandlerAdapter는 요청에 맞는 비즈니스 로직을 실행을 위해 @RequestMapping으로 선언된 메소드의 세부URL을 확인하여 요청URL과 일치하는 메소드를 실행한다. 이와같은 원리로, Handler는 실행가능한 여러 개의 메소드를 가질 수 있게 되었다. 이런 장점으로 현재 가장 많이 사용되고 있는 Controller 유형이기도 하다.
@Controller
다시 한번 말하면, 단순한 클래스가 Controller로 동작하려면 두 가지 어노테이션이 필요하다
1. @Component
2. @RequestMapping
Bean으로 등록되어야 하고 Handler임을 명시해야 한다. 이 두 가지를 한번에 하는 어노테이션이 있다. 그것이 @Controller이다.
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // @Component 가지고 있음
public @interface Controller {
//중략...
}
@Controller 어노테이션 소스를 열어보면 @Component가 등록되어 있음을 확인할 수 있다. 그러므로 @Controller로 선언된 클래스도 Bean으로 등록 가능해진다. 그런데 Handler임을 명시하는 @RequestMapping이 없다. 앞서, @RequestMapping이 선언되어야 하는 이유는 RequestMappingHandlerAdapter가 처리할 수 있는 Handler임을 나타내기 위함이라 말했다.
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
implements MatchableHandlerMapping, EmbeddedValueResolverAware {
//중략...
@Override
protected boolean isHandler(Class<?> beanType) { // Handler가 아래 유형인지 확인
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || //Controller 어노테이션
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)); //RequestMapping 어노테이션
}
//중략...
}
위 코드를 보면 알 수 있듯이, RequestMappingHandlerAdapter가 처리할 수 있는 Handler의 종류는 2가지가 있다.
1) @RequestMapping으로 선언된 클래스
2) @Controller로 선언된 클래스
두 어노테이션 중 하나로 선언된 Handler는 RequestMappingHandlerAdapter가 처리할 수 있는 Handler인 것이다. 그러므로 굳이 @RequestMapping으로 선언되어 있지 않아도 @Controller로 선언되어 있으면 RequestMappingHandlerAdapter가 처리 가능한 것이다.
@Controller
public class SpringController {
@RequestMapping("/springmvc/new-form")
public ModelAndView process(){
// [ 비즈니스 로직 중략 ... ]
return new ModelAndView("new-form");
}
}
그러므로 매번 두 개의 어노테이션을 사용할 필요가 없다. @Controller 하나면 두 가지 기능을 모두 수행할 수 있다. 그리고 메소드는 @RequestMapping로 선언하여 RequestMappingHandlerAdapter가 처리할 수 있도록 하면 된다.
정리하면 단순 클래스가 Controller로 동작하려면 두 가지가 명시되어야 한다.
1) Bean임을 명시
2) Handler임을 명시
두 가지를 한번에 명시할 수 있는 어노테이션이 @Controller이다. @Controller에 @ResponseBody를 합친 @RestController도 존재하는데, 이에 대해서는 나중에 다루어 보도록 하겠다.
참고자료
'SPRING > Spring MVC' 카테고리의 다른 글
[SpringMVC] HTTP 요청 파라미터 - @RequestHeader (0) | 2023.08.11 |
---|---|
[SpringMVC] @RequestMapping의 다양한 속성 (0) | 2023.08.10 |
[SpringMVC] HandlerAdapter (0) | 2023.08.10 |
[SpringMVC] MVC 패턴 구현하기(4) - Adapter (0) | 2023.08.09 |
[SpringMVC] MVC 패턴 구현하기(3) - Model (0) | 2023.08.09 |