Back-End/Spring

[Spring MVC][서블릿, JSP, MVC 패턴] - 서블릿, JSP, MVC 패턴

얄루몬 2022. 3. 9. 16:58

💻본 포스팅은 '스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 - 김영한'님의 강의를 듣고 작성되었습니다.

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1/dashboard

 

스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 - 인프런 | 강의

웹 애플리케이션을 개발할 때 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 MVC의 핵심 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., -

www.inflearn.com


[서블릿]

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet(name = "memberSaveServlet", urlPatterns = "/servlet/members/save")
public class MemberSaveServlet extends HttpServlet {

    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("MemberSaveServlet.service");
        String username = request.getParameter("username");
        int age = Integer.parseInt(request.getParameter("age"));

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

        response.setContentType("text/html");
        response.setCharacterEncoding("utf-8");
        PrintWriter w = response.getWriter();
        w.write("<html>\n" +
                "<head>\n" +
                " <meta charset=\"UTF-8\">\n" +
                "</head>\n" +
                "<body>\n" +
                "성공\n" +
                "<ul>\n" +
                " <li>id="+member.getId()+"</li>\n" +
                " <li>username="+member.getUsername()+"</li>\n" +
                " <li>age="+member.getAge()+"</li>\n" +
                "</ul>\n" +
                "<a href=\"/index.html\">메인</a>\n" +
                "</body>\n" +
                "</html>");
    }
}
  • 서블릿을 통해 자바코드에 HTML을 쓰는 예제 코드입니다.
  • 이 경우 HTML 코드를 전부 작성하기 위해서 위와 같은 방식으로 진행해야 하는데 이 방식은 매우 불편하기 때문에 이를 해결하기 위해서 JSP라는 템플릿 엔진을 사용해 HTML  코드에 자바를 쓰는 형식을 진행해볼 것입니다.

[JSP]

<%@ page import="java.util.List" %>
<%@ page import="hello.servlet.domain.member.MemberRepository" %>
<%@ page import="hello.servlet.domain.member.Member" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    MemberRepository memberRepository = MemberRepository.getInstance();
    List<Member> members = memberRepository.findAll();
%>
<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>
    <%
         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>
  • 이 경우는 저장된 회원 리스트를 조회하면 보여주는 형식으로 HTML 코드에 자바를 넣어 사용하는 방식인 JSP 문법을 사용해 작성한 코드입니다.
  • <% ~ %>를 사용하면 자바 코드를 넣어 사용할 수 있습니다.(자바 코드 입력 가능)
  • <%=  %>를 사용하면 자바 코드를 출력할 때 사용할 수 있습니다.

[JSP의 한계]

  • 그러나 JSP의 경우 view 부분과 서비스 로직 부분이 섞여 사용되기 때문에 유지보수가 곤란다하는 단점이 있습니다. 
  • view 부분(UI)이 변경될 경우 UI부분만 변경을 해야하는데 이 경우엔 이 일이 쉽지 않다. 반대로 서비스 로직의 변경에 UI 변경이 불가피할 때도 많다. 
  • 결과적으로 JSP에 서비스로직과 UI적 부분이 섞여 사용할 땐 유지 보수가 어려워 이를 권장하지 않고 이 때 MVC 패턴을 적용한 방안이 해결 방안으로 등장하게 된다.

[MVC 패턴]

package hello.servlet.web.servletmvc;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "mvcMemberFormServlet", urlPatterns = "/servlet-mvc/members/new-form")
public class MvcMemberFormServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
         String viewPath = "/WEB-INF/views/new-form.jsp";
         RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
         dispatcher.forward(request, response);
    }
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="save" method="post">
    username: <input type="text" name="username" />
    age: <input type="text" name="age" />
    <button type="submit">전송</button>
</form>
</body>
</html>
  • View와 Controller Service Model 등을 각각 기능에 맞게 나누어 사용한다.
  • 이경우엔 서블릿을 Controller로 JSP를 view로 사용하고 있다. 이때 model은 HttpServletRequest 객체를 사용한다.

[MVC 패턴 이전]

  • 비지니스 로직과 뷰 로직을 같이 사용해 호출하면 한 번에 나오게 했다.
    • 대표적인 예시가 JSP(템플릿 엔진)과 자바 코드를 함께 사용하는 것
    • 대표적인 예시2가 서브릿 안에(자바코드 안에) HTML 코드를 함께 섞어 쓰는 것

[MVC 패턴 적용]

  • Controller - 서비스 호출을 담당(이때 컨트롤러가 비지니스 로직을 포함하고 있는 일은 바람직하지 않음.)
  • Service / Repository - 비지니스 로직으로 데이터를 접근할 때 이를 사용한다. (서블릿이 사용되는 곳이기도 하다. HTTP 스펙을 다 처리해주고 우리는 필요한 비지니스 로직을 구현하면 된다.)
  • Model - 데이터를 전달하고 데이터를 참조할 때 사용하며 servlet에 request는 내부 데이터 저장소를 가져 이를 사용하여 데이터 전달과 참조가 가능하다.
    • request.setAttribute(), request.getAttribute()
  • View - 화면을 그리는 역할을 한다. (UI라고 생각하면 된다.)
[참고]
절대경로 - 폼 전송시 save가 가야하는 경로라면 save가 호출되고 
상대경로 - 폼 전송시 save가 가야하는 경로라면 현재 URL이 속한 계층 경로 + save가 호출된다.

[MVC의 한계]

그러나 MVC에도 한계가 있다. 

  • 사용하지 않는 코드
    • HttpServletRequest request, HttpServletResponse response 의 코드를 사용할 때도 있고 사용하지 않을 때도 있다.
  • 코드의 중복
    • 이를 해결하기 위해 메서드로 공통화해도 되지만 이때도 메서드를 항상 호출해야 하기에 번거롭다.
  • 공통 처리가 어렵다.
    • 기능이 복잡해질 수록 컨트롤러에서 공통으로 처리해야 하는 부분이 점점 더 많이 증가할 것이다. 단순히 공통 기능을 메서드로 뽑으면 될 것 같지만, 결과적으로 해당 메서드를 항상 호출해야 하고, 실수로 호출하지 않으면 문제가 될 것이다. 그리고 호출하는 것 자체도 중복이다.
    • 이를 해결하기 위해서는 수문장 역할을 하는 프론트 컨트롤러 패턴을 도입해야 해결할 수 있다. (입구를 하나로 만드는 작업을 말한다.)
    • 스프링 MVC의 핵심은 바로 이 프론트 컨트롤러에 존재한다.