프론트 컨트롤 개념 및 예제(view 분리) 여기에서 MemberInputController, MemberSaveController, MemberListController 컨트롤들이 HttpServletRequest, HttpServletResponse를 꼭 필요로 하지 않습니다.

HttpServletRequest로 요청을 받던 정보들을 Map으로 받으면 컨트롤러가 서블릿 기술을 몰라도 동작합니다.

그리고 request 객체를 Model로 사용하는 대신에 별도의 Model 객체를 만들어서 반환을 해주면 됩니다.

그리고 뷰 이름이 중복되어 컨트롤러는 뷰의 논리 이름만 반환하고 프론트 컨트롤에서 물리적 위치를 처리하면 됩니다.

프론트 컨트롤 실행 순서(Model 추가)

  1. 클라이언트가 프론트 컨트롤에 요청을 합니다.
  2. 프론트 컨트롤은 들어온 요청을 토대로 컨트롤러를 조회를 합니다.
  3. 조회한 컨트롤러로 컨트롤러를 호출합니다.
  4. 호출된 컨트롤러에서 ModelView를 반환합니다.
  5. 프론트 컨트롤에 ModelView를 반환합니다.
  6. 프론트 컨트롤은 viewResolver를 호출합니다.
  7. viewResolver는 각 컨트롤러에서 논리 이름으로 반환한 값을 물리적 위치로 바꿔주고 컨트롤러에게 MyView를 반환해줍니다.
  8. forward를 해서 응답을 해줍니다.

여기서 ModelView가 위에 말했듯이 HttpServletRequest 사용하는 것을 없애게 된 이유입니다.

 

public interface Controller {
	ModelView process(Map<Stirng, String> paramMap);
}

public class ModelView [
	private String viewName;
    private Map<String, Object> model = new HashMap<>();
    
    public ModelView(String viewName) {
    	this.viewName = viewName;
    }
    
    public String getViewName() {
    	return viewName;
    }
    
    public void setViewName(String viewName) {
    	this.viewName = viewName;
    }
    
    public Map<String, Object> getModel() {
    	return model;
    }
    
    public void setModel(Map<String,Object> model) {
    	this.model = model;
    }
}
public class MemberInputController implements Controller {
    @Override
    public ModelView process(Map<String, String> paramMap) {
    	// 논리적 이름 반환
    	return new ModelView("new-form");
    }
}


public class MemberSaveController implements Controller {
	
    // 이 코드가 왜 나왔는지 모르겠으면 프론트컨트론 개념 및 예제(프론트컨트롤 도입) 부분에서
    // 확인하면 된다.
	private MemberRepository memberRepository = MemberRepository.getInstance();
    
    @Override
    public ModelView process(Map<String, String> paramMap) {
    	String name = paramMap.get("username");
        int age = Integer.parseInt(paramMap.get("age"));
        Member member = new Member(name, age);
        // 논리적 이름 반환
        ModelView mv = new ModelView("save");
        
         mv.getModel().put("member",member);
        
    	return mv;
    }
    
public class MemberListController implements Controller {
    private MemberRepository memberRepository = MemberRepository.getInstance();
    @Override
    public ModelView process(Map<String, String> paramMap) {
    	List<Member> members = memberRepository.findAll();
        ModelView mv = new ModelView("members");
        
        // request.setAttribute와 같은 역할
        mv.getModel().put("members",members);
    }
}

 

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

    private void modelToRequestAttribute(Map<String, Object> model, HttpServletRequest request) {
        model.forEach((key, value) -> request.setAttribute(key,value));
    }
}


@WebServlet(name="frontController", urlPatterns="/frontcontroller/*")
public class FrontController extends HttpServlet {
    private Map<String, Controller> controllerMap = new HashMap<>();

    public FrontController() {
        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/members
            // requestURI => /frontcontroller/members
            String requestURI = request.getRequestURI();
            Controller controller = controllerMap.get(requestURI);
            Map<String, String> param = createParam(request);
            ModelView mv = controller.process(param);
            
            MyView view = viewResolver(mv);
            view.render(mv.getModel(),request, response);
        }
    private MyView viewResolver(ModelView mv) {
        MyView view = new MyView("/WEB-INF/view/" + mv.getViewName() + ".jsp");
        return view;
    }

    private Map<String, String> createParam(HttpServletRequest request) {
        Map<String, String> paramMap = new HashMap<>();
        request.getParameterNames().asIterator()
                .forEachRemaining(paraName -> paramMap.put(paraName, request.getParameter(paraName)) );
        return paramMap;
    }
}

예시 URL : localhost:9090/frontcontroller/members

  • requestURI : /frontcontroller/members
  • controller : new MemberListController() controllerMap.get(requestURI)를 해서 requestURI =/frontcontroller/members입니다.
  • createParam : 요청이 들어오면 값들 반환
  • param : 요청된 파라미터 값들이 키와 값으로 저장되어 있습니다.
  • mv : MemberListController.process(param)
  • viewResovler : 논리 이름을 물리적 이름으로 반환
  • view.render : 해당 jsp파일로 forward 해줍니다.

 

정리

컨트롤러에 필요 없는 HttpServletRequest와 HttpServletResponse를 없애고  모든 컨트롤러에 "/WEB-INF/view/"가 중복되어 제거하기 위해 ModelView 클래스를 따로 만들었습니다. 

ModelView에 논리 이름을 담아두고 viewResolver로 물리적 이름으로 변경해줍니다.

 

 

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

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

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

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

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

유연한 컨트롤 -