Go, Vantage point
가까운 곳을 걷지 않고 서는 먼 곳을 갈 수 없다.
Github | https://github.com/overnew/
Blog | https://everenew.tistory.com/
티스토리 뷰
* 김영한님의 스프링 MVC 1편 강좌를 수강하며 정리한 글입니다. *
이전 글에서는 MVC 패턴을 공부해 보았다.
Front Controller 패턴
이번에는 공통된 로직을 먼저 수행하는 프런트 컨트롤러를 도입해보자.
프런트 컨트롤러도 하나의 서블릿으로 클라이언트의 모든 요청을 받고, 공통 로직 수행 후에 요청에 맞는 컨트롤러로 호출을 해준다.
따라서 더이상 모든 controller가 서블릿 객체일 필요가 없어진다.
스프링 MVC도 프런트 컨트롤러 패턴이 구현되어 있으므로 자세히 알아보자.
1. 도입
프런트 컨트롤러 패턴에서는 일단 프런트 컨트롤러가 요청에 대한 URL 매핑 정보를 조회하여, 해당 컨트롤러를 호출해야 한다.
프런트 컨트롤러의 하위 컨트롤러의 접근 시, 먼저 프런트 컨트롤러가 호출되게 하기 위해 필요한 것이 @WebServlet의 urlPatterns이다.
urlPatterns = "/컨트롤러의 패키지/* " 처럼 ' /* '을 적용하면 하위의 url 접근 시, 프런트 컨트롤러가 받아들인다.
이제 프런트 컨트롤러는 클라이언트가 접근하려는 세부 URL을 자신의 하위 컨트롤러 URL과 매칭 시켜 컨트롤러를 호출하면 된다.
2. 뷰 렌더링의 중복 제거
이전에는 모든 컨트롤러에서 dispatcher.forward() 를 통해 jsp로 이동하는 중복 작업이 있었다.
이제 컨트롤러는 프런트 컨트롤러에게 View 객체를 반환하고, 반환받은 객체의 렌더링을 프런트가 담당하게 만들어보자.
MyView 객체에 jsp의 위치를 생성자에 넘겨 생성하면, 이제 render()호출로 jsp로 forwarding 이 일어난다
이제 각각의 controller의 process() 메서드는 forwarding 작업을 하지 않고 MyView 객체를 생성하고 프런트 컨트롤러로 반환한다.
그러면, 프런트 컨트롤러가 MyView 객체의 render()를 실행해 주면 된다.
public interface ControllerV2 {
MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
}
하지만 View 객체가 만들어지면 렌더링이 분리되기 때문에, Controller의 process(request, response) 추상 메서드는 request와 response를 파라미터로 받아올 필요가 없어진다.
3. 모델 도입
요청 파라미터를 request가 아닌 Model 객체를 직접 설계하여 받아오도록 작성해보자. 이러면 각 컨트롤러는 request, response를 받아오기 위한 Servlet 상속에서 벗어날 수 있다.
각 컨트롤러는 프런트 컨트롤러가 전달해준 Map 자료구조를 받아 값을 꺼내 쓴 후, Model을 생서해 반환한다.
public class MemberListControllerV3 implements ControllerV3 {
private MemberRepository memberRepository = MemberRepository.getInstance();
// 파라미터를 Map 자료구조로 받아서 Map 필드를 가지는 Model에 넣어준다.
@Override
public ModelView process(Map<String, String> paramMap) {
List<Member> members = memberRepository.findAll();
ModelView mv = new ModelView("members");
mv.getModel().put("members", members);
return mv;
}
}
ModelView객체는 Map<String, Object> 필드를 가지므로, key, value 값으로 원하는 객체를 저장할 수 있다.
추가적으로 프런트 컨트롤러가 viewResolver()를 호출하면 실제 경로를 생성하여 View객체를 반환해준다.
프런트 컨트롤러의 일이 많아지고 있지만, 각 컨트롤러는 순수한 자바 코드로 구성할 수 있었다.
4. Model 생성 간편화
개발자 입장에서는 아키텍처도 중요하지만, 사용성도 편리해야 한다.
모델과 뷰의 도입으로 중복성과 servlet 종속성은 제거되었지만, 개발자가 Front Controller에서 해야 하는 일이 너무 많다.
이제는 controller가 Model 객체를 직접 생성하지 않고, View의 이름만 반환하게 변경하자.
이를 위해서 Front Controller가 model을 만들어 controller에 매개변수로 전달한다.
더 이상 Model은 데이터 전달 그 이상의 역할은 하지 않기 때문에, Map <> 자료 구조로 대체할 수 있다.
public interface ControllerV4 {
// 각 controller의 process는 이제 model을 매개변수로 받아서 데이터를 담고, view 이름만 반환
String process(Map<String, String> paramMap, Map<String, Object> model);
}
public class MemberListControllerV4 implements ControllerV4 {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public String process(Map<String, String> paramMap, Map<String, Object> model) {
List<Member> members = memberRepository.findAll();
model.put("members", members); //전달 받은 model에 데이터 담고
return "members"; //뷰의 이름만 반환
}
}
5. 어댑터 패턴 적용
지금 까지는 버전마다 다른 controller interface를 구현해 컨트롤러에 적용하였다.
따라서 해당 controller 버전을 구현하지 않으면 프런트 컨트롤러에서는 사용할 수 없었다.
이번에는 어떤 버전의 controller를 구현하든 프런트 컨트롤러에서 동작하도록 만들어 보자.
어댑터 패턴은 서로 호환되지 않는 타입을 변환시켜 사용할 수 있도록 만들어 준다.
이제 프런트 컨트롤러는 컨트롤러를 핸들러 어댑터를 통해 호출하게 된다.
이제 컨트롤러는 더 넓은 의미인 핸들러로 명칭을 바꾼다.
public interface MyHandlerAdapter {
//해당 handler의 지원 여부 반환
boolean supports(Object handler);
ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException;
}
MyHandlerAdapter Interface를 각 Controller 버전마다 구현하면,
이제 클라이언트가 요청한 url에 맞는 Handler(controller)를 꺼내서 해당 handler에 맞는 Adapter를 MyHandlerAdapter.supports()를 통해 찾는다.
해당 버전의 어댑터를 찾았다면 handle() 메서드를 통해서 프런트 컨트롤러가 ModelView를 반환받는다.
3번째 버전에서는 Controller는 ModelView를 생성해 반환하고, 4번째 버전은 ModelView를 프런트 컨트롤러가 생성하였다.
이번에는 프런트 컨트롤러가 어댑터로부터 해당 Controller의 ModelView를 반환받는다.
따라서 어떤 Controller로 구현을 하더라도, 해당 어댑터가 ModelView를 반환하면 프런트 컨트롤러를 정상적으로 실행을 한다. 프런트 컨트롤러 변경이 없이도 어댑터 핸들러의 주입으로 확장성이 가능해, OCP를 지키게 된다.
이러한 어댑터 기능을 어노테이션으로 구현해 제공하는 것이 스프링 MVC이다.
'개발 > Spring' 카테고리의 다른 글
[Spring] 17. RequestMapping (0) | 2022.07.14 |
---|---|
[Spring] 16. Spring MVC (0) | 2022.07.11 |
[Spring] 14. 템플릿 엔진과 MVC 패턴 (0) | 2022.07.08 |
[Spring] 13. Servlet, 서블릿 (0) | 2022.07.06 |
[Spring] 12. 웹 어플리케이션 서버와 쓰레드 풀 (0) | 2022.07.04 |