4. MVC 프레임워크 만들기
2025. 4. 24. 12:48ㆍSpring Framework/스프링 MVC
[목차]
프론트 컨트롤러 패턴이란?
Version 1. 프론트 컨트롤러 도입
Version 2. View 분리
Version 3. Model 추가
Version 4. 단순하고 실용적인 컨트롤러
Version 5. 유연한 컨트롤러 (어댑터 도입)
* 참고로 JSP는 이전 포스팅의 파일 그대로 재사용
프론트 컨트롤러 패턴이란?
- 프론트 컨트롤러 서블릿 하나로 클라이언트의 요청을 받음. 입구를 하나로!
- 프론트 컨트롤러가 요청에 맞는 컨트롤러를 찾아서 호출
- 앞에서 공통적인 처리가 가능
- 프론트 컨트롤러를 제외한 나머지 컨트롤러는 서블릿을 사용하지 않아도 됨
즉, 컨트롤러가 서블릿 기술을 몰라도 작동할 수 있다. - 스프링 웹 MVC의 DispatcherServlet이 FrontController 패턴으로 구현되어 있음
Version 1. 프론트 컨트롤러 도입
기존 구조를 최대한 유지하면서 프론트 컨트롤러를 도입
구조를 변경할 때는 구조 변경에만 신경쓰고 발견되는 세세한 코드 수정은 이후에 따로 하는 것이 좋음
public interface ControllerV1 {
void process (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
}
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);
}
}
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);
}
}
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);
}
}
@WebServlet(name="frontControllerServletV1", urlPatterns = "/front-controller/v1/members/*")
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 {
String requestURI = request.getRequestURI();
ControllerV1 controller = controllerMap.get(requestURI); // *다형성 활용!*
if(controller == null){
response.setStatus(HttpServletResponse.SC_NOT_FOUND); //응답 상태코드 404
return;
}
controller.process(request, response);
}
}
Version 2. View 분리
단순 반복 되는 뷰 로직을 분리

//모든 컨트롤러에서 뷰로 이동하는 부분에 아래와 같은 중복이 있음
String viewPath = "/WEB-INF/views/new-form.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
//MyView 클래스의 render()로 로직 분리
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);
}
}
public interface ControllerV2 {
//void process (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
MyView process (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
}
public class MemberFormControllerV2 implements ControllerV2 {
@Override
public MyView 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);
return new MyView("/WEB-INF/views/new-form.jsp");
}
}
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);
//String viewPath = "/WEB-INF/views/save-result.jsp";
//RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
//dispatcher.forward(request,response);
return new MyView("/WEB-INF/views/save-result.jsp");
}
}
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);
//String viewPath = "/WEB-INF/views/members.jsp";
//RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
//dispatcher.forward(request,response);
return new MyView("/WEB-INF/views/members.jsp");
}
}
@WebServlet(name="frontControllerServletV2", urlPatterns = "/front-controller/v2/members/*")
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 {
System.out.println("FrontControllerServletV2.service");
String requestURI = request.getRequestURI();
ControllerV2 controller = controllerMap.get(requestURI); // *다형성 활용!*
if(controller == null){
response.setStatus(HttpServletResponse.SC_NOT_FOUND); //응답 상태코드 404
return;
}
//controller.process(request, response);
MyView view = controller.process(request, response);
view.render(request, response);
}
}
Version 3. Model 추가
- 컨트롤러의 서블릿 종속성 제거
- 프론트 컨트롤러에서 요청 파라미터 정보를 HttpServletRequest가 아닌 자바의 Map으로 만들어서 컨트롤러에 넘기면
컨트롤러가 서블릿 기술을 몰라도 (서블릿 클래스가 아닌 단순 자바 클래스여도) 동작할 수 있다. - 그리고 컨트롤러에서 request 객체 자체를 Model로 사용하는 대신에 별도의 Model 객체를 만들어서 반환하면 된다.
- 이렇게 하면 구현 코드도 매우 단순해지고, 테스트 코드 작성이 쉽다.
- 프론트 컨트롤러에서 요청 파라미터 정보를 HttpServletRequest가 아닌 자바의 Map으로 만들어서 컨트롤러에 넘기면
- 뷰 이름 중복 제거
- 컨트롤러에서 지정하는 뷰 이름 앞 뒤로 중복이 있음
- 컨트롤러는 뷰의 논리 이름을 반환하고, 실제 물리 위치의 이름은 프론트 컨트롤러에서 처리하도록 단순화 하자.
- 이렇게 해두면 향후 뷰의 폴더 위치가 함께 이동해도 프론트 컨트롤러만 고치면 된다.
뷰의 물리 위치 이름 | 뷰의 논리 이름 |
/WEB-INF/views/new-form.jsp | → new-form |
/WEB-INF/views/save-result.js | → save-result |
/WEB-INF/views/members.jsp | → members |

//기존에 model로 사용하던 request객체를 대신할 HashpMap을 멤버로 갖는 ModelView 클래스 만들기
@Getter @Setter
public class ModelView {
String viewName;
Map<String, Object> model = new HashMap<>();
public ModelView(String viewName){
this.viewName = viewName;
}
}
public interface ControllerV3 {
//MyView process (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
ModelView process(Map<String, String> paramMap);
}
public class MemberFormControllerV3 implements ControllerV3 {
@Override
public ModelView process(Map<String, String> paramMap) {
//return new MyView("/WEB-INF/views/new-form.jsp");
return new ModelView("new-form"); //물리적인 주소는 프론트 컨트롤러에서 일괄적으로 처리한다
}
}
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);
//request.setAttribute("member", member);
//return new MyView("/WEB-INF/views/save-result.jsp");
ModelView mv = new ModelView("save-result");
mv.getModel().put("member", member);
return mv;
}
}
public class MemberListControllerV3 implements ControllerV3 {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public ModelView process(Map<String, String> paramMap) {
List<Member> members = memberRepository.findAll();
//request.setAttribute("members", members);
//return new MyView("/WEB-INF/views/members.jsp");
ModelView mv = new ModelView("members");
mv.getModel().put("members", members);
return mv;
}
}
@WebServlet(name="frontControllerServletV3", urlPatterns = "/front-controller/v3/members/*")
public class FrontControllerServletV3 extends HttpServlet {
private Map<String, ControllerV3> controllerMap = new HashMap<>();
public FrontControllerServletV3() {
//매핑 정보 저장
controllerMap.put("/front-controller/v3/members/new-form", new MemberFormControllerV3());
controllerMap.put("/front-controller/v3/members/save", new MemberSaveControllerV3());
controllerMap.put("/front-controller/v3/members", new MemberListControllerV3());
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("FrontControllerServletV3.service");
String requestURI = request.getRequestURI();
ControllerV3 controller = controllerMap.get(requestURI); // *다형성 활용!*
if(controller == null){
response.setStatus(HttpServletResponse.SC_NOT_FOUND); //응답 상태코드 404
return;
}
//MyView view = controller.process(request, response);
//view.render(request, response);
//1. Request객체의 Attribute로부터 paramMap 만들어서 컨트롤러에 넘기기
Map<String, String> paramMap = requestAttributeToParamMap(request);
ModelView mv = controller.process(paramMap);
//2. 뷰리졸버: 논리이름(mv에 넘어온 viewName)을 물리이름(실제 jsp파일이 있는 viewPath)으로 반환
String viewName = mv.getViewName();
String viewPath = viewResolver(viewName);
//3. jsp 화면으로 포워딩
MyView myView = new MyView(viewPath);
myView.render(mv.getModel(), request, response);
//모델 객체 내부에 담아놓은 데이터를 request에 담아서 jsp에 전달하기 위해
//render()에 모델도 파라미터로 함께 넘겨야한다!
//MyView의 render() 오버로딩 필요
}
private static String viewResolver(String viewName) {
return "/WEB-INF/views/"+ viewName +".jsp";
}
private static Map<String, String> requestAttributeToParamMap(HttpServletRequest request) {
//request attribute -> HashMap으로 변환
Map<String, String> paramMap = new HashMap<>();
request.getParameterNames().asIterator().forEachRemaining(
paramName -> paramMap.put(paramName, request.getParameter(paramName))
);
return paramMap;
}
}
//MyView 클래스에 render() 오버로딩
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);
}
public void render(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//model에 담은 데이터 -> request Attribute에 추가
modelToRequestAttribute(model, request);
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
private static void modelToRequestAttribute(Map<String, Object> model, HttpServletRequest request) {
//model.forEach((key, value) -> request.setAttribute(key, value));
model.forEach(request::setAttribute);
}
}
Version 4. 단순하고 실용적인 컨트롤러
- 구조는 Version 3와 동일
- 단, 컨트롤러에서 ModelView를 직접 생성해서 반환하도록 편리한 인터페이스 제공
- 프론트 컨트롤러에서 모델 객체를 만들어서 컨트롤러에 보내면 컨트롤러는 뷰의 논리이름만 String으로 반환하면 된다.
- 컨트롤러에서는 개발자가 요청, 응답, 포워딩 등에 대해서는 신경쓰지 않고 "최대한 비즈니스 로직만 처리하도록" 고치고 고치는 것
프레임워크가 수고로울 수록 사용자(개발자)가 편해진다!

public interface ControllerV4 {
// ModelView process(Map<String, String> paramMap);
String process(Map<String, String> paramMap, Map<String, Object> model);
}
public class MemberFormControllerV4 implements ControllerV4 {
@Override
public String process(Map<String, String> paramMap, Map<String, Object> model) {
//return new ModelView("new-form");
return "new-form";
}
}
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);
//ModelView mv = new ModelView("save-result");
//mv.getModel().put("member", member);
//return mv;
model.put("member", member);
return "save-result";
}
}
public class MemberListControllerV4 implements ControllerV4 {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
public String process(Map<String, String> paramMap, Map<String, Object> model) {
List<Member> members = memberRepository.findAll();
//ModelView mv = new ModelView("members");
//mv.getModel().put("members", members);
//return mv;
model.put("members", members);
return "members";
}
}
@WebServlet(name="frontControllerServletV4", urlPatterns = "/front-controller/v4/members/*")
public class FrontControllerServletV4 extends HttpServlet {
private Map<String, ControllerV4> controllerMap = new HashMap<>();
public FrontControllerServletV4() {
//매핑 정보 저장
controllerMap.put("/front-controller/v4/members/new-form", new MemberFormControllerV4());
controllerMap.put("/front-controller/v4/members/save", new MemberSaveControllerV4());
controllerMap.put("/front-controller/v4/members", new MemberListControllerV4());
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("FrontControllerServletV4.service");
String requestURI = request.getRequestURI();
ControllerV4 controller = controllerMap.get(requestURI); // *다형성 활용!*
if(controller == null){
response.setStatus(HttpServletResponse.SC_NOT_FOUND); //응답 상태코드 404
return;
}
//Request객체의 Attribute로부터 paramMap 만들기
//Map<String, String> paramMap = requestAttributeToParamMap(request);
//ModelView mv = controller.process(paramMap);
//String viewName = mv.getViewName();
Map<String, String> paramMap = requestAttributeToParamMap(request);
Map<String, Object> model = new HashMap<>(); //모델 객체 만들어서 같이 보내기!**
String viewName = controller.process(paramMap, model);
String viewPath = viewResolver(viewName);
MyView myView = new MyView(viewPath);
myView.render(model, request, response); //모델도 같이 보내기
}
private static String viewResolver(String viewName) {
return "/WEB-INF/views/"+ viewName +".jsp";
}
private static Map<String, String> requestAttributeToParamMap(HttpServletRequest request) {
//request Attribute -> paramMap으로 변환
Map<String, String> paramMap = new HashMap<>();
request.getParameterNames().asIterator().forEachRemaining(
paramName -> paramMap.put(paramName, request.getParameter(paramName))
);
return paramMap;
}
}
Version 5. 유연한 컨트롤러 (어댑터 도입)
- 만약 어떤 개발자는 ControllerV3 방식으로 개발하고 싶고, 어떤 개발자는 ControllerV4 방식으로 개발하고 싶다면 프레임워크 입장에서는 어떻게 해야할까? 프레임워크가 한 가지 방식만 고집하면 다양한 개발자들을 수용할 수 없다.
→ 어댑터 도입하여 프레임워크를 유연하고 확장성 있게 설계! - ControllerV3와 ControllerV4는 매개변수, 리턴값이 모두 다른 완전히 다른 인터페이스이다. 이렇게 다양한 방식의 컨트롤러를 함께 사용할 수 있도록 중간에서 처리, 변환해주는 바로 것이 "어댑터".
public interface ControllerV3 {
ModelView process(Map<String, String> paramMap);
}
public interface ControllerV4 {
String process(Map<String, String> paramMap, Map<String, Object> model);
}

- 핸들러: 컨트롤러의 이름을 더 넓은 범위인 핸들러로 변경했다. 그 이유는 이제 어댑터가 있기 때문에 꼭 컨트롤러의 개념 뿐만 아니라 어떠한 것이든 해당하는 종류의 어댑터만 있으면 다 처리할 수 있기 때문이다.
- 핸들러 어댑터
- 중간에 어댑터 역할을 하는 어댑터가 추가되었는데 이름이 핸들러 어댑터이다.
- 원래는 컨트롤러 호출 및 리턴값 처리를 프론트 컨트롤러에서 담당했으나, 컨트롤러마다 파라미터 및 리턴 값이 제각각이기 때문에 각 컨트롤러에 맞는 어댑터를 각각 생성하여 이 과정을 위임하고 프론트 컨트롤러는 모든 컨트롤러에 공통적인 부분만 수행한다.
- 어댑터는 실제 컨트롤러를 호출하고, 그 결과로 ModelView를 반환하도록 리턴값을 처리하는 역할을 한다.
- 따라서 프레임워크가 여러 핸들러(컨트롤러) 어댑터를 만들어두면 다양한 종류의 핸들러를 호출하여 사용할 수 있게 된다.
public interface MyHandlerAdaptor {
//어댑터가 해당 핸들러(컨트롤러)를 처리할 수 있는지 판단하는 메서드
boolean support(Object handler);
//해당 핸들러의 방식에 맞게 호출 및 리턴값 처리
ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException;
}
public class ControllerV3HandlerAdaptor implements MyHandlerAdaptor {
@Override
public boolean support(Object handler) {
//파라미터로 들어온 핸들러가 ControllerV3의 객체인지 확인
return (handler instanceof ControllerV3);
}
@Override
public ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
//ControllerV3의 process()에 맞게 호출하고 리턴값 처리
//ModelView process(Map<String, String> paramMap);
ControllerV3 controller = (ControllerV3) handler;
//request -> param
Map<String, String> paramMap = requestAttributeToParamMap(request);
ModelView mv = controller.process(paramMap); //호출
return mv; //mv로 반환
}
private static Map<String, String> requestAttributeToParamMap(HttpServletRequest request) {
//request Attribute -> paramMap으로 변환
Map<String, String> paramMap = new HashMap<>();
request.getParameterNames().asIterator().forEachRemaining(
paramName -> paramMap.put(paramName, request.getParameter(paramName))
);
return paramMap;
}
}
public class ControllerV4HandlerAdaptor implements MyHandlerAdaptor {
@Override
public boolean support(Object handler) {
return (handler instanceof ControllerV4);
}
@Override
public ModelView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
//ControllerV4의 process()에 맞게 호출하고 리턴값 처리
//String process(Map<String, String> paramMap, Map<String, Object> model);
ControllerV4 controller = (ControllerV4) handler;
Map<String, String> paramMap = requestAttributeToParamMap(request);
Map<String, Object> model = new HashMap<>();
String viewName = controller.process(paramMap, model);
ModelView mv = new ModelView(viewName);
mv.setModel(model);
return mv; //리턴값 String을 mv로 변환하여 반환
}
private static Map<String, String> requestAttributeToParamMap(HttpServletRequest request) {
//request Attribute -> paramMap으로 변환
Map<String, String> paramMap = new HashMap<>();
request.getParameterNames().asIterator().forEachRemaining(
paramName -> paramMap.put(paramName, request.getParameter(paramName))
);
return paramMap;
}
}
@WebServlet(name="frontControllerServletV5", urlPatterns = "/front-controller/v5/*")
public class FrontControllerServletV5 extends HttpServlet {
private final Map<String, Object> handlerMappingMap = new HashMap<>();
private final List<MyHandlerAdaptor> handlerAdaptors = new ArrayList<>();
public FrontControllerServletV5(){
initHandlerMappingMap(); //핸들러-URL 매핑 정보 초기화
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());
handlerMappingMap.put("/front-controller/v5/v4/members/new-form", new MemberFormControllerV4());
handlerMappingMap.put("/front-controller/v5/v4/members/save", new MemberSaveControllerV4());
handlerMappingMap.put("/front-controller/v5/v4/members", new MemberListControllerV4());
}
private void initHandlerAdapters() {
handlerAdaptors.add(new ControllerV3HandlerAdaptor());
handlerAdaptors.add(new ControllerV4HandlerAdaptor());
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 요청URL에 맞는 핸들러를 찾는다.
Object handler = getHandler(request);
if(handler==null){
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
}
//2. 핸들러에 맞는 핸들러어댑터를 찾는다.
MyHandlerAdaptor adapter = getAdapter(handler);
//3. 어댑터 실행 -> 어댑터를 통해 핸들러를 호출 -> mv 객체를 반환받음
ModelView mv = adapter.handle(request, response, handler);
//4. 뷰 랜더링 호출
String viewPath = viewResolver(mv.getViewName());
MyView myView = new MyView(viewPath);
myView.render(mv.getModel(),request, response);
}
private static String viewResolver(String viewName) {
return "/WEB-INF/views/"+ viewName +".jsp";
}
private MyHandlerAdaptor getAdapter(Object handler) {
//핸들러 어댑터를 저장해놓은 handlerAdaptors 리스트에서
//해당 핸들러를 처리할 수 있는 핸들러 어댑터를 찾는다.
//adapter.supports(handler)를 통해 맞는 어댑터를 찾을 수 있다.
for (MyHandlerAdaptor handlerAdaptor : handlerAdaptors) {
if(handlerAdaptor.support(handler)){
return handlerAdaptor;
}
}
throw new IllegalArgumentException("handler adapter를 찾을 수 없습니다. handler=" + handler);
}
private Object getHandler(HttpServletRequest request) {
//핸들러 매핑 정보인 handlerMappingMap에서 URL에 매핑된 핸들러 객체를 찾아서 반환한다.
String requestURI = request.getRequestURI();
Object handler = handlerMappingMap.get(requestURI);
return handler;
}
}