[SpringMVC] 스프링 MVC 구조

szlee·2024년 3월 25일
1

SpringMVC

목록 보기
6/6

< 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 > 강의를 보고 이해한 내용을 바탕으로 합니다.





스프링 MVC 구조

DispatcherServlet

org.springframework.web.servlet.DispatcherServlet
스프링 MVC도 프론트 컨트롤러 패턴으로 구현되어 있고, 이게 바로 디스패처 서블릿(DispatcherSerlvet)이다. => 스프링 MVC의 핵심.

DispatcherServlet 서블릿 등록

  • DispatcherServlet도 부모 클래스에서 HttpServlet을 상속받아서 사용하고, 서블릿으로 동작한다.
    • DispatcherServlet -> FrameworkServlet -> HttpServletBean -> HttpServlet
  • 스프링 부트는 DispatcherServlet을 서블릿으로 자동으로 등록하면서 모든 경로(urlPatterns="/")에 대해서 매핑한다.

요청 흐름

  • 서블릿이 호출되면 HttpServlet이 제공하는 service()가 호출된다.
  • 스프링 MVC는 DispatcherServlet의 부모인 FrameworkServlet에서 service()를 오버라이드 해두었다.
  • FrameworkServlet.service()를 시작으로 여러 메서드가 호출되면서 DispatcherServlet.doDispatch()가 호출된다.

DispatcherServlet 의 핵심인 doDispatch() 코드 분석

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
     HttpServletRequest processedRequest = request;
     HandlerExecutionChain mappedHandler = null;
     ModelAndView mv = null;
     
	// 1. 핸들러 조회
	mappedHandler = getHandler(processedRequest); 
    if (mappedHandler == null) {
         noHandlerFound(processedRequest, response);
		return; 
    }
    
	//2.핸들러 어댑터 조회-핸들러를 처리할 수 있는 어댑터
	HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

	// 3. 핸들러 어댑터 실행 -> 4. 핸들러 어댑터를 통해 핸들러 실행 -> 5. ModelAndView 반환 
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}

 private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView
 mv, Exception exception) throws Exception {
 	// 뷰 렌더링 호출
	render(mv, request, response);
 }
 
 protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
     View view;
	 String viewName = mv.getViewName(); 
     
     //6. 뷰 리졸버를 통해서 뷰 찾기, 7.View 반환
     view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
     
	 // 8. 뷰 렌더링
     view.render(mv.getModelInternal(), request, response);
 }

작동 순서

  1. 핸들러 조회: 핸들러 매핑을 통해 요청 URL에 매핑된 핸들러(컨트롤러)를 조회한다.
  2. 핸들러 어댑터 조회: 핸들러를 실행할 수 있는 핸들러 어댑터를 조회한다.
  3. 핸들러 어댑터 실행: 핸들러 어댑터를 실행한다.
  4. 핸들러 실행: 핸들러 어댑터가 실제 핸들러를 실행한다.
  5. ModelAndView 반환: 핸들러 어댑터는 핸들러가 반환하는 정보를 ModelAndView로 변환해서 반환한다.
  6. viewResolver 호출: 뷰 리졸버를 찾고 실행한다.
    JSP의 경우: InternalResourceViewResolver 가 자동 등록되고, 사용된다.
  7. View 반환: 뷰 리졸버는 뷰의 논리 이름을 물리 이름으로 바꾸고, 렌더링 역할을 담당하는 뷰 객체를 반환한다.
    JSP의 경우 InternalResourceView(JstlView) 를 반환하는데, 내부에 forward() 로직이 있다.
  8. 뷰 렌더링: 뷰를 통해서 뷰를 렌더링 한다.

스프링 MVC의 큰 강점은 DispatcherServlet 코드의 변경 없이, 원하는 기능을 변경하거나 확장할 수 있다는 점이다.

주요 인터페이스 목록

  • 핸들러 매핑: org.springframework.web.servlet.HandlerMapping
  • 핸들러 어댑터: org.springframework.web.servlet.HandlerAdapter
  • 뷰 리졸버: org.springframework.web.servlet.ViewResolver
  • 뷰: org.springframework.web.servlet.View



@RequestMapping

가장 우선순위가 높은 핸들러 매핑과 핸들러 어댑터는 RequestMappingHandlerMapping ,
RequestMappingHandlerAdapter 이다.

@RequestMapping 의 앞글자를 따서 만든 이름인데, 이것이 바로 지금 스프링에서 주로 사용하는 애노테이션 기반의 컨트롤러를 지원하는 매핑과 어댑터이다. (실무에서는 99.9% 이 방식의 컨트롤러를 사용한다.)

 @Controller
 public class SpringMemberFormControllerV1 {
    
    @RequestMapping("/springmvc/v1/members/new-form")
    public ModelAndView process() {
        return new ModelAndView("new-form");
    }
}
  • @Controller : 스프링이 자동으로 스프링 빈으로 등록한다. (내부에 @Component 애노테이션이 있어서 컴포넌트 스캔의 대상이 됨)
    스프링 MVC에서 애노테이션 기반 컨트롤러로 인식한다.
  • @RequestMapping : 요청 정보를 매핑한다. 해당 URL이 호출되면 이 메서드가 호출된다. 애노테이션을 기반으로 동작하기 때문에, 메서드의 이름은 임의로 지으면 된다.
  • ModelAndView : 모델과 뷰 정보를 담아서 반환하면 된다.

RequestMappingHandlerMapping 은 스프링 빈 중에서@RequestMapping 또는 @Controller 가 클래스 레벨에 붙어 있는 경우에 매핑 정보로 인식한다.

해당 애노테이션을 통해 클래스를 통합 및 조합이 가능하다.
조합 결과

  • 클래스 레벨 @RequestMapping("/springmvc/v2/members")
    • 메서드 레벨 @RequestMapping("/new-form") -> /springmvc/v2/members/new-form
    • 메서드 레벨 @RequestMapping("/save") -> /springmvc/v2/members/save
    • 메서드 레벨@RequestMapping -> /springmvc/v2/members

@RequestMapping -> @GetMapping, @PostMapping

 @Controller
 @RequestMapping("/springmvc/v3/members")
 public class SpringMemberControllerV3 {
     private MemberRepository memberRepository = MemberRepository.getInstance();
     
     @GetMapping("/new-form")
     public String newForm() {
         return "new-form";
     }
     
     @PostMapping("/save")
     public String save(
             @RequestParam("username") String username,
             @RequestParam("age") int age,
             Model model) {
         Member member = new Member(username, age);
         memberRepository.save(member);
         model.addAttribute("member", member);
         return "save-result";
     }
     
     @GetMapping
     public String members(Model model) {
         List<Member> members = memberRepository.findAll();
         model.addAttribute("members", members);
         return "members";
     } 
}

ViewName 직접 반환
뷰의 논리 이름을 반환할 수 있다.

@RequestParam 사용
스프링은 HTTP 요청 파라미터를 @RequestParam 으로 받을 수 있다.
@RequestParam("username")request.getParameter("username") 와 거의 같은 코드라 생각하면 된다.
물론 GET 쿼리 파라미터, POST Form 방식을 모두 지원한다.

@RequestMapping -> @GetMapping, @PostMapping
@RequestMapping 은 URL만 매칭하는 것이 아니라, HTTP Method도 함께 구분할 수 있다.
예를 들어서 URL이 /new-form 이고, HTTP Method가 GET인 경우를 모두 만족하는 매핑을 하려면 다음과 같이 처리하면 된다.

@RequestMapping(value = "/new-form", method = RequestMethod.GET) 

이것을 @GetMapping , @PostMapping 으로 더 편리하게 사용할 수 있다. (Get, Post, Put, Delete, Patch 모두 애노테이션이 준비되어 있다.)

profile
🌱

0개의 댓글