기존의 패턴을 설명하자면 아래 그림과 같다
각 클라이언트들은 Controller A, B, C에 대해 각각 호출한다.
공통 코드들은 별도로 처리되어 있지 않고 각 Controller에 포함되어 있다.
하지만 프론트 컨트롤러 패턴을 도입한다면?
각 클라이언트들은 Front Controller에 요청을 보내고,
Front Controller은 각 요청에 맞는 컨트롤러를 찾아서 호출시킨다.
공통 코드에 대해서는 Front Controller에서 처리하고, 서로 다른 코드들만 각 Controller에서 처리할 수 있도록 한다.
장점을 정리해보자면,
- 공통 코드 처리가 가능 (★중요)
- Front Controller 외 다른 Controller에서 Servlet 사용하지 않아도 됨
스프링 웹 MVC의 핵심도 위 같은 FrontController이다.
(스프링 웹 MVC의 DispatcherServlet이 FrontController 패턴으로 구현되어 있음)
이제 앞에서 계속 개발했던 회원 관리 웹 어플리케이션에 FrontController를 적용해보겠다.
한번에 Spring MVC 패턴에 맞추는 것이 아니라 기존 코드를 최대한 살려가며 버전을 업그레이드 시킬 것이다.
jsp 폴더에 관해서는 MVC 패턴 적용 시 썼던 파일을 재사용한다. ( yeonyeon.tistory.com/102 참고)
가장 먼저 main/.../web/frontcontroller/v1 폴더를 생성해 인터페이스를 하나 생성해준다.
ControllerV1
public interface ControllerV1 {
void process(HttpServletRequest req, HttpServletResponse res) throws ServletException,IOException;
}
v1폴더 하위에 controller 폴더를 만들고 이 안에 각 컨트롤러들을 생성한다.
로직은 앞서 만들었던 MVC 패턴과 동일하므로 자세한 설명을 생략한다.
MemberFormControllerV1
public class MemberFormControllerV1 implements ControllerV1{
@Override
public void process(HttpServletRequest req, HttpServletResponse res) throws ServletException,IOException {
String viewPath = "/WEB-INF/views/new-form.jsp";
RequestDispatcher dispatcher = req.getRequestDispatcher(viewPath);
dispatcher.forward(req, res);
}
}
MemberSaveControllerV1
public class MemberSaveControllerV1 implements ControllerV1{
MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public void process(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
String username = req.getParameter("username");
int age = Integer.parseInt(req.getParameter("age"));
Member member = new Member(username, age);
memberRepository.save(member);
req.setAttribute("member", member);
String viewPath = "/WEB-INF/views/save-result.jsp";
RequestDispatcher dispatcher = req.getRequestDispatcher(viewPath);
dispatcher.forward(req, res);
}
}
MemberListControllerV1
public class MemberListControllerV1 implements ControllerV1{
MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public void process(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
List<Member> members = memberRepository.findAll();
req.setAttribute("members", members);
String viewPath = "/WEB-INF/views/members.jsp";
RequestDispatcher dispatcher = req.getRequestDispatcher(viewPath);
dispatcher.forward(req, res);
}
}
이제 가장 중요한 Front Controller를 생성할 차례이다.
위에서 만든 컨트롤러보다 한 단계 위인 v1 폴더에 생성한다.
(가장 먼저 만들었던 interface와 같은 위치)
FrontControllerServletV1
@WebServlet(name = "frontControllerServletV1", urlPatterns = "/front-controller/v1/*")
public class FrontControllerServletV1 extends HttpServlet {
private Map<String, ControllerV1> controllerMap = new HashMap<>();
public FrontControllerServletV1() {
controllerMap.put("/front-controller/v1/members/new-form", new MemberFormControllerV1());
controllerMap.put("/front-controller/v1/members/save", new MemberSaveControllerV1());
controllerMap.put("/front-controller/v1/members", new MemberListControllerV1());
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
System.out.println("FrontControllerServletV1.service");
String reqURI = req.getRequestURI();
ControllerV1 controller = controllerMap.get(reqURI);
if(controller == null) {
res.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
controller.process(req, res);
}
}
- urlPatterns = "/front-controller/v1/*": front-controller/v1 하위의 모든 항목에 대해 해당 서블릿 실행
- 생성자: 각 url에 대해 Controller를 매핑하기 위해 Map에 데이터를 put
- request에 대한 URI 값을 가져와서 controllerMap에서 어떤 컨트롤러에 매핑 되었는지 찾는다
- 만약 controller가 존재하지 않는다면 404 에러 처리
이제 위에서 살펴봤던 그림을 예제 코드를 짚어가며 다시 보자
각 클라이언트들은 /front-controller/v1 하위의 어떤 경로를 접속하던간에 Front Controller으로 이동한다.
예를 들어, localhost:8080/front-controller/v1/members에 접속했다고 가정하자.
- @WebServlet의 urlPatterns에 의해 FrontControllerServletV1으로 이동
- 접속한 URI를 받고, ControllerV1 controller 변수에 MemberListControllerV1 저장 (Map에서 꺼내옴)
- MemberListControllerV1의 process 실행
아직 모든 Controller에서 RequestDispatcher을 이용해 view로 이동한다는 코드가 계속 반복되고 있다.
다음 글에서는 이 부분을 개선해보겠다.
본 게시글은 김영한 님의 '스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술' 강의를 구매 후 정리하기 위한 포스팅입니다.
내용을 임의로 추가, 수정, 삭제한 부분이 많으며 정확한 이해를 위해서 강의를 구매하시는 것을 추천 드립니다.
'Develop > Java' 카테고리의 다른 글
[MVC] Model 분리 (0) | 2021.05.11 |
---|---|
[MVC] View 분리 (0) | 2021.05.10 |
[MVC패턴] 회원 관리 웹 애플리케이션 (0) | 2021.05.10 |
[JSP] 회원 관리 웹 애플리케이션 (0) | 2021.05.07 |
[Servlet] 회원 관리 웹 애플리케이션 (0) | 2021.05.06 |