MVC : Model, View, Controller를 의미하며 관심사(기능)를 분리하는 디자인 패턴

  • Model : 요청받은 파라미터로 비즈니스 로직을 수행하여 결과 데이터를 얻어낸다
  • View : 결과 데이터를 화면에 그린다
  • Controller : HTTP 요청을 받아 파라미터를 검증하고 비즈니스로직을 실행시킨다. 그리고 그 결과를 뷰로 전달한다

스프링 MVC 구조

① 클라이언트로부터 요청이 들어옴, DispatcherServlet이 그 요청을 받음
HandlerMapping은 요청에 부합한 Controller를 탐색
HandlerAdapter는 HandlerMapping에 의해 결정된 Controller에서 요청에 부합한 메서드를 찾아 실행 요청
④ 부합된 Controller 실행하여 비즈니스 로직 실행
⑤ 실행 결과인 Model로 부터 반환된 view이름을 DispatcherServlet에게 전달 (Model or ModelAndView 객체)
⑥ 받은 view이름을 이용하여 ViewResolver는 그에 맞는 view를 탐색
⑦, ⑧ 탐색된 View로 클라이언트에게 응답

1. DispatcherServlet (링크)

org.springframework.web.servlet.DispatcherServlet

  • 프론트 컨트롤러
  • DispatcherServletFrameworkServletHttpServletBeanHttpServlet 상속
  • HttpServlet.service()가 호출되어 DispatcherServlet.doDispatch()가 호출된다
  • 클라이언트의 모든 요청을 받은 후 이를 처리할 핸들러에게 넘기고 핸들러가 처리한 결과를 받아 사용자에게 응답 결과를 보여줌

2. HandlerMapping

org.springframework.web.servlet.HandlerMapping

  • 어떤 핸들러가 요청을 처리할지에 대한 정보를 알고 있음
public interface HandlerMapping {
	@Nullable
	HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

3. HandlerAdapter

org.springframework.web.servlet.HandlerAdapter

  • 실제 핸들러를 실행하는 역할
  • 선택된 핸들러를 실행하는 방법과 응답을 ModelAndView로 변화하는 방법에 대해 알고 있음
public interface HandlerAdapter {
	boolean supports(Object handler);

	@Nullable
	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}

4. ModelAndView

org.springframework.web.servlet.ModelAndView

  • Controller의 처리 결과를 보여줄 view와 view에서 사용할 값(데이터)을 전달하는 클래스(데이터와 뷰의 이름을 전달)
    cf) Model : 데이터만 전달해주는 객체
public class ModelAndView {
	@Nullable
	private Object view;

	@Nullable
	private ModelMap model;

	@Nullable
	private HttpStatus status;

	public ModelAndView() {}

	public ModelAndView(String viewName) {
		this.view = viewName;
	}

	public ModelAndView(View view) {
		this.view = view;
	}

	public ModelAndView(String viewName, @Nullable Map<String, ?> model) {
		this.view = viewName;
		if (model != null) {
			getModelMap().addAllAttributes(model);
		}
	}

	public ModelAndView(View view, @Nullable Map<String, ?> model) {
		this.view = view;
		if (model != null) {
			getModelMap().addAllAttributes(model);
		}
	}

	public ModelAndView(String viewName, HttpStatus status) {
		this.view = viewName;
		this.status = status;
	}

5. ViewResolver

org.springframework.web.servlet.ViewResolver

  • 컨트롤러가 리턴한 뷰 이름을 참고해서 적절한 뷰 오브젝트를 찾아줌
public interface ViewResolver {
	View resolveViewName(String viewName, Locale locale) throws Exception;
}

MVC 구현

1. Configuration객체

  • @Configuration 어노테이션 사용 : spring 설정클래스 명시
  • @EnableWebMvc : mvc관련 어노테이션을 사용하기 위해 사용
    = ,
  • @ComponentScan : 명시한 패키지에서 @Controller, @Service, @Repository, @Component, @Configuration가 붙은 클래스를 자동으로 스프링 컨테이너에 등록
    = <context:component-scan base-package=”package.mvc.controller”>
@Configuration 
@EnableWebMvc
@ComponentScan("package.mvc.controller")
public class WebConfiguration extends WebMvcConfigurerAdapter {

    // @Service를 사용하지 않고 Bean을 직접등록함
    @Bean
    public MemberService memberService() {
        return new MemberService(memberRepository());
    }

     // @Repository를 사용하지 않고 Bean을 직접등록함
    @Bean
    public MemberRepository memberRepository() {
        return new MemberService();
    }
    
    
    // addResourceHandler처럼 들어오는 URL은 addResourceLocations에서 찾도록 설정
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/css/**").addResourceLocations("/css/").setCachePeriod(31556926);
        registry.addResourceHandler("/img/**").addResourceLocations("/img/").setCachePeriod(31556926);
        registry.addResourceHandler("/js/**").addResourceLocations("/js/").setCachePeriod(31556926);
    }
    
    // ViewResolver 빈등록, 아래 적어놓은 View설정을 config를 통해 설정하는 방법
    @Bean
    public InternalResourceViewResolver getInternalResourceViewResolver() {		
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");		// 뷰이름 앞에 붙일 값
        resolver.setSuffix(".jsp"); 				// 뷰이름 뒤에 붙일 값
        return resolver;
    }
}

-> WebMvcConfigurerAdapter : @EnableWebMvc 를 이용하면 기본적인 설정이 모두 자동으로 되지만, 기본 설정 이외의 설정이 필요할 경우 해당 클래스를 상속 받은 후, 메소드를 오버라이딩 하여 구현

※ 스프링 5.0, 스프링부트 2.0 부터는 WebMvcConfigurer를 사용함

2. Controller객체(=Handler)

  • @Controller 어노테이션 사용 : Configuration 클래스에 @EnableWebMvc 어노테이션 추가
  • @RequestMapping : Controller내의 메서드에 요청을 맵핑할 패턴을 명시
  • Model 객체를 파라미터로 사용하거나 ModelAndView객체를 리턴타입으로 사용
@Controller
Class public MemberController {

    @Autowired
    MemberService memberService;
    

    @RequestMapping(value="/memJoin" method="RequestMethod.POST")
    public String memJoin(Model model, 
            @RequestParam("memId") String memId, @RequestParam String memPwd, 
            @ModelAttribute Member member) {
    
//      String memId = request.getParameter("memId");
//      String memPwd = request.getParameter("memPwd");
        
        memberService.memRegister(memId, memPwd);
        
        model.addAttribute("memId", memId);
        model.addAttribute("memPwd", memPwd);
        
        return "memJoinOk";
    }
    
    @RequestMapping("/memLogin")
    public ModelAndView memLogin() {
        ModelAndView mav = new ModelAndView();
        Member member = memberService.memSearch();
        
        mav.setViewName("memLoginOk");
        mav.addObject("member", member);
        
        return mav;
    }
}

★ 파라미터로 사용되는 어노테이션

1) @RequestParam

  • HTTP 요청 파라미터를 변수로 받음 (HttpServletRequest request의 getParameter 대신 사용 가능)
    ex) @RequestParam("memId") String memId
    ex) @RequestParam(value="memId", defaultValue = "user", required = false) String memId
  • 파라미터와 파라미터를 받는 변수명이 같으면 파라미터 생략 가능 (@RequestParam String memPwd)
  • @ModelAttribute 처럼 아예 생략 가능 (String memPwd)

2) @PathVariable

  • @RequestMapping 패턴에 있는 중괄호에 명시된 값을 변수로 받음
    ex) @RequestMapping("/user/{id}"} 사용시 @PathVariable("id") String id

3) @ModelAttribute

  • HTTP 요청 파라미터를 객체로 받음 (DTO/VO), 생략 가능
  • @ModelAttribute("member2") Member membermodel.addAttribute("member2", member)를 의미한다.
  • 다만 생략한다면 @ModelAttribute Member member = Member member 이 되는데 이 경우에는 클래스명의 카멜표기법이 key값이 되어 model.addAttribute("member", member)가 된다

4) @RequestHeader

  • HTTP 요청헤더 정보를 가져옴
    ex) @RequestHeader("host") String host

5) @RequestBody

  • HTTP 요청바디 정보를 가져옴, JSON방식의 정보를 가져올때 사용
    ex) @RequestBody String host

6) @CookieValue

  • HTTP 요청 쿠키정보를 가져옴
    ex) @CookieValue String cookie

3. Service객체

  • @Service 어노테이션 사용
@Service
Class public MemberService {

    @Autowired
    MemberDao memberDao;
    
    public void memRegister(String memId, String memPwd) {
        memberDao.memInsert(memId, memPwd);
    }
}

4. Repository객체(=DAO)

  • @Repository 어노테이션 사용
@Repository
Class public MemberDao {

    public void memInsert(String memId, String memPwd) {
        .....
    }
}

5. View객체

  • 스프링 설정파일에 ViewResolver를 빈으로 등록
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <beans:property name="prefix" value="/WEB-INF/views/" />	<!-- 뷰이름 앞에 붙일 값 -->
    <beans:property name="suffix" value=".jsp" />                	<!-- 뷰이름 뒤에 붙일 값 -->
</beans:bean>