개발/Servlet, JSP

Spring - Servlet + JSP를 이용한 MVC 동작 과정 정리 V3

잇(IT) 2023. 6. 14. 21:47
728x90

- 위와 같은 형태로 MVC 형태의 코드를 작성해 볼 것이다.

- 현재 서버를 기동하고 아무런 정보가 담겨있지 않은 상태이다.

- ~~~/new-form의 URI를 통해 접근했을 때

 

- 아래의 코드는 여러 Controller를 관리하는 앞단에 존재하는 FrontController에 해당한다.

@WebServlet(name = "frontControllerServletV3", urlPatterns = "/front-controller/v3/*")
// URI 요청이 v3 하위 경로로 오면 아래 메서드가 무조건 실행된다.
public class FrontControllerServletV3 extends HttpServlet {

    private Map<String, ControllerV3> controllerMap = new HashMap<>();
    // String과 ControllerV3를 참조형으로 하는 controllerMap이라는 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());
    }
    // 해당 메서드를 통해 Map에 URI, new를 통해 객체가 생성된다.

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("FrontControllerServletV3.service");

        String requestURI = request.getRequestURI();
        // 브라우저에 입력된 URI를 받을 수 있다. 회원 가입을 누르게 되면 new-form의 URI가 requestURI에 저장된다.

        ControllerV3 controller = controllerMap.get(requestURI);
        // Map에 저장된 해당 URI에 대한 value 값을 controller 변수에 넣는다. // 여기서 ControllerV3의 참조형이지만 실제로는 MemberFormControllerV3의 객체를 사용한다.
        if (controller == null) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        // 해당 URI에 대한 value가 없으면 404(SC_NOT_FOUND)를 호출하고 아무것도 반환하지 않는다.

        //paramMap
        Map<String, String> paramMap = createParamMap(request);
        // 매배변수를 request로 하고, request 저장소에 있는 key, value 값을 Map<String, String> 형태로 반환한다.
        // 즉, 회원가입 form을 통해 저장된 파라미터의 key, value값을 반환하는 것이다.
        ModelView mv = controller.process(paramMap);
        // createParamMap을 통해 저장된 파라미터 key, value들을 ModelView를 참조형으로 하는 참조변수 mv에 저장한다.
        // 현재 MemberFormControllerV3의 객체를 사용하고 있다.
        // 현재 위의 코드는 ModelView mv = new ModelView("new-form"); 와 동일하다.


        //new-form
        String viewName = mv.getViewName();//논리 이름 new-form 이것밖에 못 가져온다.
        // /WEB-INF ~~~ 로 만들어진다.
        // 매개변수를 포함한 생성자를 통해 해당 viewName이 new-form으로 전달된다.
        MyView view = viewResolver(viewName);
        //viewResolver라는 메서드를 통해 "/WEB-INF/views/new-form.jsp"의 값이 viewPath로 저장된다.

        view.render(mv.getModel(), request, response);
    }

    private static MyView viewResolver(String viewName) {
        return new MyView("/WEB-INF/views/" + viewName + ".jsp");
    }

    private static 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;
    }
}
@WebServlet(name = "frontControllerServletV3", urlPatterns = "/front-controller/v3/*")

- 위 설정으로 인해 /v3/하위 경로가 호출되면 아래 메서드가 전부 실행된다.

 

- 우선 new-form에서는 별도의 데이터가 저장되어 넘어가지 않기 때문에 request 저장소에 parameter 값으로 꺼낼 값은 현재 없는 상황이다.


<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<!-- 상대경로 사용, [현재 URL이 속한 계층 경로 + /save] -->
<form action="save" method="post">
  username: <input type="text" name="username" />
  age: <input type="text" name="age" />
  <button type="submit">전송</button>
</form>
</body>
</html>

- 위와 같이 username과 age를 입력하게 전송 버튼을 누르게 되면 상대 경로를 통해 /front-controller/v3/members/save로 입력된 정보들이 post를 통해 데이터가 전달된다.


- 데이터를 전달하게 되면 위와 같이 ~~~/save의 URI로 form에 의한 데이터가 전달된다.

@WebServlet(name = "frontControllerServletV3", urlPatterns = "/front-controller/v3/*")
// URI 요청이 v3 하위 경로로 오면 아래 메서드가 무조건 실행된다.
public class FrontControllerServletV3 extends HttpServlet {

    private Map<String, ControllerV3> controllerMap = new HashMap<>();
    // String과 ControllerV3를 참조형으로 하는 controllerMap이라는 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());
    }
    // 해당 메서드를 통해 Map에 URI, new를 통해 객체가 생성된다.

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("FrontControllerServletV3.service");

        String requestURI = request.getRequestURI();
        // 브라우저에 입력된 URI를 받을 수 있다. 회원 가입을 누르게 되면 new-form의 URI가 requestURI에 저장된다.

        ControllerV3 controller = controllerMap.get(requestURI);
        // Map에 저장된 해당 URI에 대한 value 값을 controller 변수에 넣는다. // 여기서 ControllerV3의 참조형이지만 실제로는 MemberFormControllerV3의 객체를 사용한다.
        if (controller == null) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        // 해당 URI에 대한 value가 없으면 404(SC_NOT_FOUND)를 호출하고 아무것도 반환하지 않는다.

        //paramMap
        Map<String, String> paramMap = createParamMap(request);
        // 매배변수를 request로 하고, request 저장소에 있는 key, value 값을 Map<String, String> 형태로 반환한다.
        // 즉, 회원가입 form을 통해 저장된 파라미터의 key, value값을 반환하는 것이다.
        ModelView mv = controller.process(paramMap);
        // createParamMap을 통해 저장된 파라미터 key, value들을 ModelView를 참조형으로 하는 참조변수 mv에 저장한다.
        // 현재 MemberFormControllerV3의 객체를 사용하고 있다.
        // 현재 위의 코드는 ModelView mv = new ModelView("new-form"); 와 동일하다.


        //new-form
        String viewName = mv.getViewName();//논리 이름 new-form 이것밖에 못 가져온다.
        // /WEB-INF ~~~ 로 만들어진다.
        // 매개변수를 포함한 생성자를 통해 해당 viewName이 new-form으로 전달된다.
        MyView view = viewResolver(viewName);
        //viewResolver라는 메서드를 통해 "/WEB-INF/views/new-form.jsp"의 값이 viewPath로 저장된다.

        view.render(mv.getModel(), request, response);
    }

    private static MyView viewResolver(String viewName) {
        return new MyView("/WEB-INF/views/" + viewName + ".jsp");
    }

    private static 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;
    }
}

- 위의 코드를 자세히 분석해보겠다.

 

@WebServlet(name = "frontControllerServletV3", urlPatterns = "/front-controller/v3/*")
// URI 요청이 v3 하위 경로로 오면 아래 메서드가 무조건 실행된다.
public class FrontControllerServletV3 extends HttpServlet {

    private Map<String, ControllerV3> controllerMap = new HashMap<>();
    // String과 ControllerV3를 참조형으로 하는 controllerMap이라는 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());
    }

- 위 코드를 통해 1. ~~~/new-form 2. ~~~/save / 3.~~~/members의 String 값을 key로, new ~~~를 통한 객체를 value로 controllerMap에 HashMap을 통해 저장된다. 

 

@Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("FrontControllerServletV3.service");

        String requestURI = request.getRequestURI();
        // 브라우저에 입력된 URI를 받을 수 있다. 회원 가입을 누르게 되면 new-form의 URI가 requestURI에 저장된다.

- HttpServletRequest를 URI로 전달된 정보들이 담긴다.

https://insoobaik.tistory.com/366 

 

2023.06.14.WED

HttpServletRequest 객체에는 다음과 같은 정보가 포함될 수 있습니다: 요청 URI(Uniform Resource Identifier): 클라이언트가 요청한 리소스의 경로와 파일 이름을 나타냅니다. 요청 URL(Uniform Resource Locator): 클

insoobaik.tistory.com

- HttpServletRequest에 저장되는 정보들은 위와 같다.

 

- 현재 ~~~/save의 URI가 requestURI 변수에 담긴다.

 

ControllerV3 controller = controllerMap.get(requestURI);
        // Map에 저장된 해당 URI에 대한 value 값을 controller 변수에 넣는다. // 여기서 ControllerV3의 참조형이지만 실제로는 MemberFormControllerV3의 객체를 사용한다.
        if (controller == null) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        // 해당 URI에 대한 value가 없으면 404(SC_NOT_FOUND)를 호출하고 아무것도 반환하지 않는다.

- controller 변수에 ~~~/save URI가 담긴다.

- 없을 경우 SC_NOT_FOUNT = 404 메서드가 응답 데이터에 저장된다.

 

//paramMap
        Map<String, String> paramMap = createParamMap(request);
        // 매배변수를 request로 하고, request 저장소에 있는 key, value 값을 Map<String, String> 형태로 반환한다.
        // 즉, 회원가입 form을 통해 저장된 파라미터의 key, value값을 반환하는 것이다.
        ModelView mv = controller.process(paramMap);
        // createParamMap을 통해 저장된 파라미터 key, value들을 ModelView를 참조형으로 하는 참조변수 mv에 저장한다.
        // 현재 MemberFormControllerV3의 객체를 사용하고 있다.
        // 현재 위의 코드는 ModelView mv = new ModelView("new-form"); 와 동일하다.
        
        
.......

private static 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;

- paramMap을 컬렉션 변수에 createParamMap메서드를 통해 반환되는 paramMap이 저장된다.

- createParamMap은 request를 통해 전달된 파라미터의 key, value 값들이 paramMap에 저장된다.

- ModelView를 참조형으로 하는 mv 참조 변수는 process(paramMap)메서드를 실행하게 되는데 현재 controller 참조 변수는

controllerMap.put("/front-controller/v3/members/save", new MemberSaveControllerV3());

에 의해 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;
    }
}

- 위의 process 메서드는 paramMap에 담겨있는 각, key에 대한 value 값들을 뽑아내는 작업을 한다.

- 변수를 통해 저장한 value 값들을 member 객체를 통해 Member 저장소에 해당하는 memberRepository에 저장한다.

- mv 참조변수에는 "save-result"의 viewName을 저장하고, getModel을 통해 member라는 key값에 memberRepository에 저장된 member 객체를 저장하고, 최종적으로 mv 객체를 반환한다.

 

username : baek
age : 29

- paramMap에 위의 정보가 저장되어 있을 것이다.

 

//new-form
        String viewName = mv.getViewName();//논리 이름 new-form 이것밖에 못 가져온다.
        // /WEB-INF ~~~ 로 만들어진다.
        // 매개변수를 포함한 생성자를 통해 해당 viewName이 new-form으로 전달된다.
        MyView view = viewResolver(viewName);
        //viewResolver라는 메서드를 통해 "/WEB-INF/views/new-form.jsp"의 값이 viewPath로 저장된다.

        view.render(mv.getModel(), request, response);
    }
    
    ...
    
    private static MyView viewResolver(String viewName) {
        return new MyView("/WEB-INF/views/" + viewName + ".jsp");
    }
    
    ---------------------
    
    public String getViewName() {
        return viewName;
    }
    
    ---------------------
    
    public void render(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // view.render(mv.getModel(), request, response); 해당 값들이 넘어온다. getModel을 통해 입력된 파라미터 key, value 값들이 HashMap으로 저장되어 넘어온다.
        modelToRequestAttribute(model, request);
        // 해당 메서드를 통해 JSP로 데이터를 넘기기 위해 request.setAttribute를 통해 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));
    }

- mv의 viewName 변수에는 "save-result"가 담겨있다.

- viewResolver를 통해 "/WEB-INF/views/save-result.jsp"의 값이 view 객체의 viewName 값에 저장된다.

- mv.getModel를 통해 process() 메서드를 통해 저장된 "member"의 객체 "member"를 호출한다.

- modelToRequestAttribute(model, request) 메서드를 통해 model객체 담긴 key, value값을 request 저장소에 저장한다. setAttribute를 통해 저장하게되면 jsp에서 해당 데이터들을 사용할 수 있다.

- 현재 request 저장소에는 key = id, value = 1 / key = username, value = 29 / key = age, value = 29의 값이 저장되어 있다.

- 해당 데이터를 viewPath("/WEB-INF/views/save-result.jsp") jsp로 연결되며, dispatcher.forward(request, response)를 통해 해당 저장소에 저장된 데이터들이 jsp 파일로 전달된다.

 

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
  <title>Title</title>
</head>
<body>
성공
<ul>
  <li>id=${member.id}</li>
  <li>username=${member.username}</li>
  <li>age=${member.age}</li>
</ul>
<a href="/index.html">메인</a>
</body>
</html>

- 위 jsp 페이를 통해 해당 URI에 대해 아래와 같은 화면을 보여준다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

출처 : 인프런 - 우아한 형제들 기술이사 김영한의 스프링 완전 정복 (스프링 핵심원리 - 기본 편)

728x90