3. mvc 프레임워크 만들기

유호경·2022년 9월 4일

mvc 프레임워크 만들기

[01. mvc 프레임워크 만들기 - 프론트 컨트롤러 패턴이란?][02. mvc 프레임워크 만들기 - 프론트 컨트롤러 도입]
[03. mvc 프레임워크 만들기 - View 분리][04. mvc 프레임워크 만들기 - Model 추가]
[05. mvc 프레임워크 만들기 - 단순하고 실용적인 컨트롤러][06. mvc 프레임워크 만들기 - 유연한 컨트롤러1]
[07. mvc 프레임워크 만들기 - 유연한 컨트롤러2][08. mvc 프레임워크 만들기 - 정리]

[01. mvc 프레임워크 만들기 - 프론트 컨트롤러 패턴이란?]


이전 시간까지 만들었던 Controller는 모두 공통로직이 들어가 있었다.


프론트 컨트롤러 도입시 하나의 공통 로직을 두고 각 Controller에서 필요한 로직은 각각의 Controller에 구현하게 된다.

FrontController의 특징

  • 서블릿 하나로 클라이언트의 요청을 받음
  • 요청에 맞는 컨트롤러를 찾아서 호출
  • 입구를 하나로
  • 공통 처리 기능
  • 프론트 컨트롤러를 제외한 나머지 컨트롤러는 서블릿을 사용하지 않아도 됨
  • 스프링 웹mvc의 DispatcherServlet 또한 FrontController 패턴으로 구현되어 있다.

[02. mvc 프레임워크 만들기 - 프론트 컨트롤러 도입]

프론트 컨트롤러를 단계적으로 도입해보자.
이번 목표는 기존 코드를 최대한 유지하면서 프론트 컨트롤러를 도입하는 것이다.
먼저 구조를 맞추어두고 점진적으로 리펙터링 해보자!

ControllerV1

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public interface ControllerV1 {
	
	void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
}

공통로직을 작성해줄 ControllerV1은 interface로 만들어준다.

MemberControllerV1

public class MemberFormControllerV1 implements ControllerV1{

	@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);
	}

}

MemberSaveControllerV1

public class MemberListControllerV1 implements ControllerV1{

	MemberRepository memberRepository = MemberRepository.getInstance();
	
	@Override
	public void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		 List<Member> members = memberRepository.findAll();
		 request.setAttribute("members", members);
		 
		 String viewPath = "/WEB-INF/views/members.jsp";
	     RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
	     dispatcher.forward(request, response);
	
	}

}

MemberListControllerV1

public class MemberSaveControllerV1 implements ControllerV1{
	
	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);
		
		// Model에 데이터를 보관한다.
		request.setAttribute("member", member);
		
		String viewPath = "/WEB-INF/views/save-result.jsp"; //해당 viewPath가 dispatcher에 의해 내부로 호출
		RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
		dispatcher.forward(request, response); //내부에서 호출해줌
	}

}

ControllerV1을 implements 하고 있는 3개의 Controller

비지니스로직은 mvc패턴으로 만들었던 코드를 그대로 사용한다.

frontControllerServletV1

@WebServlet(name = "frontControllerServletV1", urlPatterns = "/front-controller/v1/*")
public class FrontControllerServletV1 extends HttpServlet{
	
	private Map<String, ControllerV1> controllerMap = new HashMap<>();
	
	private FrontControllerServletV1() { //기본 생성자
		controllerMap.put("/front-controller/v1/members/new-form", new MemberFormControllerV1()); // 해당 url로 요청이 오면 매핑된 Controller가 생성됨
		controllerMap.put("/front-controller/v1/members/save", new MemberSaveControllerV1());
		controllerMap.put("/front-controller/v1/members", new MemberListControllerV1());
	}
	
	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("FrontControllerServletV1.service");
		
		String requestUri = request.getRequestURI();
		ControllerV1 controller = controllerMap.get(requestUri); 
		if(controller == null) { // 해당하는 requestUri가 존재하지 않을 경우
			response.setStatus(HttpServletResponse.SC_NOT_FOUND); //404
			return;
		}
		
		controller.process(request, response);
	}
}

servlet을 통해 3개의 Controller에 요청을 보내줄 Controller다.
코드를 보면 Map 안에 요청 uri와 Controller 객체를 넣어준다. 클라이언트에서 요청이 들어오면
frontControllerServletV1.ervice() → 요청 uri가 일치한다면 → 각 Controller의 process 순으로
메서드가 작동한다. 여기서 다형성이라는 개념을 이해할 필요가 있다. service 메서드를 보면
부모객체인 ControllerV1 타입인 controller 객체가 uri를 담고 있다. 각 Controller는 부모객체인 ControllerV1을 상속받고 있기 때문에 부모객체에 담아 사용할 수 있다.
이렇게 작성하고 회원가입을 하면 회원List에 데이터가 잘들어오는 모습을 볼 수 있다.

frontController를 도입하고 보니 생각보다 간결하게 구현되지는 않는 것 같다.
여기서 어떤점들을 개선할 수 있는지 확인해서 좀더 보완하고 수정할 필요가 있는 것 같다.

profile
하면된다.

0개의 댓글