ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • MVC 이해해보자
    스프링 2022. 12. 24. 14:32

    자바 서블릿으로만 회원을 등록하는 예제를 만들어 보자.

    package com.example.servletmvc.servlet;
    
    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.io.PrintWriter;
    
    @WebServlet(name = "memberFormServlet", urlPatterns = "/servlet/members/new-form")
    public class MemberFormServlet extends HttpServlet {
        @Override
        protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println("hi");
            response.setContentType("text/html");
            response.setCharacterEncoding("utf-8");
            PrintWriter w = response.getWriter();
            w.write("<!DOCTYPE html>\n" +
                    "<html>\n" +
                    "<head>\n" +
                    " <meta charset=\"UTF-8\">\n" +
                    " <title>Title</title>\n" +
                    "</head>\n" +
                    "<body>\n" +
                    "<form action=\"/servlet/members/save\" method=\"post\">\n" +
                    " username: <input type=\"text\" name=\"username\" />\n" +
                    " age: <input type=\"text\" name=\"age\" />\n" +
                    " <button type=\"submit\">전송</button>\n" +
                    "</form>\n" +
                    "</body>\n" +
                    "</html>\n");
        }
    }

    보기만 해도 매우 막연하다.

    HTML에 있는 코드를 Java로 일일히 다 쳐야한다? 이건정말,,,

    페이지가 더 많아 질 수록 이 과정들은 유지 보수가 절대로 되지 않는다.

    따라서 View 즉, 화면만을 관리 해주는 부분을 분리 시키도록 한다.

     

     

    new-form.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
    <form action="/jsp/members/save.jsp" method="post">
        username: <input type="text" name="username" />
        age: <input type="text" name="age" />
        <button type="submit">전송</button>
    </form>
    </body>
    </html>

    save.jsp

    <%@ page import="com.example.servletmvc.repository.MemberRepository" %>
    <%@ page import="com.example.servletmvc.domain.Member" %>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    
    <%
        MemberRepository memberRepository = MemberRepository.getInstance();
        String username = request.getParameter("username");
        int age = Integer.parseInt(request.getParameter("age"));
    
        Member member = new Member(username, age);
        memberRepository.save(member);
    
    %>
    
    <html>
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
    성공
    <ul>
        <li>id=<%=member.getId()%></li>
        <li>username=<%=member.getUsername()%></li>
        <li>age=<%=member.getAge()%></li>
    </ul>
    <a href="/index.html">메인</a>
    </body>
    </html>

    members.jsp

    <%@ page import="com.example.servletmvc.repository.MemberRepository" %>
    <%@ page import="com.example.servletmvc.domain.Member" %>
    <%@ page import="java.util.List" %>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%
        MemberRepository memberRepository = MemberRepository.getInstance();
        List<Member> members = memberRepository.findAll();
    %>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <a href="/index.html">메인</a>
    <table>
        <thead>
        <th>id</th>
        <th>username</th>
        <th>age</th>
        </thead>
        <tbody>
        <%
            for (Member member : members) {
                out.write(" <tr>");
                out.write(" <td>" + member.getId() + "</td>");
                out.write(" <td>" + member.getUsername() + "</td>");
                out.write(" <td>" + member.getAge() + "</td>");
                out.write(" </tr>");
            }
        %>
        </tbody>
    </table>
    </body>
    </html>

     

     

    하지만,,,

    서블릿으로 개발할 때는 뷰(View)화면을 위한 HTML을 만드는 작업이 자바 코드에 섞여서 지저분하고 복잡했다. JSP를 사용한 덕분에 뷰를 생성하는 HTML 작업을 깔끔하게 가져가고, 중간중간 동적으로 변경이 필요한 부분에만 자바 코드를 적용했다. 그런데 이렇게 해도 해결되지 않는 몇가지 문제점이 있다.

     

    회원 저장 JSP를 보자. 코드의 상위 절반은 회원을 저장하기 위한 비즈니스 로직이고, 나머지 하위 절반만 결과를 HTML로 보여주기 위한 뷰 영역이다.

    회원 목록의 경우에도 마찬가지다. 코드를 잘 보면, JAVA 코드, 데이터를 조회하는 리포지토리 등등 다양한 코드가 모두 JSP에 노출되어 있다. JSP가 너무 많은 역할을 한다.
    또한 코드의 볼륨이 너무 커져버린다.

    따라서 MVC를 이용하여 이를 해결한다.

     

    비즈니스 로직은 서블릿 처럼 다른곳에서 처리하고, JSP는 목적에 맞게 HTML로 화면(View)을 그리는 일에 집중하도록 하자.

     

    MVC를 사용해보자

    MVC는 보여주는것, 조종한는것을 아예 분리시킨다. 따라서 비즈니스 로직은 서블릿처럼 다른 곳에서 처리하고, JSP는 목적에 맞게 view를 그리는 일에 집중시킨다.

     

    하나의 서블릿이나, JSP만으로 비즈니스 로직, 뷰 ,랜더링 모두 처리하게 되면 너무 많은 역할을 하게된다. 비즈니스 로직을 호출하는 부분에 변경이 발생해도 해당코드를 손대야되고, UI를 변경을 해도 비즈니스 로직이 함께 있는 해당 코드를 손대야 한다.

    • 변경 라이프사이클 둘 사이에 라이프 사이클이 다르다는게 중요하다. UI와 비즈니스 로직을 수정하는 것은 다르게 발생할 가능성이 매우 높다.
      이렇게 라이프 사이클이 다른 부분을 하나의 코드로 관리하는 것은 유지보수 하기에 좋지 않다.
    • 특히 JSP같은 뷰 템플릿은 화면을 랜더링 하는데 최적화 되어있어서 이부분의 업무만 담당하는 것이 효과적이다.

    MVC!

    MVC패턴은 하나의 서블릿, JSP로 처리하던 것을 controller와 view라는 영역으로 서로 역할을 나눈것을 말한다.

    • 컨트롤러: HTTP 요청을 받아서 파라미터를 검증하고, 비즈니스 로직을 실행한다. 그리고 뷰에 전달할 결과 데이터를 조회해서 모델에 담는다.
    • 모델: 뷰에 출력할 데이터를 담아둔다. 뷰가 필요한 데이터를 모두 모델에 담아서 전달해주는 덕분에 뷰는 비즈니스 로직이나 데이터 접근을 몰라도 되고, 화면을 렌더링 하는 일에 집중할 수 있다.
    • 뷰: 모델에 담겨있는 데이터를 사용해서 화면을 그리는 일에 집중한다. 여기서는 HTML을 생성하는 부분을 말한다

     

     

     

    고객이 비즈니스로직을 요청하면 controller에서 비즈니스로직을 수행을하고, Model에 data를 담는다.

    그리고 Veiw 로직을 실행한다(View에 제어권을 넘긴다).

    View 로직이 Model에 있는 data를 참고해서 응답이 나가게 된다.

     

    컨트롤러에 비즈니스 로직을 둘수도 있지만, 이렇게되면 컨트롤러가 너무 많은 역할을 담당한다.
    그래서 일반적으로 비즈니스로직은 Service라는 계층을 별도로 만들어서 처리한다.
    그리고 컨트롤러는 비즈니스 로직이 있는 서비스를 호출하는 담당을 한다.

     

    MvcMemberSaveServlet.java

    package com.example.servletmvc.servletMvc;
    
    import com.example.servletmvc.domain.Member;
    import com.example.servletmvc.repository.MemberRepository;
    import jakarta.servlet.RequestDispatcher;
    import jakarta.servlet.ServletException;
    import jakarta.servlet.ServletRequest;
    import jakarta.servlet.ServletResponse;
    import jakarta.servlet.annotation.WebServlet;
    import jakarta.servlet.http.HttpServlet;
    
    import java.io.IOException;
    
    @WebServlet(name = "mvcMemberSaveServlet", urlPatterns = "/servlet-mvc/members/save")
    public class MvcMemberSaveServlet extends HttpServlet {
        private MemberRepository memberRepository = MemberRepository.getInstance();
        @Override
        public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
            String viewPath = "/WEB-INF/views/save.jsp";
            RequestDispatcher requestDispatcher = request.getRequestDispatcher(viewPath);
            String username = request.getParameter("username");
            int age = Integer.parseInt(request.getParameter("age"));
    
            Member member = new Member(username, age);
            memberRepository.save(member);
    
            request.setAttribute("member", member);
            requestDispatcher.forward(request, response);
        }
    }

    save.jsp

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

    컨트롤러에서 앞서 언급한 비즈니스 로직을 처리 하도록 한다.

    필요한 데이터들을 setAttribute를 통하여 모델에 담고 View에서 해당 모델에 담겨 있던 데이터들을꺼내서 동적으로 랜더링을 한다.

     

     참고로 Model은 HttpServletRequest 객체를 사용한다. request는 내부에 데이터 저장소를 가지고 있는데, request.setAttribute() , request.getAttribute() 를 사용하면 데이터를 보관하고, 조회할 수 있다.

    RequestDispatcher는 컨트롤러에서 뷰로 이동할때 사용되는 것이다.

     

    위와같이 만든  MVC는 단점들이 많다.

    MVC 컨트롤러의 단점 포워드 중복 View로 이동하는 코드가 항상 중복 호출되어야 한다.

    물론 이 부분을 메서드로 공통화해도 되지만, 해당 메서드도 항상 직접 호출해야 한다. 

    또한 viewPath가 중복 적이기 때문에 다른 템플릿 엔진이 들어가버리면 코드를 전체적으로 수정해야 한다.

    728x90

    '스프링' 카테고리의 다른 글

    스프링 MVC  (0) 2022.12.26
    MVC 만들어보기  (0) 2022.12.25
    로깅  (0) 2022.12.23
    빈스코프  (0) 2022.12.22
    의존관계 자동주입  (2) 2022.12.22

    댓글

Designed by Tistory.