ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • (1) MVC 프레임워크 만들기 - FrontController의 도입
    Spring/MVC1-Servlet vs JSP vs MVC패턴 2023. 6. 13. 08:50
    728x90

    Servlet -> JSP -> 고전 MVC(Servlet + JSP) 의 과정을 거치면서 나름대로 기능별로 나누어 파일을 관리하였다.

    하지만, 여전히 중복되는 코드들이 많았다.

    https://hongs429-blog.tistory.com/44

     

    웹 애플리케이션으로 Servlet / JSP / 고전 MVC 패턴 비교 - MVC

    Servlet Pattern https://hongs429-blog.tistory.com/42 웹 애플리케이션으로 Servlet / JSP / 고전 MVC 패턴 비교 - Servlet 간단한 회원 관리 웹 애플리케이션 구현( Servlet ) 예전의 Servlet 구조화 JSP, MVC 패턴의 비교를

    hongs429-blog.tistory.com

     

     

    중복이 일어나는 부분

    • View를 처리하는 로직
    • dispatcher로 forwarding (view로 데이터 전송) 해주는 로직

    김영한의 로드맵 강의 - MVC 1편 내용 中

    현재의 그림은 기능마다 Servlet을 두어 처리하는 방식이다.

     

    이제 공통로직을 묶고 최대한 Servlet 방식의 종속성을 없애려면 1개의 Servlet으로 공통로직을 처리하는

    Front Controller를 둘 것이다.

    이렇게 Front controller를 두는 방식이
    실제 우리가 쓰는 MVC에서 DispatcherServlet이 구현되어있는 방식과 유사하다

     

    Front Controller 의 특징

    • 1개의 서블릿으로 클라이언트의 요청을 받음
    • 프론트 컨트롤러가 요청을 받고, 이를 각각의 기능에 맞는 controller를 호출
    • 공통 로직을 처리할 수 있어, 실제로 구현을 하는 controller의 코드를 줄일 수 있음
    • 실제 서블릿은 프론트 컨트롤러에서만 사용하고,
      뒷단의 controller에서는 Servlet 사용하지 않아도됨 (Servlet에 종속적인 프레임워크가 더이상 x)

     

     

    김영한의 로드맵 강의 - MVC 1편 내용 中

     

    < FrontController >

    역할

    • 해당 url 패턴으로 들어오는 모든 요청을 FrontController 가 받는다
    • FrontController는 원하는 요청에 맞는  url과 해당 url에 대한 로직이 구현되어 있는 객체를 가지고 있는다
      이후, 다형성을 이용하여 3가지 객체(회원 저장, 조회, 전체조회)를 담을 수 있는 Interface 타입으로 url에 맞게 컨트롤러를 mapping 한다.
    • mapping 된 controller 객체는 각자의 로직에 맞는 overriding된 메소드 process()를 실행하여
      각 요청에 맞는 View를 RequestDispatcher.forward()로 forwarding 한다
    @WebServlet(name = "frontControllerServletV1", urlPatterns = "/front-controller/v1/*")
    public class FrontControllerServletV1 extends HttpServlet {
    
        private Map<String, ControllerV1> controllerV1Map = new HashMap<>();
    
        public FrontController() {
            controllerV1Map.put("/front-controller/v1/members/new-form", new MemberFormControllerV1());
            controllerV1Map.put("/front-controller/v1/members/save", new MemberSaveController());
            controllerV1Map.put("/front-controller/v1/members", new MemberListControllerV1());
    
        }

    [mapping 할 객체를 미리 선언하여 가지고 있는 모습]

     

     

     	@Override
        protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("FrontControllerServletV1.service");
    
            String requestURI = request.getRequestURI();
            System.out.println("requestURI = " + requestURI);
    
            // 인터페이스의 다형성을 이용하여 모든 컨드롤러를 담을 수 있다.
            ControllerV1 controller = controllerV1Map.get(requestURI);
    
            if (controller == null) {
                response.setStatus(HttpServletResponse.SC_NOT_FOUND);
                return;
            }
    
            controller.process(request, response); // dispatcher.forward 사용되는 메소드
    
    
        }

    [ FrontController는 Servlet에 종속적으로 만들어져 있다 ]

     

     

     

    < Controller : interface >

    역할

    • 각각의 기능을 담당하는 Controller에서 수행할 메소드가 미리 정의되어 있다
    • 3개의 객체(회원 저장, 조회, 전체 조회)는 내부 로직은 다르지만, 공통의 class를 상속받고 있어,
      후에 다형성을 이용하여 객체를 저장할 수 있다.
    public interface ControllerV1 {
    
        void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
    
    
    }

    이제 위의 Controller interface를 이용하여 각각의 기능을 수행하는 구현체를 만들 것이다.

     

     

     

     

    Controller : MemberFormController 구현체

    기능

    • 회원가입 페이지로 이동을 담당하는 기능
    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);
        }
    }

    각각의 구현체들은 Controller interface를 상속받아 만들어지며,

    메소드 안에 미리 forwading할 view의 경로가 지정되어 있다.

     

     

     

     

    Controller : MemberSaveController 구현체

    기능

    • Memory Repository(메모리 저장소)에 회원의 정보를 저장하는 기능
    • 저장된 회원의 정보를 보여주는 기능
    public class MemberSaveController 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);
        }
    }

    여기서 중요한 것은 기능을 수행하고 결과 데이터를 전송하는 방식을 request를 활용하고 있다.

    추후에 이는 Model 객체가 수행하며, 현재로선 request의 기능 중에 저장소의 기능을 활용하여 데이터를 View로 전달한다.

     

     

     

     

    Controller : MemberListController 구현체

    기능

    • 전체 회원 조회 및 View에 데이터 전달 기능
    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);
        }
    }

    MemberListController의 코드는 MemberSaveController와 유사하므로 설명 생략.

     

     

     

    정리

    http 요청
    -> FrontController : url mapping
    -> Controller 인터페이스의 구현체를 찾아옴
    -> 구현체에 정의된 메소드를 실행하여, 작성된 비지니스로직을 수행하고 미리 정해놓은 view로 forwarding 함

     

     

    위의 방법은 단순히 여러 Controller 앞단에 FrontController를 만들어 공통로직을 처리하는 방식을 취하고 있다.

    그럼에도 각각의 Controller 구현체들은 servlet에 의존적이며(매개변수로 servlet을 받아서 내부로직을 처리함),

    반복되는 로직이 존재한다.

    앞으로 Controller 구현체간의 공통로직을 하나 하나 처리하는 방식으로 MVC패턴을 수정하고자 한다.

     

     

    다음 단계 링크

    https://hongs429-blog.tistory.com/47
     

    (2) MVC 프레임워크 만들기 - Controller 구현체들의 공통로직 처리

    https://hongs429-blog.tistory.com/46 (1) MVC 프레임워크 만들기 - FrontController의 도입 Servlet -> JSP -> 고전 MVC(Servlet + JSP) 의 과정을 거치면서 나름대로 기능별로 나누어 파일을 관리하였다. 하지만, 여전히

    hongs429-blog.tistory.com

     

Designed by Tistory.