[Spring Web MVC] Spring MVC
실제 Spring MVC 프레임워크의 구조이다.
지난번에 만든 MVC 프레임워크와 크게 다른 점은 없다.
다르게 사용되는 용어들만 새로 정리하자.
FrontController -> DispatcherServlet
handlerMappingMap -> HandlerMapping
MyHandlerAdapter -> HandlerAdapter
ModelView -> ModelAndView
viewResolver -> ViewResolver (Interface)
MyView -> View (Interface)
여기서 DispatcherServlet이 가장 중요하다.
프론트 컨트롤러 대신 사용하는 용어로, 역시 HttpServlet을 상속받아 서블릿으로 동작한다.
WebSerblet 애너테이션을 사용하는 대신 스프링 부트는 내장 톰캣 서버를 띄우면서 DispatcherServlet을 서블릿으로 등록하고, 모든 urlPattern에 대해 매핑되도록 설정한다. (자세할 수록 높은 우선순위를 가지기 때문에 개발자가 만든 서블릿이 무시되지는 않는다)
서블릿이 호출되면 service() 메서드가 실행되고, 이 메서드를 시작으로 doDispatch를 포함한 여러 메서드가 호출된다.
doDispatch 메서드는 핸들러 조회 / 핸들러 어댑터 조회 및 실행 / 뷰 렌더링 / 뷰 리졸버 호출 등 기능 대부분을 실행한다.
동작 순서는 다음과 같다.
1. 핸들러 조회 : 이 때 요청 URL 뿐만 아니라 HTTP 요청 메세지의 다양한 정보를 활용한다.
2. 핸들러 어댑터 조회 : 핸들러 조회를 통해 찾을 핸들러를 실행할 어댑터를 찾는다.
3. 핸들러 어댑터 실행 : 핸들러 어댑터가 실제 핸들러를 찾아서 실행한다.
4. ModelAndView 반환 : 핸들러가 뭘 반환하든 ModelAndView로 변환해서 반환한다.
5. viewResolver 호출 : JSP의 경우는 InternalResouceViewResolver가 실행된다.
6. View 반환 : 뷰 리졸버를 통해 이름을 제대로 받고 렌더링을 담당하는 뷰 객체를 반환한다.
7. 뷰 렌더링 : 뷰 객체를 통해 렌더링한다.
스프링 MVC는 대부분의 기능을 인터페이스로 제공해 DispatcherServlet을 바꾸지 않고 대부분을 확장할 수 있다.
HandlerMapping
요청을 처리할 수 있는 적절한 컨트롤러를 찾을 때 사용된다.
1. RequestMappingHandlerMapping : 애너테이션 기반의 컨트롤러인 @RequestMapping에서 사용된다.
2. BeanNameUrlHandlerMapping : URL이 들어오면 해당 URL과 같은 등록된 스프링 빈의 이름으로 핸들러를 찾는다.
거의 1번 방식으로 개발한다.
URL등 여러 정보로 핸들러를 찾으면 해당 핸들러를 처리할 수 있는 핸들러 어댑터를 찾아야 한다.
HandlerAdapter
1. RequestMappingHandlerAdapter : 애너테이션 기반의 컨트롤러인 @RequestMapping에서 사용된다.
2. HttpRequestHandlerAdapter : HttpRequestHandler 을 처리한다.
3. SimpleControllerHandlerAdapter : Controller 인터페이스 (애너테이션 X) 을 처리한다.
핸들러 어댑터를 찾으면 실행한다.
handle 메서드를 실행하고 그 결과를 반환한다.
핸들러 매핑과 핸들러 어댑터는 우선순위가 높은 방식부터 차례대로 확인한다.
ViewResolver
1. BeanNameViewResolver : 빈 이름으로 뷰를 찾아서 반환한다.
2. InternalResourceViewResolver : 해당 뷰 리졸버가 JSP를 처리할 수 있는 뷰를 반환한다.
InternalResourceViewResolver는 InternalResourceView를 반환하고 해당 뷰를 통해 forward를 호출하고 JSP를 호출한다.
(JSP 제외 나머지 뷰 템플릿은 forward 없이 렌더링된다.)
spring.mvc.view.prefix = /WEB-INF/views/
spring.mvc.view.suffix = .jsp
설정 파일로 뷰 리졸버 관련 접두사와 접미사를 추가 해 줄 수 있다.
스프링이 제공하는 컨트롤러는 애너테이션 기반으로 동작해 편하게 사용할 수 있다.
@Controller
public class SpringMemberFormControllerV1 {
@RequestMapping("/springmvc/v1/members/new-form")
public ModelAndView process(){
return new ModelAndView("new-form");
}
}
@Controller : 내부에 @Component 애너테이션이 있어 스프링이 자동으로 스프링 빈으로 등록하고, 스프링 MVC에서 애너테이션 기반 컨트롤러로 인식한다.
@RequestMapping : 요청 정보를 매핑한다. 애너테이션을 기반으로 동작하기 때문에 메서드의 이름은 임의로 작성해도 된다.
HandlerMapping의 일부로 클라이언트의 요청을 어떤 컨트롤러가 처리할건지 결정한다.
ModelAndView : 모델과 뷰 정보를 담아서 반환한다.
RequestMappingHandlerMapping 은 스프링 빈 중에서 @RequestMapping 또는 @Controller 가 클래스 레벨에 붙어 있어야 매핑 정보로 인식한다.
@Controller
public class SpringMemberSaveControllerV1 {
private MemberRepository memberRepository = MemberRepository.getInstance();
@RequestMapping("/springmvc/v1/members/save")
public ModelAndView process(HttpServletRequest request, HttpServletResponse response) {
String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age"));
Member member = new Member(username, age);
memberRepository.save(member);
ModelAndView mv = new ModelAndView("save-result");
mv.addObject("member", member);
return mv;
}
}
@RequestMapping 애너테이션은 메서드 레벨과 클래스 레벨에 붙을 수 있어 하나의 컨트롤러에 여러 가지 메서드를 포함하도록 설계할 수 있다.
@Controller
@RequestMapping("/springmvc/v2/members/")
public class SpringMemberControllerV2 {
private MemberRepository memberRepository = MemberRepository.getInstance();
@RequestMapping("/new-form")
public ModelAndView newForm(){
return new ModelAndView("new-form");
}
@RequestMapping
public ModelAndView save() {
List<Member> members = memberRepository.findAll();
ModelAndView mv = new ModelAndView("members");
mv.addObject("members", members);
return mv;
}
@RequestMapping("/save")
public ModelAndView members(HttpServletRequest request, HttpServletResponse response) {
String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age"));
Member member = new Member(username, age);
memberRepository.save(member);
ModelAndView mv = new ModelAndView("save-result");
mv.addObject("member", member);
return mv;
}
}
클래스에 @RequestMapping 애너테이션을 붙여 URL 경로를 줄여서 쓸 수 있다.
지금 이렇게 컨트롤러만 따로 구현해도 되는 이유가 디스패처 서블릿 덕분이다.
스프링 프레임워크 내부에 이미 프론트 컨트롤러 역할을 하는 DispatcherServlet이 구현되어있고, 귀찮은 작업을 다 해 준다. (핸들러 매핑 써주기, 뷰의 논리적 이름 해석하기 등등.. 이외에 앞에서 언급했던 내용도)
DispatcherServlet은 그대로 가져다가 쓰고, 필요한 컨트롤러들만 생성해 애너테이션을 적절히 붙여서 사용하자.
다시 돌아와서..
스프링 MVC를 사용하는데, 여전히 ModelAndView를 만들어서 반환해야 하는 귀찮음이 있다.
@Controller
@RequestMapping("/springmvc/v3/members")
public class SpringMemberControllerV3 {
private MemberRepository memberRepository = MemberRepository.getInstance();
// @RequestMapping("/new-form", method= RequestMethod.GET)
@GetMapping("/new-form")
public String newForm(){
return "new-form";
}
@RequestMapping
public String members(Model model) {
List<Member> members = memberRepository.findAll();
model.addAttribute("members", members);
return "members";
}
// @RequestMapping("/save", method = RequestMethod.POST)
@PostMapping("/save")
public String save(@RequestParam("username") String name, @RequestParam("age") int age, Model model) {
Member member = new Member(name, age);
memberRepository.save(member);
model.addAttribute("member", member);
return "save-result";
}
}
스프링 MVC를 사용하면 이렇게 간단하게 처리된다...
각각의 메서드에 @RequestMapping 애너테이션을 통해 클라이언트의 요청 메서드도 넣을 수 있었는데, 이걸 한 번에 쓰는 애너테이션을 도입해 URL 매핑과 요청 메서드를 한 번에 작성했다.
메서드 각각의 반환타입을 String으로 바꿔서 DispatcherServlet이 알아서 해석하게 설계했다.
인자로 request와 response 대신 @RequestParam을 사용해 형변환도 할 필요 없이 간단하게 작성했고, Model을 통해 데이터를 넘겨주는 작업도 간단하게 바꿨다.
JSP, Thymeleaf 등 뷰 템플릿 엔진을 사용해서 개발 할 때는 거의 이 방법을 사용한다.
'Spring > Spring Web MVC' 카테고리의 다른 글
[Spring Web MVC] 응답 정보 다루기 (0) | 2022.08.17 |
---|---|
[Spring Web MVC] 요청 정보 다루기 (0) | 2022.08.17 |
[Spring Web MVC] Adaptor (0) | 2022.08.16 |
[Spring Web MVC] Model (0) | 2022.08.15 |
[Spring Web MVC] Front Controller (0) | 2022.08.15 |
댓글
이 글 공유하기
다른 글
-
[Spring Web MVC] 응답 정보 다루기
[Spring Web MVC] 응답 정보 다루기
2022.08.17 -
[Spring Web MVC] 요청 정보 다루기
[Spring Web MVC] 요청 정보 다루기
2022.08.17 -
[Spring Web MVC] Adaptor
[Spring Web MVC] Adaptor
2022.08.16 -
[Spring Web MVC] Model
[Spring Web MVC] Model
2022.08.15