Spring

Spring MVC - 김영한 백엔드 (2)

jmboy 2024. 4. 22. 17:56

MVC 프레임워크 만들기

  • V3, V4에 대한 설명
  • 이후 V3,V4에 접근할 어댑터를 생성한 V5 버전에 대한 이해를 풀어내겠습니다.

V3

package hello.servlet.web.frontcontroller.v3;

import hello.servlet.web.frontcontroller.ModelView;
import hello.servlet.web.frontcontroller.MyView;
import hello.servlet.web.frontcontroller.v3.controller.MemberFormControllerV3;
import hello.servlet.web.frontcontroller.v3.controller.MemberListControllerV3;
import hello.servlet.web.frontcontroller.v3.controller.MemberSaveControllerV3;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@WebServlet(name = "frontControllerServletV3", urlPatterns = "/front-controller/v3/*") // * -> v1 하위 모든 요청
public class FrontControllerServletV3 extends HttpServlet {

    private Map<String, ControllerV3> controllerMap = new HashMap<>(); // 요청 URI에 해당하는 controller를 찾기 위한 맵

    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 {

        request.getRequestURI(); // 요청 URI -> new-form

        // get으로 해당하는 controller 찾기
        ControllerV3 controller = controllerMap.get(request.getRequestURI()); // 요청 URI에 해당하는 controller 찾기 -> new form 된다.
        if (controller == null) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);// 404 처리
            return;
        }

//        request.getParameterNames().asIterator()
//                .forEachRemaining(paramName -> System.out.println(paramName + "=" + request.getParameter(paramName)));
        //paramMap
        // form 에 값을 넣었을때.
        Map<String, String> paramMap = createParamMap(request); // 파라미터 값을 다 넣음

        ModelView mv = controller.process(paramMap); // controller 호출 -> model view 반환
        String viewName = mv.getViewName(); // 논리이름 new-form

        MyView view = viewResolver(viewName);// 물리이름 new-form.jsp

        view.render(mv.getModel(), request,response); // view 호출 jsp 호출
    }

    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))); // 파라미터에 있는 값을 다 꺼내서 paramMap에 저장
        return paramMap;
    }
}

new-form → save → list 순서로 넘어갑니다.


  • Frontcontroller에서 v3의 하위 모든 요청을 받아들인다.
  • 이후 요청 URI에 해당하는 controller를 찾아줄때 사용할 맵을 생성한다.
  • CollectionMap에 생성자로 각 URI에 알맞은 controller 를 넣어준다.

공통

  • request.getRequestURI 로 요청 URI 를 가져온다.
  • controllerMap 에서 어떠한 controller 를 사용할지 가져온다.
    • 여기서 맞는 controller 가 없다면 404 처리를 해준다.

new-form

  • param값이 없기 때문에 아무것도 없는 map 이 넘어간다.

controller들이 구현할 interface

package hello.servlet.web.frontcontroller.v3;

import hello.servlet.web.frontcontroller.ModelView;

import java.util.Map;

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

MemberFormController

package hello.servlet.web.frontcontroller.v3.controller;

import hello.servlet.web.frontcontroller.ModelView;
import hello.servlet.web.frontcontroller.v3.ControllerV3;

import java.util.Map;

public class MemberFormControllerV3 implements ControllerV3 {
    @Override
    public ModelView process(Map<String, String> paramMap) {
        return new ModelView("new-form");
    }
}
package hello.servlet.web.frontcontroller;

import lombok.Getter;
import lombok.Setter;

import java.util.HashMap;
import java.util.Map;

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

    public ModelView(String viewName) {
        this.viewName = viewName;
    }
}
  • viewname으로 new-form 이 제공된다.
  • 이후 mv.getViewName 에서 new-form을 가져와 모델과 함께 렌더링한다.
    • 여기서 model은 현재 null 이다.

save

앞의 new-form 의 창이 떴다.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <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>
  • save action 에 의해서 controller 에서 saveController 를 가져온다.
    • request에 username과 age를 담아서 준다.
  • controller 에 필요한 paramMap 을 만들어 준다.
    • 각 파라미터에서 이름과 값을 iterator 로 가져와서 map으로 만들어준다.
    • ex) username : jason, age: 20
package hello.servlet.web.frontcontroller.v3.controller;

import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import hello.servlet.web.frontcontroller.ModelView;
import hello.servlet.web.frontcontroller.MyView;
import hello.servlet.web.frontcontroller.v3.ControllerV3;

import java.util.Map;

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;

    }
}
  • 여기서 memberRepository는 멤버를 담을 수 있는 저장소
  • 앞에서 paramMap에서 username과 age를 담아서 제공해주었다.
  • 해당 paramMap에서 username과 age를 가져와서 Member 객체로 만들어준 후 저장소에 저장한다.
  • 이후 modelview로 save-result 라는 값을 준다.
  • 또한 modelView에 member 값을 넣어서 return 해준다.
  • view.render에서 모델의 값을 가지고 렌더링 한다.
<%--
  Created by IntelliJ IDEA.
  User: kimjimin
  Date: 4/19/24
  Time: 11:47 AM
  To change this template use File | Settings | File Templates.
--%>
<%@ 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">Main</a>

</body>
</html>

list

package hello.servlet.web.frontcontroller.v3.controller;

import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;
import hello.servlet.web.frontcontroller.ModelView;
import hello.servlet.web.frontcontroller.v3.ControllerV3;

import java.util.List;
import java.util.Map;

public class MemberListControllerV3 implements ControllerV3 {

    private MemberRepository memberRepository = MemberRepository.getInstance();
    @Override
    public ModelView process(Map<String, String> paramMap) {
        List<Member> members = memberRepository.findAll();
        ModelView mv = new ModelView("members");
        mv.getModel().put("members", members);
        return mv;
    }
}
  • memberRepository에서 모든 값을 찾아서 List로 만든다.
  • ModelView에 members라는 viewname을 넣어서 생성한다.
  • model이란 Map에 members라는 key에 members 라는 리스트를 넣는다.
  • mv에서 String viewName = mv.getViewName(); 으로 뷰의 이름을 가져오고, 똑같이 렌더링 한다.
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--
  Created by IntelliJ IDEA.
  User: kimjimin
  Date: 4/19/24
  Time: 11:52 AM
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<a href="/index.html">메인 </a>
<table>
    <thead>
    <th>id</th>
    <th>username</th>
    <th>age</th>
    </thead>
    <tbody>
    <c:forEach var="item" items="${members}">
        <tr>
            <td>${item.id}</td>
            <td>${item.username}</td>
            <td>${item.age}</td>
        </tr>
</c:forEach>
    </tbody>
</table>

</body>
</html>
  • 해당 코드에서 members list를 반복문을 돌리면서 id, username, age를 가져와서 출력한다.