๐Ÿ“‹ Spring MVC - 4. MVC ํ”„๋ ˆ์ž„์›Œํฌ ๋งŒ๋“ค๊ธฐ


MVC ํ”„๋ ˆ์ž„์›Œํฌ ๋งŒ๋“ค๊ธฐ

ํ”„๋ก ํŠธ ์ปจํŠธ๋กค๋Ÿฌ ํŒจํ„ด

ํ”„๋ก ํŠธ ์ปจํŠธ๋กค๋Ÿฌ๋„ ์„œ๋ธ”๋ฆฟ์ด๋‹ค.
์š”์ฒญ์„ ๋ชจ๋‘ ๋ฐ›๊ณ  ์š”์ฒญ์— ๋งž๋Š” ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

ํŠน์ง•

ํ”„๋ก ํŠธ ์ปจํŠธ๋กค๋Ÿฌ ์„œ๋ธ”๋ฆฟ ํ•˜๋‚˜๋กœ ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ์„ ๋ฐ›๊ณ , ์š”์ฒญ์— ๋งž๋Š” ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ฐพ์•„์„œ ํ˜ธ์ถœํ•œ๋‹ค.
์ž…๊ตฌ๋ฅผ ํ•˜๋‚˜๋กœ ๋ฌถ๋Š”๋‹ค.
ํ”„๋ก ํŠธ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ œ์™ธํ•œ ๋‚˜๋จธ์ง€ ์ปจํŠธ๋กค๋Ÿฌ๋Š” ์„œ๋ธ”๋ฆฟ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.

์Šคํ”„๋ง ์›น MVC์™€ ํ”„๋ก ํŠธ ์ปจํŠธ๋กค๋Ÿฌ

์Šคํ”„๋ง ์›น MVC์˜ ํ•ต์‹ฌ๋„ ๋ฐ”๋กœ FrontController์ด๋‹ค. (DispatcherServlet์ด ํ”„๋ก ํŠธ ์ปจํŠธ๋กค๋Ÿฌ ํŒจํ„ด์œผ๋กœ ๋˜์–ด์žˆ๋‹ค.)

ControllerV1์„ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๋งŒ๋“ค๊ณ  ์ด๋ฅผ ๊ตฌํ˜„ํ•œ Controller๋“ค์„ ๋งŒ๋“ ๋‹ค.

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 requestDispatcher = request.getRequestDispatcher(viewPath); // ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ๋ทฐ๋กœ ์ด๋™
        requestDispatcher.forward(request, response); // ์„œ๋ธ”๋ฆฟ์—์„œ JSP๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
    }
}

FrontControllerServletV1

@WebServlet(name = "frontControllerServletV1", urlPatterns = "/front-controller/v1/*")
public class FrontControllerServletV1 extends HttpServlet {
    private Map<String, ControllerV1> controllerMap = new HashMap<>();

    public FrontControllerServletV1(Map<String, ControllerV1> controllerMap) {
        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 req, HttpServletResponse resp) throws ServletException, IOException {
        String requestURI = req.getRequestURI();
        ControllerV1 controller = controllerMap.get(requestURI);
        if (controller == null) {
            resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        controller.process(req, resp);
    }
}

Controller ์ •๋ณด๋ฅผ ๊ฐ€์ง„ ์„œ๋ธ”๋ฆฟ์ธ FrontContoller๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค.

  • urlPatterns : โ€œ/front-controller/v1/โ€๋ฅผ ํฌํ•จํ•œ ํ•˜์œ„ ๋ชจ๋“  ์š”์ฒญ์„ ํ•˜๋‚˜์˜ ์„œ๋ธ”๋ฆฟ์—์„œ ๋ฐ›์•„๋“ค์ธ๋‹ค.
  • controllerMap : key-๋งคํ•‘ URL, value-ํ˜ธ์ถœ๋  ์ปจํŠธ๋กค๋Ÿฌ
  • service() : reqeustURI๋ฅผ ์กฐํšŒํ•˜์—ฌ ์‹ค์ œ ํ˜ธ์ถœํ•  ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ฐพ๊ณ  ํ•ด๋‹น ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์‹คํ–‰(process())ํ•œ๋‹ค.

์—ฌ๋Ÿฌ Controller์™€ ์„œ๋ธ”๋ฆฟ์ด ํ•˜๋‚˜์˜ JSP๋ฅผ ์žฌ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค.


View ๋ถ„๋ฆฌ

Controller์—์„œ View๋กœ ์ด๋™ํ•˜๋Š” ๋ถ€๋ถ„์ด ์ค‘๋ณต๋œ๋‹ค.
Controller๋Š” MyView๋งŒ ๋ฐ˜ํ™˜ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

ControllerV2

public interface ControllerV2 {
    MyView process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
}
  • ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋ฏ€๋กœ ๋ฐ˜ํ™˜ ํƒ€์ž…์„ ๋งž์ถœ ์ˆ˜ ์žˆ๋‹ค.

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

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");
    }
}
  • viewPath๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” MyView ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

FrontControllerServletV2

@WebServlet(name = "frontControllerServletV2", urlPatterns = "/front-controller/v2/*")
public class FrontControllerServletV2 extends HttpServlet {
    private Map<String, ControllerV2> controllerMap = new HashMap<>();

    public FrontControllerServletV2(Map<String, ControllerV2> controllerMap) {
        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 req, HttpServletResponse resp) throws ServletException, IOException {
        String requestURI = req.getRequestURI();
        ControllerV2 controller = controllerMap.get(requestURI);
        if (controller == null) {
            resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        MyView view = controller.process(req, resp);
        view.render(req, resp);
    }
}

Model ์ถ”๊ฐ€

์„œ๋ธ”๋ฆฟ ์ข…์†์„ฑ ์ถ”๊ฐ€

์ปจํŠธ๋กค๋Ÿฌ ์ž…์žฅ์—์„œ HttpServletRequest, HttpServletResponse๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค.
์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ Map์œผ๋กœ ๋„˜๊ธฐ๋ฉด ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ์„œ๋ธ”๋ฆฟ์ด ์•„๋‹ˆ์–ด๋„ ๋™์ž‘ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ทฐ ์ด๋ฆ„ ์ค‘๋ณต ์ œ๊ฑฐ

ํ˜„์žฌ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์ง€์ •ํ•˜๋Š” ๋ทฐ ์ด๋ฆ„์—๋Š” ์ค‘๋ณต(/WEB-INF/views/)์ด ์กด์žฌํ•œ๋‹ค.
์ปจํŠธ๋กค๋Ÿฌ๋Š” ๋ทฐ์˜ ๋…ผ๋ฆฌ ์ด๋ฆ„์„ ๋ฐ˜ํ™˜ํ•˜๊ณ , ๋ฌผ๋ฆฌ ์ด๋ฆ„์€ ํ”„๋ก ํŠธ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์ฒ˜๋ฆฌํ•˜๋„๋ก ๋ณ€๊ฒฝํ•ด๋ณด์ž.
๋…ผ๋ฆฌ์  ๋ฌผ๋ฆฌ์  ์ด๋ฆ„์„ ๊ตฌ๋ถ„ํ•˜๋ฉด ๋‚˜์ค‘์— ๋ณ€๊ฒฝํ•  ๋•Œ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋ณ€๊ฒฝํ•  ์ผ์ด ์—†๋‹ค.

ModelView

Model์„ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•ด์„œ request.setAttribute() ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ณ  ๋ทฐ์— ์ „๋‹ฌํ–ˆ๋‹ค.
์„œ๋ธ”๋ฆฟ์˜ ์ข…์†์„ฑ์„ ์ œ๊ฑฐํ•˜๊ธฐ ์œ„ํ•ด, Model์„ ๋”ฐ๋กœ ๋งŒ๋“ค๊ณ  View ์ด๋ฆ„๊นŒ์ง€ ์ „๋‹ฌํ•˜๋Š” ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ ๋‹ค.

public class ModelView {
    private String viewName;
    private Map<String, Object> model = new HashMap<>();

    public ModelView(String viewName) {
        this.viewName = viewName;
    }

    // ...
}

ControllerV3

public interface ControllerV3 {
    ModelView process(Map<String, String> paramMap);
}
  • ModelView๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ํ•œ๋‹ค.

MemberSaveControllerV3

public class MemberSaveControllerV3 implements ControllerV3 {
    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    public ModelView process(Map<String, String> paramMap) {
        String username = paramMap.get("username");
        int age = Integer.parseInt(paramMap.get("age"));

        Member member = new Member(username, age);
        memberRepository.save(member);

        ModelView mv = new ModelView("save-result");
        mv.getModel().put("member", member);
        return mv;
    }
}
  • ๋…ผ๋ฆฌ ์ด๋ฆ„์„ ModelView ๊ฐ์ฒด์— ๋„ฃ์–ด ์ƒ์„ฑํ•˜๋„๋ก ํ•œ๋‹ค.
  • ๋ชจ๋ธ์— ๋ทฐ์— ํ•„์š”ํ•œ ๊ฐ์ฒด๋ฅผ ๋‹ด๊ณ  ModelView ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

FrontControllerServletV3 ์ผ๋ถ€

@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String requestURI = req.getRequestURI();
        ControllerV3 controller = controllerMap.get(requestURI);
        if (controller == null) {
            resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        // param Map
        Map<String, String> paramMap = createParamMap(req);
        ModelView mv = controller.process(paramMap);

        String viewName = mv.getViewName(); // ๋…ผ๋ฆฌ ์ด๋ฆ„ ex) new-form
        MyView view = viewResolver(viewName);
        view.render(mv.getModel(), req, resp);
    }

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

    private MyView viewResolver(String viewName) {
        MyView view = new MyView("/WEB-INF/views/" + viewName + ".jsp");
        return view;
    }
}
  • createParamMap() : HttpServletRequest์˜ ํŒŒ๋ผ๋ฏธํ„ฐ ์ •๋ณด๋ฅผ Map์œผ๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค.
  • viewResolver() : ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ๋ฐ˜ํ™˜ํ•œ ๋…ผ๋ฆฌ ๋ทฐ ์ด๋ฆ„์„ ์‹ค์ œ ๋ฌผ๋ฆฌ ๋ทฐ ๊ฒฝ๋กœ๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค.
  • view.render(mv.getModel(), req, resp) : ๋ทฐ ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด HTML ํ™”๋ฉด์„ ๋ Œ๋”๋งํ•œ๋‹ค.

MyView

public void render(Map<String, Object> model, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    modelToRequestAttribute(model, req);
    RequestDispatcher dispatcher = req.getRequestDispatcher(viewPath);
    dispatcher.forward(req, resp);
}

๋‹จ์ˆœํ•˜๊ณ  ์‹ค์šฉ์ ์ธ ์ปจํŠธ๋กค๋Ÿฌ

์ปจํŠธ๋กค๋Ÿฌ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด์„œ ํ•ญ์ƒ ModelView ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•˜๋Š” ๋ถ€๋ถ„์ด ๋ฒˆ๊ฑฐ๋กญ๋‹ค.
์ปจํŠธ๋กค๋Ÿฌ๋Š” View์˜ ์ด๋ฆ„๋งŒ ๋ณ€๊ฒฝํ•˜๋„๋ก, ์‹ค์šฉ์„ฑ ์žˆ๋„๋ก ๋ณ€๊ฒฝํ•œ๋‹ค.

ControllerV4

public interface ControllerV4 {
    /**
     *
     * @param paramMap
     * @param model
     * @return viewName
     */

    String process(Map<String, String> paramMap, Map<String, Object> model);
}
  • view ์ด๋ฆ„์„ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๋ณ€๊ฒฝํ•œ๋‹ค.

####

public class MemberSaveControllerV4 implements ControllerV4 {
    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    public String process(Map<String, String> paramMap, Map<String, Object> model) {
        String username = paramMap.get("username");
        int age = Integer.parseInt(paramMap.get("age"));
        Member member = new Member(username, age);
        memberRepository.save(member);
        model.put("member", member);
        return "save-result";
    }
}
  • Controller์—์„œ๋Š” ํ•„์š”ํ•œ ๊ฐ’์„ ๊บผ๋‚ด๊ณ , ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ํ˜ธ์ถœํ•œ ํ›„ viewName๋งŒ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

FrontControllerServletV4

@WebServlet(name = "frontControllerServletV4", urlPatterns = "/front-controller/v2/*")
public class FrontControllerServletV4 extends HttpServlet {
    private Map<String, ControllerV4> controllerMap = new HashMap<>();

    public FrontControllerServletV4(Map<String, ControllerV4> controllerMap) {
        controllerMap.put("/front-controller/v2/members/new-form",
                new MemberFormControllerV4());
        controllerMap.put("/front-controller/v2/members/save",
                new MemberSaveControllerV4());
        controllerMap.put("/front-controller/v2/members",
                new MemberListControllerV4());
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String requestURI = req.getRequestURI();
        ControllerV4 controller = controllerMap.get(requestURI);
        if (controller == null) {
            resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        // param Map
        Map<String, String> paramMap = createParamMap(req);
      	// model 
        Map<String, Object> model = new HashMap<>();
        String viewName = controller.process(paramMap, model);

        MyView view = viewResolver(viewName);
        view.render(model, req, resp);
    }

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

    private MyView viewResolver(String viewName) {
        MyView view = new MyView("/WEB-INF/views/" + viewName + ".jsp");
        return view;
    }
}
  • ํ”„๋ก ํŠธ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ model์„ ๋งŒ๋“ค๊ณ  ๊ฐ Controller์— paramMap๊ณผ model์„ ๋„˜๊ฒจ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ณ , viewName์„ ๋ฐ˜ํ™˜๋ฐ›๋Š”๋‹ค.
  • modelView์—์„œ model์„ ๊บผ๋‚ผ ํ•„์š”๊ฐ€ ์—†๋‹ค.

ํ•˜์ง€๋งŒ, ์ง€๊ธˆ ControllerV4 ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ œํ•œํ•˜๊ณ  ์žˆ๋‹ค.


์œ ์—ฐํ•œ Controller - ์–ด๋Œ‘ํ„ฐ ํŒจํ„ด

์ง€๊ธˆ๊นŒ์ง€์˜ ํ”„๋ก ํŠธ ์ปจํŠธ๋กค๋Ÿฌ๋Š” ํ•œ๊ฐ€์ง€ ์ธํ„ฐํŽ˜์ด์Šค๋งŒ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ–ˆ์—ˆ๋‹ค.
์–ด๋– ํ•œ ์ปจํŠธ๋กค๋Ÿฌ๋„ ํ˜ธํ™˜์ด ๊ฐ€๋Šฅํ•˜๋„๋ก ์–ด๋Œ‘ํ„ฐ ํŒจํ„ด์„ ์‚ฌ์šฉํ•œ๋‹ค.

  • ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ : ์ค‘๊ฐ„์— ์–ด๋Œ‘ํ„ฐ ์—ญํ• ์„ ํ•˜๋Š” ์–ด๋Œ‘ํ„ฐ๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ๋‹ค. ์ด ๋•๋ถ„์— ๋‹ค์–‘ํ•œ ์ข…๋ฅ˜์˜ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๊ธฐ์กด์—๋Š” ํ”„๋ก ํŠธ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ๋ฐ”๋กœ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ํ˜ธ์ถœํ•˜์˜€์ง€๋งŒ, ์ด์ œ๋Š” ์–ด๋Œ‘ํ„ฐ๋ฅผ ํ†ตํ•ด์„œ ํ˜ธ์ถœํ•œ๋‹ค.
  • ํ•ธ๋“ค๋Ÿฌ : ์ปจํŠธ๋กค๋Ÿฌ์˜ ์ข€ ๋” ๋„“์€ ๋ฒ”์œ„. ์–ด๋Œ‘ํ„ฐ๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ปจํŠธ๋กค๋Ÿฌ ๊ฐœ๋… ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ํ•ด๋‹นํ•˜๋Š” ์–ด๋Œ‘ํ„ฐ๋งŒ ์žˆ์œผ๋ฉด ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

MyHandlerAdapter

public interface MyHandlerAdapter {
    boolean supports(Object handler);
    ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException;
}
  • supports(Object handler) : ์–ด๋Œ‘ํ„ฐ๊ฐ€ ํ•ด๋‹น ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ๋ฐ˜ํ™˜
  • handle(...) : ์–ด๋Œ‘ํ„ฐ๋Š” ์‹ค์ œ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ๊ฒฐ๊ณผ๋กœ ModelView๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • ๋งŒ์•ฝ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ModelView๋ฅผ ๋ฐ˜ํ™˜ํ•˜์ง€ ๋ชปํ•˜๋ฉด, ์–ด๋Œ‘ํ„ฐ๊ฐ€ ์ง์ ‘ ์ƒ์„ฑํ•œ๋‹ค.
  • ํ”„๋ก ํŠธ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ์•„๋‹Œ ์–ด๋Œ‘ํ„ฐ๊ฐ€ ์‹ค์ œ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

ControllerV3HandlerAdapter

public class ControllerV3HandlerAdapter implements MyHandlerAdapter {
    @Override
    public boolean supports(Object handler) {
        return (handler instanceof ControllerV3);
    }

    @Override
    public ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
        ControllerV3 controller = (ControllerV3) handler;
        Map<String, String> paramMap = createParamMap(request);
        ModelView mv = controller.process(paramMap);
        return mv;
    }

    private Map<String, String> createParamMap(HttpServletRequest request) {
        Map<String, String> paramMap = new HashMap<>();
        request.getParameterNames().asIterator()
                .forEachRemaining(paramName -> paramMap.put(paramName,
                        request.getParameter(paramName)));
        return paramMap;
    }
}
  • supports()๋กœ ControllerV3 ํƒ€์ž…์ธ์ง€ ํ™•์ธํ•˜๊ณ  ๋งž๋‹ค๋ฉด ํ˜ธ์ถœํ•œ๋‹ค.

####

@WebServlet(name = "frontControllerServletV5", urlPatterns = "/front-controller/v5/*")
public class FrontControllerServletV5 extends HttpServlet {
    private final Map<String, Object> handlerMappingMap = new HashMap<>();
    private final List<MyHandlerAdapter> handlerAdapters = new ArrayList<>();

    public FrontControllerServletV5() {
        initHandlerMappingMap();
        initHandlerAdapters();
    }

    private void initHandlerMappingMap() {
        handlerMappingMap.put("/front-controller/v5/v3/members/new-form",
                new MemberFormControllerV3());
        handlerMappingMap.put("/front-controller/v5/v3/members/save",
                new MemberSaveControllerV3());
        handlerMappingMap.put("/front-controller/v5/v3/members",
                new MemberListControllerV3());
    }

    private void initHandlerAdapters() {
        handlerAdapters.add(new ControllerV3HandlerAdapter());

    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // ์š”์ฒญ ์ •๋ณด๋กœ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.
        Object handler = getHandler(req);

        if (handler == null) {
            resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        // ์–ด๋Œ‘ํ„ฐ๋ฅผ ์ฐพ๋Š”๋‹ค.
        MyHandlerAdapter adapter = getHandlerAdapter(handler);

        ModelView mv = adapter.handle(req, resp, handler);

        String viewName = mv.getViewName(); // ๋…ผ๋ฆฌ ์ด๋ฆ„ ex) new-form
        MyView view = viewResolver(viewName);
        
        view.render(mv.getModel(), req, resp);
    }

    private MyHandlerAdapter getHandlerAdapter(Object handler) {
        for (MyHandlerAdapter adapter : handlerAdapters) {
            if (adapter.supports(handler)) {
                return adapter;
            }
        }
        throw new IllegalArgumentException("handler adapter๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. handler " + handler);
    }

    private Object getHandler(HttpServletRequest req) {
        String requestURI = req.getRequestURI();
        Object handler = handlerMappingMap.get(requestURI);
        return handler;
    }

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

    private MyView viewResolver(String viewName) {
        MyView view = new MyView("/WEB-INF/views/" + viewName + ".jsp");
        return view;
    }
}
  • ์ƒ์„ฑ์ž : ํ•ธ๋“ค๋Ÿฌ ๋งคํ•‘๊ณผ ์–ด๋Œ‘ํ„ฐ๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค.
  • ๋งคํ•‘ ์ •๋ณด์ธ handlerMappingMap ์ด Object Map์œผ๋กœ ์•„๋ฌด ํƒ€์ž…์ด๋‚˜ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.
  • ํ•ธ๋“ค๋Ÿฌ ๋งคํ•‘
  • ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์–ด๋Œ‘ํ„ฐ ์กฐํšŒ
  • ์–ด๋Œ‘ํ„ฐ ํ˜ธ์ถœ

ControllerV4HandlerAdapter ์˜ˆ์ œ

public class ControllerV4HandlerAdapter implements MyHandlerAdapter {
    @Override
    public boolean supports(Object handler) {
        return (handler instanceof ControllerV4);
    }

    public ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
        ControllerV4 controller = (ControllerV4) handler;
        Map<String, String> paramMap = createParamMap(request);
        HashMap<String, Object> model = new HashMap<>();
      
        //  viewName๋งŒ ๋ฐ˜ํ™˜ํ•  ๊ฒฝ์šฐ ModelView ๊ฐ์ฒด๋กœ ๋งŒ๋“ค์–ด์ฃผ๋Š” ์—ญํ• ๋„ ํ•œ๋‹ค.
        String viewName = controller.process(paramMap, model);

        ModelView mv = new ModelView(viewName);
        mv.setModel(model);
        return mv;
    }

    private Map<String, String> createParamMap(HttpServletRequest request) {
        Map<String, String> paramMap = new HashMap<>();
        request.getParameterNames().asIterator()
                .forEachRemaining(paramName -> paramMap.put(paramName,
                        request.getParameter(paramName)));
        return paramMap;
    }
}
  • ์–ด๋Œ‘ํ„ฐ๊ฐ€ ํ˜ธ์ถœํ•˜๋Š” ControllerV4๋Š” viewName๋งŒ ๋ฐ˜ํ™˜ํ•˜๋Š”๋ฐ, ์ด๋•Œ ์–ด๋Œ‘ํ„ฐ์—์„œ ์ด๋ฅผ ModelView ๊ฐ์ฒด๋กœ ๋งŒ๋“ค์–ด ๋ฐ˜ํ™˜ํ•œ๋‹ค.

ํ”„๋ก ํŠธ ์ปจํŠธ๋กค๋Ÿฌ ์ž…์žฅ์—์„œ๋Š” ํ•ธ๋“ค๋Ÿฌ ์–ด๋Œ‘ํ„ฐ์—๋งŒ ์˜์กดํ•˜๊ณ  ์žˆ๋‹ค.

์ด MVC ํ”„๋ ˆ์ž„ ์›Œํฌ๋Š” ์—ญํ• ๊ณผ ๊ตฌํ˜„์ด ๋ถ„๋ฆฌ๋˜์–ด ์žˆ๋‹ค.
๋ชจ๋“  ๊ฒƒ์„ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๋งŒ๋“ค๊ณ  ๊ตฌํ˜„ํ•˜๊ณ  ์‹ถ์€ ๋ถ€๋ถ„๋งŒ ์ฃผ์ž…ํ•˜๋ฉด ์™„๋ฒฝ! (์Šคํ”„๋ง MVC๋Š” ๋ทฐ์™€ ๋ทฐ๋ฆฌ์กธ๋ฒ„ ๋‹ค ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๊ตฌํ˜„๋˜์–ด์žˆ๋‹ค.) ์–ด๋–ค ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ์–ด๋„ ์šฐ๋ฆฌ๋Š” ํ•ธ๋“ค๋Ÿฌ์–ด๋Œ‘ํ„ฐ๋งŒ ๊ตฌํ˜„ํ•ด์ฃผ๋ฉด ๋œ๋‹ค!

ex) @RequestMapping()

RequestMappingHandlerAdapter : ์œ„ ์–ด๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ฒ˜๋ฆฌํ•ด์ฃผ๋Š” ์–ด๋Œ‘ํ„ฐ