MVC 프레임 워크

Single Ko·2023년 5월 31일
0

Spring 강의 정리

목록 보기
10/31

프론트 컨트롤러 패턴

  • 프론트 컨트롤러 서블릿 하나로 클라이언트의 요청을 받음
  • 프론트 컨트롤러가 요청에 맞는 컨트롤러를 찾아서 호출
  • 입구를 하나로 만듬, 공통 처리 가능.
  • 프론트 컨트롤러를 제외한 나머지 컨트롤러는 서블릿을 사용하지 않아도 된다.
  • Spring MVC의 FrontController 패턴으로 구현된 DispatcherServlet이 구현되어있음.

스프링 MVC 구조

DispatcherServlet의 구조

  • 스프링의 MVC도 프론트 컨트롤러 패턴으로 구현되어 있다.
  • DispatcherServlet 가 바로 스프링의 프론트 컨트롤러.
  • 스프링 MVC의 핵심이다.
  • DispatcherServlet도 부모 클래스에서 HttpServlet을 상속받아서 사용하고, 서블릿으로 동작.
  • 스프링 부트는 DispatcherServlet을 서블릿으로 자동 등록하면서 모든경로(urlPatterns="/")에 대해 매핑한다

요청 흐름

//doDispatch method 의 호출 
protected void doDispatch(HttpServletRequest request, 
						  HttpServletResponse response) throws Exception {


//1. 핸들러 조회 Handler, 
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) throw 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 호출 : view 리졸버를 찾고 실행.
  7. View 반환 : 뷰 리졸버는 뷰의 논리 이름을 물리 이름으로 바꾸고, 렌더링 역할을 담당하는 뷰 객체를 반환한다.
  8. 뷰 렌더링 : 뷰를 통해서 뷰를 렌더링 한다.

spring의 주요 인터페이스 : HandlerMapping , HandlerAdapter, ViewResolver, View

Spring은 오랜 기간 개발되어 왔다. 대부분의 개발자들이 필요하는 기능은 개발되어 있다. 따라서 내가 필요한 기능들이 웬만하면 다 구현되어있다. 필요한 핸들러 매핑과 핸들러 어댑터등은 대부분 구현되어있음.

스프링 부트가 자동 등록하는 핸들러 매핑과 핸들러 어댑터


실제로는 더 많지만, 중요한것 위주로 살펴보자.

HandlerMapping
0 = RequestMappingHandlerMapping - 애노테이션 기반의 컨트롤러에서 사용 (최근엔 거의 전부 이방식)
1 = BeanNameUrlHandlerMapping - 스프링 빈의 이름으로 핸들러를 찾는다.

HandlerAdapter
0 = RequestMappingHandlerAdapter : 애노테이션 기반의 컨트롤러에서 사용
1 = HttpRequestHandlerAdapter : HttpRequestHandler 처리
2 = SimpleControllerHandlerAdapter : Controller 인터페이스(애노테이션 x, 옛날에 사용) 처리

핸들러 매핑, 어댑터들을 순서대로 매칭을 찾고, 없으면 다음으로 넘어간다.

실제로는 현재 @RequestMapping의 앞글자를 딴 RequestMappingHandlerMapping 과 RequestMappingHandlerAdpater를 거의 99.9퍼 사용한다고 보면 된다.

뷰 리졸버

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

1 = BeanNameViewResolver : 빈 이름으로 뷰를 찾아서 반환한다.
2 = InternalResourceViewResolver : JSP를 처리할 수 있는 뷰를 반환한다.

참고
다른 뷰는 실제 뷰를 렌더링, JSP의 경우 forward() 를 통해서 해당 jSP로 이동(실행)해야 렌더링 된다. JSP를 제외한 나머지 뷰 템플릿들은 forward()과정 없이 바로 렌더링됨.
Thymeleaf 뷰 템플릿을 사용하면 ThymeleafViewResolver를 등록해야함. 최근에는 라이브러리만 추가하면 스프링 부트가 이런 작업도 모두 자동화 해준다.

스프링 MVC

스프링이 제공하는 컨트롤러는 애노테이션 기반으로 동작해서, 매우 유연하고 실용적. 과거에는 자바 언어에 애노테이션이 없기도 했고, 스프링도 처음부터 이런 유연한 컨트롤러를 제공한 것은 아니다. 그 이후 스프링 WEB이 평정해버렸고, 거의 다른 프레임워크를 찾기 힘들어짐.

@RequestMapping
스프링은 애노테이션을 활용한 매우 유연하고 실용적인 컨트롤러. 사용하기도 쉬우며 간편하다.


@Controller
public class MemberController () {

	@RequestMapping("/springmvc/members/new-form")
   public ModelAndView process() {
   	return new ModelAndView("new-form");
   }
   
   @RequestMapping("/springmvc/members/save")
   public ModelAndView save(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;
   }
}

@Controller

  • 이 애노테이션이 붙으면 스프링이 자동으로 스프링 빈으로 등록한다.(@Component 애노테이션이 있어서 컴포넌트 스캔의 대상이 됨)
  • 스프링 MVC에서 애노테이션 기반 컨트롤러로 인식

@RequestMapping

  • 요청 정보를 매핑한다. 해당 URL이 호출되면 이 메서드가 호출된다. 애노테이션을 기반으로 동작하기 때문에, 메서드의 이름은 임의로 지으면 된다.

ModelAndView

  • 모델과 뷰 정보를 담아서 반환하면 된다.

@Controller = @Component , @RequestMapping 이 붙어 있는것과 같은 것이라고 보면 된다.
훨씬 더 간편하고, 이름을 지어줘서 더 확실하게 인식 가능.
이 외에도 @RequestMapping을 붙이고, 따로 @Bean으로 등록도 가능하다.

클래스 단위에 @RequestMapping("/spring/v2") 이렇게 붙여주면 그 밑의 메서드에 경로를 추가해주는 역할. 중복되는 패스는 미리 클래스에 추가해놓을 수 있음
ex ) http://localhost:8080/spring/v2

실용적인 방식

사실 위에 MovdelView를 직접 보낼 필요도 없다. 이미 Spring은 이런 것을 다 처리 시켜준다.

 @Controller
 @RequestMapping("/springmvc/member")
 public class MemberController () {
 
 	@RequestMapping("/new-form")
    public String newForm() {
    	return "new-form";
    }
    
    @RequestMapping("/save")
    public String save(@RequestParam String username,
    				   @RequestParam int age,
                       Model model) {
    
   	Member member = new Member(username,age);
    memberRepository.save(member);
    
    model.addAttribute("member", member);
    return "save-result";
	}
 }

위의 방식과 차이가 보이는가? ModelAndView를 직접 보낼필요 없이 String type의 리턴으로 그저 ModelAndView의 물리적 경로의 이름을 넣으면 바로 연결된다.

거기다 HttpServletResponse와 HttpServletRequest를 받을 필요가 없이 바로 파라미터로 쿼리 파라미터를 받을 수 있다. 심지어 Type에 대한 처리도 전부 스프링이 처리해준다.

하지만 이 방식에서도 조금 고칠 수 있는 방법이 있다.
@RequestMapping의 HTTP Method를 정확하게 알기 힘들다. GET, POST뿐 아니라 HTTP API를 사용할시 DELETE , PUT, PATHCH등까지 사용할수 있는데 그것을 어떻게 하는가?

@RequestMapping(value = "/save", method = RequestMethod.GET)
이런 식으로 뒤에 method가 무슨 HTTP method인지 알려 줄 수있다. 하지만 이것은 너무 길고 적어야 할게 많다.

@GetMapping, @PostMapping 등을 사용하면 된다. 딱 봐도 무엇을 의미하는지 알 수 있다.
GetMapping은 RequsetMapping의 get버전이고, PostMapping은 post 버전이다.
우리는 이것을 사용하면 된다.

참고 : 본 글은 김영한님의 스프링 강의를 정리한 것이다.

profile
공부 정리 블로그

0개의 댓글