[ 스프링 웹 MVC ] - 스프링 MVC - 구조 이해

.·2021년 5월 27일
0

스프링 MVC

목록 보기
5/6

스프링 MVC 전체 구조

  • 스프링 MVC의 프론트 컨트롤러가 디스패처 서블릿이다.
  • 스프링 부트는 Dispacher Servlet를 서블릿으로 자동으로 등록하면서 모든 경로에 대하여 매핑한다.
  • 더 자세한 경로가 우선순위가 높기 때문에 기존에 등록한 서블릿도 작동함.

요청 흐름

  • 서블릿이 호출되면 service()가 호출 됨.
  • 오버라이딩을 통해 여러 메서드가 호출되다가 doDispatch가 호출 됨.

doDispatch 동작 순서

1. 핸들러 조회

핸들러 매핑을 통해 요청 URL에 매핑된 핸들러(컨트롤러)를 조회한다

2. 핸들러 어댑터 조회

핸들러를 실행할 수 있는 핸들러 어댑터를 조회한다.

3. 핸들러 어댑터 실행

핸들러 어댑터를 실행한다.

4. 핸들러 실행

핸들러 어댑터가 실제 핸들러를 실행한다

5. ModleAndView 반환

핸들러 어댑터는 핸들러가 반환하는 정보를 ModelAndView로 변환해서
반환한다.

6. viewResolver 호출

뷰 리졸버를 찾고 실행한다.

  • JSP의 경우: InternalResourceViewResolver 가 자동 등록되고, 사용된다.

7. View 반환

뷰리졸버는 뷰의논리이름을 물리이름으로 바꾸고,렌더링역할을 담당하는 뷰객체를
반환한다.

  • JSP의 경우 InternalResourceView(JstlView) 를 반환하는데, 내부에 forward() 로직이 있다.

8. 뷰 렌더링

뷰를통해서 뷰를 렌더링한다.

핸들러 매핑과 핸들러 어댑터

특정 컨트롤러가 호출되려면 다음 2가지 과정을 거친다.

핸들러 매핑

  • 핸들러 매핑을 통해 특정 핸들러를 찾는다.
  • HandlerMapping 을 순서대로 실행해서, 해당하는 핸들러를 찾는다.

스프링 부트가 자동으로 등록하는 핸들러 매핑

핸들러 어댑터

  • 핸들러 매핑을 통해 찾은 핸들러를 실행할 수 있는 핸들러 어댑터가 필요하다.
  • HandlerAdapter 의 supports() 를 순서대로 호출한다. - 조회
  • 디스패처 서블릿이 조회한 어댑터를 실행하면서 핸들러 정보도 함께 넘겨준다.
  • 핸들러를 내부에서 실행하고 그 결과를 반환 한다. - 실행

스프링 부트가 자동으로 등록하는 핸들러 어댑터


뷰 리졸버

스프링 부트는 InternalResourceViewResolver 라는 뷰 리졸버를 자동으로 등록하는데, 이때 application.properties 에 등록한 spring.mvc.view.prefix , spring.mvc.view.suffix 설정 정보를 사용해서 등록한다.

동작 방식

  1. 핸들러 어댑터 호출
    핸들러 어댑터를 통해 new-form이라는 논리 뷰 이름을 획득

  2. ViewResolver 호출
    new-form 이라는 뷰 이름으로 viewResolver를 순서대로 호출한다.

  3. InternalResourceViewResolver
    이 뷰 리졸버는 InternalResourceView 를 반환한다.

  4. 뷰 - InternalResourceView
    InternalResourceView 는 JSP처럼 포워드 forward() 를 호출해서 처리할 수 있는 경우에 사용한다.

  5. view.render()
    view.render() 가 호출되고 InternalResourceView 는 forward() 를 사용해서 JSP를 실행한다.

스프링 MVC

@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 : 스프링이 자동으로 스프링 빈에 등록한다. 애노테이션 기반 컨트롤러로 인식
  • @RequestMapping : 요청 정보를 매핑한다. 해당 URL이 호출되면 이 메서드가 호출된다. 애노테이션을 기반으로 동작하기 때문에, 메서드의 이름은 임의로 지으면 된다.
  • ModelAndView : 모델과 뷰 정보를 담어서 반환
      @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);
 System.out.println("member = " + member);
 
 memberRepository.save(member);
 ModelAndView mv = new ModelAndView("save-result");
 mv.addObject("member", member);
 
 return mv;
 
} }
 
  • 스프링이 제공하는 ModelAndView 를 통해 Model 데이터를 추가할 때는 addObject() 를 사용하면 된다. 이 데이터는 이후 뷰를 렌더링 할 때 사용된다.

RequestMapping 을 잘 보면 클래스 단위가 아니라 메서드 단위에 적용된 것을 확인할 수 있다. 따라서 컨트롤러 클래스를 유연하게 하나로 통합할 수 있다.

스프링 MVC - 컨트롤러 통합

@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("/save")
  public ModelAndView save(HttpServletRequest request, HttpServletResponse response) {
  
  String username = request.getParameter("username");
  int age = Integer.parseInt(request.getParameter("age"));
  
  //생략
  }
  
  @RequestMapping
  public ModelAndView members() {
   // 생략
}

스프링 MVC - 실용적인 방식

@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) {         
            //생략
            }
            
     @GetMapping
      public String members(Model model) {
          List<Member> members = memberRepository.findAll();
          model.addAttribute("members", members);
          return "members";
}
  • Model을 파라미터로 직접 받음
  • ViewName 직접 반환
  • @RequestParam 사용

@RequestParam("username") 은 request.getParameter("username") 와 거의 같은 코드라 생각하면 된다.

  • RequestMapping -> @GetMapping, @PostMapping
profile
지금부터 공부하고 개발한것들을 꾸준하게 기록하자.

0개의 댓글