본문 바로가기
Develop/Java

[MVC] 프론트 컨트롤러 패턴

by 연로그 2021. 5. 10.
반응형

기존의 패턴을 설명하자면 아래 그림과 같다

각 클라이언트들은 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에 접속했다고 가정하자.

  1. @WebServlet의 urlPatterns에 의해 FrontControllerServletV1으로 이동
  2. 접속한 URI를 받고, ControllerV1 controller 변수에 MemberListControllerV1 저장 (Map에서 꺼내옴)
  3. MemberListControllerV1의 process 실행

 

아직 모든 Controller에서 RequestDispatcher을 이용해 view로 이동한다는 코드가 계속 반복되고 있다.

다음 글에서는 이 부분을 개선해보겠다.

 


본 게시글은 김영한 님의 '스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술' 강의를 구매 후 정리하기 위한 포스팅입니다.

내용을 임의로 추가, 수정, 삭제한 부분이 많으며 정확한 이해를 위해서 강의를 구매하시는 것을 추천 드립니다.

 

inf.run/B756

 

스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 - 인프런 | 강의

웹 애플리케이션을 개발할 때 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 MVC의 핵심 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., 원

www.inflearn.com

 

반응형

'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