프론트 컨트롤이란

  • 프론트 컨트롤러 서블릿 하나로 클라이언트의 요청을 받는다.
  • 프론트 컨트롤러가 요청에 맞는 컨트롤러를 찾아서 호출한다.

프론트 컨트롤이 실행되는 순서

  1. 클라이언트가 FrontController로 요청
  2. FrontController는 요청된 매핑정보 확인
  3. 매핑 정보 확인 후 controller 호출
  4. 해당 controller에 맞는 jsp로 이동
  5. jsp로 이동후 클라이언트에게 응답
공통 코드
@Getter
@Setter
@ToString
public class Member {

    private Long id;
    private String username;
    private int age;


    public Member(String username, int age) {
        this.username = username;
        this.age = age;
    }
    public Member() {

    }
}

public class MemberRepository {

    private static Map<Long, Member> store = new HashMap<>();
    private static long sequence = 0L;

    private  static final MemberRepository instance = new MemberRepository();
    public static MemberRepository getInstance() {
        return instance;
    }
    private MemberRepository() {

    }

    public Member save(Member member) {
        member.setId(++sequence);
        store.put(member.getId(), member);
        return member;
    }
    public Member findById(Long id) {
        return store.get(id);
    }
    public List<Member> findAll() {
        return new ArrayList<>(store.values());
    }
    public void clearStore() {
        store.clear();
    }
}

프론트 컨트롤이 적용 안된 코드

@WebServlet(name="mvcMemberInputSerlvet", urlPatterns = "/mvc/members/input")
class MvcMemberInputSerlvet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
                        throws ServletException, IOException {

        String path = "/WEB-INF/view/input.jsp"; //urlPatterns를 입력했을때 이동하는 경로

        // Controller에서 View로 이동할때 사용하는 메소드 => getReuqestDispatcher(이동할 경로)
        RequestDispatcher dispatcher = request.getRequestDispatcher(path);

        // 다른 서블릿이나 JSP로 이동할 수 있는 기능, 서버 내부에서 다시 호출 발생
        dispatcher.forward(request,response);

        }

}

@WebServlet(name = "mvcMemberSaveServlet", urlPatters = "/mvc/members/save"
class MvcMemberSaveServlet extends HttpServlet {

    // 이 부분은 정보를 저장하기 위해 싱글톤 패턴으로 구현된 객체이다.
    private MemberRepsoitory memberRepository = MemberRepository.getInstance();


    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
                             throws ServletException, IOException {
         //input.jsp에서 입력된 name값과 age값 받기
        String name = 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 path = "/WEB-INF/view/save.jsp";

        RequestDispatcher dispatcher = request.getRequestDispatcher(path);

        dispatcher.forward(request,response);
    }

 }

 @WebSerblet(name ="mvcMemberListServlet", urlPatterns="/mvc/members"
 class MvcMemberListServlet extends HttpServlet {
     private MemberRepository memberRepository = MemberRepository.getInstance();

     @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
                             throws ServletException, IOException {
            List<Member> members = memberRepository.findAll();
            String path = "/WEB-INF/view/members.jsp";
            request.setAttribute("members",member);

            RequestDispatcher dispatcher = request.getRequestDispatcher(path);

            dispatcher.forward(request,response);

    }
}

처음 부분에 설명했듯이 프론트 컨트롤은 서블릿 하나로 클라이언트의 요청을 받는데 프론트컨트롤 패턴을 안 한 코드를 보면 여러 개의 서블릿이 클라이언트의 요청을 받는다.
이제 프론트 컨트롤 패턴을 적용한 코드를 보겠다.

프론트 컨트롤 패턴 적용 코드

public interface Controller {

       void process(HttpServletRequest request, HttpServletResponse response)
                           throws ServletException, IOException;
}


public MemberInputController implements Controller {
    @Override
    void process(HttpServletRequest request, HttpServletResponse response)
                           throws ServletException, IOException{
          String path = "/WEB-INF/view/input.jsp";
          RequestDispatcher dispatcher = request.getRequestDispatcher(path);
          dispatcher.forward(request,response);                        
        }        
 }

 public MemberSaveController implements Controller {
         private MemberRepository memberRepository = MemberRepository.getInstatnce();

    @Override
    void process(HttpServletRequest request, HttpServletResponse response)
                           throws ServletException, IOException{

          String name = request.getParameter("name");
        int age = Integer.parseInt(request.getParameter("age"));

        Member member = new Member(name, age);

        memberRepository.save(member);

        request.setAttribute("member",member);
        String path = "/WEB-INF/view/save.jsp";
        RequestDispatcher dispatcher = request.getRequsetDispatcher(path);
        dispatcher.forward(request,response);
     }      
 }

 public MemberListController implements Controller {
         private MemberRepository memberRepository = MemberRepository.getInstatnce();
    @Override
    void process(HttpServletRequest request, HttpServletResponse response)
                           throws ServletException, IOException{

            List<Member> members = memberRepository.findAll();
            String path = "/WEB-INF/view/members";

            request.setAttribute("members",members);
            RequestDispatcher dispatcher = request.getRequsetDispatcher(path);
            dispatcher.forward(request,response);
     }
}

프론트 컨트롤 패턴을 적용한 코드와 안 한 코드를 비교해보면 차이점을 바로 알 수 있다.
이제 프론트컨트롤을 적용한 코드에 하나의 서블릿으로 요청을 받는 코드를 작성하겠다.

@WebServlet(name="frontControllerServlet", urlPatterns="/frontcontroller/*")
// /frontcontroller/* => frontcontroller 하위에 뭐가 들어와도 상관이 없다

public class FrontControllerServlet extends HttpServlet {
    private Map<String, Controller> controllerMap = new HashMap();

    public FrontControllerServlet() {
    	// 생성자 안에 controllerMap.put을 한이유
        // => 서블릿이 서블릿 컨테이너로 들어가지면서 객체가 생성이 되기때문에
        // 미리 저장해서 사용할려고
        controllerMap.put("/frontcontroller/input", new MemberInputController());
        controllerMap.put("/frontcontroller/save", new MemberSaveController());
        controllerMap.put("/frontcontroller/members", new MemberListController());
        }
         @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
                        throws ServletException, IOException {
             //만약 localhost:9090/frontcontroller/input 으로 요청을 하면 
             // requestURI = /frontcontroller/input
             String requestURI = request.getRequestURI();

             //controllerMap.get(requestURI) => 키값이 requestURI이니깐 
             // 만약 requestURI = /frontcontroller/input 일 경우 new MemberInputController() 인스턴스가 생성

             Controller controller = controllerMap.get(requestURI);

             controller.process(request,request);

    }
}

정리

프론트 컨트롤 적용 안 한 코드 : 여러 개의 서블릿으로 클라이언트의 요청을 받는다.

프론트 컨트롤 적용한 코드 : 하나의 서블릿으로 클라이언트의 요청을 받는다.

밑으로 내려갈수록 발전된 프론트 컨트롤 패턴 활용

프론트 컨트롤러 도입 - https://hoestory.tistory.com/12

view분리 - https://hoestory.tistory.com/13

Model 추가 - https://hoestory.tistory.com/17

단순하고 실용적 컨트롤러 - https://hoestory.tistory.com/19

유연한 컨트롤 -