mvc

프론트 컨트롤러 패턴

img.png
img_1.png

프론트 컨트롤러 패턴 특징

  • 프론트 컨트롤러 서블릿 하나로 클라이언트의 요청을 받는다.

  • 프론트 컨트롤러가 요청에 맞는 컨트롤러를 찾아서 호출한다.

  • 입구를 하나로 만들어서 공통 로직을 처리한다.

  • 프론트 컨트롤러를 제외한 나머지 컨트롤러는 서블릿을 사용하지 않아도 된다.

img_7.png

스프링 웹 MVC의 핵심도 FrontController이다. 스프링 웹 MVC의 DispatchServlet프론트 컨트롤러 패턴으로 구현되어 있다.


프론트 컨트롤러 - V1

img_2.png
  • 컨트롤러 인터페이스

각 컨트롤러에서 이 인터페이스를 구현하고 프론트 컨트롤러는 이 인터페이스를 호출하면 일관성을 가져갈 수 있다.

  • 회원 등록 컨트롤러

  • 회원 저장 컨트롤러

  • 회원 목록 컨트롤러

각 컨트롤러의 내부 로직은 기존 서블릿과 거의 똑같다.

  • 프론트 컨트롤러

  • controllerMap

    • key : 매핑 URL

    • value : 호출될 컨트롤러

  • service()

    • requestURI를 조회해서 호출할 컨트롤러를 controllerMap에서 찾고 없다면 404 반환

    • 찾은 컨트롤러의 process(request, response)를 실행한다.

프론트 컨트롤러를 제외하고 모든 컨트롤러에서 뷰로 이동하는 부분에 중복이 존재한다. 이 부분을 분리하기 위해 별도로 뷰를 처리하는 객체가 필요하다.


프론트 컨트롤러 - V2

img_3.png
  • MyView

  • 컨트롤러 인터페이스(뷰를 반환)

  • 회원 등록 폼 컨트롤러

지금처럼 각 컨트롤러는 이제 단순히 MyView 객체에 뷰 이름만 넣어서 반환하면 된다.

  • 회원 저장 컨트롤러

  • 회원 목록 컨트롤러

  • 프론트 컨트롤러

프론트 컨트롤러와 MyView 객체로 각 컨트롤러는 MyView 객체를 생성해서 반환하면 MyView의 render()로 일관되게 처리할 수 있게 되었다.


프론트 컨트롤러 - V3

컨트롤러 입장에서 HttpServletRequest, HttpServletResponse는 꼭 필요하지 않다.

요청 파라미터 정보를 Map으로 대신 넘기면 컨트롤러가 서블릿 기술을 몰라도 동작할 수 있다. 그리고 request 객체를 Model로 사용하는 대신에 별도의 객체를 만들어서 반환하면 된다. 컨트롤러를 서블릿 기술을 전혀 사용하지 않도록 해보자. 이런 구조는 구현 코드도 단순해지고 테스트 코드 작성도 쉬워진다.

또한 뷰 이름이 중복이 있으므로 컨트롤러는 뷰의 논리 이름을 반환하고, 물리 위치 이름은 프론트 컨트롤러에서 처리하도록 할 수 있다.

img_4.png
  • ModelView

    • 지금까지 컨트롤러에서 서블릿에 종속적인 HtttpServletRequest를 사용해 모델에 데이터를 저장하고 뷰를 전달했다.

    • 서블릿 종속성을 제거하기 위해 모델을 직접 만들고 뷰 이름까지 전달하는 객체가 필요하다.

    • 이후 버전에서는 HtttpServletRequest를 사용할 수 없기 때문에 모델이 별도로 필요하다.

  • 컨트롤러 인터페이스

서블릿 기술을 전혀 사용하지 않는다.

  • 회원 등록 폼 컨트롤러

논리 이름만 지정하면 된다.

  • 회원 저장 컨트롤러

  • 회원 목록 컨트롤러

  • MyView 추가

  • 프론트 컨트롤러

  • createParamMap()

    • HttpServletRequest에서 파라미터 정보를 꺼내서 Map으로 변환하고 해당 Map을 컨트롤러에 전달하면서 호출한다.

  • viewResolver()

    • 컨트롤러가 반환한 논리 뷰 이름을 실제 물리 뷰 경로로 변경한다. 그리고 실제 물리 경로가 있는 MyView객체를 반환한다.

  • view.render()

    • 뷰 객체를 통해서 HTML 화면을 렌더링한다.

    • 이 때 모델 정보도 함께 받는다.

    • JSP는 request.getAttribute()로 데이터를 조회하기 때문에, 모델의 데이터를 꺼내서 request.setAttribute()로 담아둔다.

    • JSP로 포워드해서 JSP를 렌더링한다.


컨트롤러 - V4

V3는 서블릿 종속성도 제거하고 뷰 경로의 중복도 제거하는 등 잘 설계됐다. 그러나 항상 ModelView 객체를 생성하고 반환해야 하는 부분이 번거롭다.

img_5.png
  • 컨트롤러 인터페이스

  • 회원 등록 폼 컨트롤러

뷰의 논리 이름만 반환한다.

  • 회원 저장 컨트롤러

model.put("member", member) : 모델이 파라미터로 전달되기 때문에 모델을 직접 생성하지 않아도 된다.

  • 회원 목록 컨트롤러

  • 프론트 컨트롤러

이전 버전과 거의 같고 다른 것은 프론트 컨트롤러에서 모델 객체를 생성해서 컨트롤러에게 넘겨주면 로직 후에 모델에는 데이터가 담긴 상태가 된다.


컨트롤러 - V5

어댑터 패턴

지금까지 개발한 컨트롤러는 해당 인터페이스만 받을 수 있기 때문에 다른 버전끼리 호환이 불가능하다. 어댑터 패턴을 사용해서 프론트 컨트롤러가 다양한 방식의 컨트롤러를 처리할 수 있다.

img_6.png
  • 핸들러 어댑터

    • 중간에서 어댑터 역할을 한다. 덕분에 다양한 종류의 컨트롤러를 호출할 수 있게 된다.

  • 핸들러

    • 컨트롤러의 이름을 더 넓은 범위인 핸들러로 변경한다.

    • 어댑터가 있기 때문에 컨트롤러의 개념 뿐만 아니라 해당하는 종류의 어댑터만 있으면 다 처리할 수 있기 때문이다.

어댑터용 인터페이스

  • boolean supports()

    • handler == 컨트롤러

    • 어댑터가 해당 컨트롤러를 처리할 수 있는지 판단한다.

  • ModelView handle()

    • 실제 컨트롤러를 호출하고 그 결과로 ModelView를 반환해야 한다.

    • 실제 컨트롤러가 ModelView를 반환하지 못하면 어댑터가 직접 생성해서라도 반환해야 한다.

    • 이전에는 프론트 컨트롤러가 실제 컨트롤러를 호출했지만 이제는 이 어댑터를 통해서 호출된다.

V3 지원 어댑터

  • boolean supports()

    • ControllerV3를 처리할 수 있는지 판단한다.

  • ModelView handle()

    • handler를 V3로 변환한 다음 V3 형식에 맞도록 호출한다.

    • ControllerV3는 ModelView를 반환한다.

V4 지원 어댑터

프론트 컨트롤러

어댑터가 호출하는 ControllerV4는 뷰 이름을 반환하지만 어댑터는 ModelView를 만들어서 형식을 맞춰서 반환한다. 이것이 어댑터가 필요한 이유다.

Last updated