ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [스프링MVC - 1편] MVC 프레임워크 만들기 (1)
    스프링&스프링부트 2025. 1. 12. 13:27

    프론트 컨트롤러 패턴 소개

    프론트 컨트롤러 도입

     

     

    프론트 컨트롤러 도입 후

     

     

    FrontController 패턴 특징
    • 프론트 컨트롤러 서블릿 하나로 클라이언트의 요청을 받음
    • 프론트 컨트롤러가 요청에 맞는 컨트롤러를 찾아서 호출
    • 입구를 하나로
    • 공통 처리 가능
    • 프론트 컨트롤러를 제외한 나머지 컨트롤러는 서블릿을 사용하지 않아도 

     

     

    스프링 웹 MVC와 프론트 컨트롤러

    스프링 MVC 핵심도 바로 FrontController이다

    스프링 MVC DispatcherServlet FrontController 패턴으로 구현되어 있다

     

     

    프론트 컨트롤러 도입 - v1

    [V1 구조 ]

     

     

    1. ControllerV1

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

    서블릿과 비슷한 모양의 컨트롤러 인터페이스를 도입한다 (각 컨트롤러들은  인터페이스를 구현)

    프론트 컨트롤러는 인터페이스를 호출해서 구현과 관계없이 로직의 일관성을 가져갈 있다

     

     

    2. MemberFormControllerV1 - 회원 등록 컨트롤러

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

     

     

    3. MemberSaveControllerV1 - 회원 저장 컨트롤러

    public class MemberSaveControllerV1 implements ControllerV1 {
    
        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);
    
            // Model 데이터 보관
            request.setAttribute("member", member);
    
            String viewPath = "/WEB-INF/views/save-result.jsp";
            RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
            dispatcher.forward(request, response);
        }
    
    }

     

     

    4. MemberListControllerV1 - 회원 목록 컨트롤러

    public class MemberListControllerV1 implements ControllerV1 {
    
        private 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);
        }
    }

     

     

    5. 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 request, HttpServletResponse response) throws ServletException, IOException {
            // System.out.println("FrontControllerServletV1.service");
    
            String requestURI = request.getRequestURI();
            ControllerV1 controller = controllerMap.get(requestURI);
    
            if (controller == null) {
                response.setStatus(HttpServletResponse.SC_NOT_FOUND);
                return;
            }
            
            controller.process(request, response);
        }
    }

     

     

    프론트 컨트롤러 분석
      • urlPatterns
        • urlPatterns = "/front-controller/v1/*  : /front-controller/v1 포함한 하위 모든 요청 서블릿에서 받아들인다
      • controllerMap
        • key : 매핑 URL
        • value : 호출될 컨트롤러
      • service()
        • requestURI 조회해서 실제 호출할 컨트롤러를 controllerMap에서 찾는다
        • 컨트롤러를 찾고 controller.process(request, response); 호출해서 해당 컨트롤러를 실행한다
      • JSP - 동일

     

     

    View 분리 - v2

    String viewPath = "/WEB-INF/views/new-form.jsp";
    RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
    dispatcher.forward(request, response);

    모든 컨트롤러에서 뷰로 이동하는 부분에 중복이 있고, 깔끔하지 않다

    부분을 깔끔하게 분리하기 위해 별도로 뷰를 처리하는 객체를 만든다

     

     

    V2 구조

     

     

    1. MyView

    public class MyView {
        private String viewPath;
    
        public MyView(String viewPath) {
            this.viewPath = viewPath;
        }
    
        public void render(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
            dispatcher.forward(request, response);
        }
    }

     

     

    2. ControllerV2

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

     

     

    3. MemberFormControllerV2 - 회원 등록

    public class MemberFormControllerV2 implements ControllerV2 {
        @Override
        public MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            return new MyView("/WEB-INF/views/new-form.jsp");
        }
    }

     

     

    4. MemberSaveControllerV2 - 회원 저장

    public class MemberSaveControllerV2 implements ControllerV2 {
    
        private MemberRepository memberRepository = MemberRepository.getInstance();
    
        @Override
        public MyView 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);
    
            return new MyView("/WEB-INF/views/save-result.jsp");
        }
    }

     

     

    5. MemberListControllerV2 - 회원 목록

    public class MemberListControllerV2 implements ControllerV2 {
    
        private MemberRepository memberRepository = MemberRepository.getInstance();
    
        @Override
        public MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            List<Member> members = memberRepository.findAll();
            request.setAttribute("members", members);
            return new MyView("/WEB-INF/views/members.jsp");
        }
    }

     

     

    6. 프론트 컨트롤러 V2

    @WebServlet(name="frontControllerServletV2", urlPatterns="/front-controller/v2/*")
    public class FrontControllerServletV2 extends HttpServlet {
    
        private Map<String, ControllerV2> controllerMap = new HashMap<>();
    
        public FrontControllerServletV2() {
            controllerMap.put("/front-controller/v2/members/new-form", new MemberFormControllerV2());
            controllerMap.put("/front-controller/v2/members/save", new MemberSaveControllerV2());
            controllerMap.put("/front-controller/v2/members", new MemberListControllerV2());
        }
    
        @Override
        protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            String requestURI = request.getRequestURI();
            ControllerV2 controller = controllerMap.get(requestURI);
    
            if (controller == null) {
                response.setStatus(HttpServletResponse.SC_NOT_FOUND);
                return;
            }
    
            MyView view = controller.process(request, response);
            view.render(request, response);
        }
    
    }

    728x90
Designed by Tistory.