이번 포스팅에서는 FrontController 패턴을 소개하고자 한다.
Spring의 가장 핵심적인 Servlet인 DispatcherServlet은 FrontController 패턴으로 구성되어있다. FrontController 패턴이 무엇인지에 대해서 이해하는 시간을 갖고 이후 간단한 DispatcherServlet을 다섯 가지 스텝으로 나누어 구현해보는 시간을 갖고자 한다.
FrontController 패턴이란?
FrontContoller 패턴은 지휘자를 만드는 것이다.
오케스트라 연주를 보면 여러 연주자들이 각각의 연주를 진행한다. 이때 지휘자가 없다면 어떻게 될까?
통제를 받지 못함으로써 소리의 음량을 조절하지 못할뿐더러 악기의 순서가 뒤죽박죽 엉켜 결국엔 불협화음이 생기게 된다.
FrontController 패턴도 마찬가지다 여러 클라이언트들의 요청이 있고 해당하는 요청마다 컨트롤러가 있다고 가정하자!
요청을 보낼 때마다 특정한 작업을 하는 것이 필수적이라면 요청이 들어올 때마다 해당 작업을 실어서 보내야 하는데 이 결과 코드의 중복이 생기고 해당 작업을 누락한다면 최악의 경우에는 장애로 이어질 수도 있다.
이러한 까닭에 앞에서 언급한 지휘자가 필요한데 요청을 처리하는 지휘자는 해당 작업을 가지고 있고 해당 작업을 처리한 뒤에 요청에 맞는 컨트롤러에게 전달해주는 역할을 하게 된다. 이것이 바로 FrontController 패턴이다.
지금부터 다섯 가지 버전을 통해서 FrontController 패턴을 구현해보고자 한다.
다섯 가지 버전을 통해서 FrontController 사용법과 Spring의 DispatcherServlet의 동작 원리 또한 정리해보는 시간을 갖고자 한다.
FrontController - Version01
첫 번째 버전은 위와 같은 형태이다.
FrontController에서 HTTP 요청을 받고 매핑 정보를 확인한 다음 매핑 정보에 맞는 컨트롤러를 호출하여
컨트롤러는 뷰를 호출해주는 구조로 설계하고자 한다.
도메인을 설계하는 것이 중요한 게 아니기 때문에 단순히 회원정보를 저장하고 조회하는 구조로 설계해보려고 한다.
컨트롤러 인터페이스 생성
추상화를 위해 조회와 저장을 추상화한 ControllerVersion01 인터페이스를 생성하였다.
public interface ControllerVersion01 {
void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
}
폼, 저장, 조회 클래스 생성
컨트롤러 인터페이스를 구현한 폼, 저장, 조회 클래스를 생성하였다.
해당 클래스는 요청을 받아서 처리한 뒤 뷰를 랜더링 하는 기능을 담당한다.
Form (구현 코드)
public class MemberFormController implements ControllerVersion01 {
@Override
public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String viewPath = "/WEB-INF/views/new-form.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
}
Save (구현 코드)
public class MemberSaveController implements ControllerVersion01 {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age"));
Member member = new Member(username, age);
memberRepository.save(member);
request.setAttribute("member", member);
String viewPath = "/WEB-INF/views/save-result.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
}
List (구현코드)
public class MemberListController implements ControllerVersion01 {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Member> members = memberRepository.finalAll();
request.setAttribute("members", members);
String viewPath = "/WEB-INF/views/members.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
}
FrontController 생성
최초 요청을 받아서 처리하는 FrontController를 생성하였다.
해당 컨트롤러는 HttpServlet을 상속받고 있고 해당 요청의 url 정보에 따라서 컨트롤러를 매핑시켜서 건네주는 역할을 담당한다.
/front-controller/v1/ 에 대한 모든 요청이 들어올 수 있도록 " * " 설정
url에 맞는 컨트롤러로 전달할 수 있게 Map으로 설정
key | value |
front-controller/v1/members/new-form | MemberFormController |
front-controller/v1/members/save | MemberSaveController |
front-controller/v1/members | MemberListController |
service 메서드에서 요청 url을 가져와서 매핑된 컨트롤러로 전달
FrontController (구현 코드)
@WebServlet(name = "frontControllerServletV1", urlPatterns = "/front-controller/v1/*")
public class FrontControllerVersion01 extends HttpServlet {
private Map<String, ControllerVersion01> controllerMap = new HashMap<>();
public FrontControllerVersion01() {
controllerMap.put("/front-controller/v1/members/new-form", new MemberFormController());
controllerMap.put("/front-controller/v1/members/save", new MemberSaveController());
controllerMap.put("/front-controller/v1/members", new MemberListController());
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String requestURI = request.getRequestURI();
ControllerVersion01 controller = controllerMap.get(requestURI);
if (controller == null) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
controller.process(request, response);
}
}
클래스 다이어그램
Code Link
'CS > DesignPattern' 카테고리의 다른 글
(DesignPattern) FrontController - Version03 (Model 추가) (0) | 2021.05.04 |
---|---|
(DesignPattern) FrontController - Version02 (View 분리) (0) | 2021.05.02 |
(DesignPattern) 메멘토 패턴 (0) | 2020.07.26 |
패턴 정리 (템플릿, 팩토리, 추상팩토리) (0) | 2020.06.18 |
패턴 정리 (브릿지, 어댑터, 전략) (0) | 2020.06.18 |