article thumbnail image
Published 2022. 1. 24. 12:01
반응형

순서대로 보자면, 발전 순서는 이렇다.
순수 자바로 모든 웹 처리 구현 → 서블릿 → JSP → MVC1 → MVC2
어떤 차이점이 있는지 알아보자.

서블릿


JSP, MVC도 모두 서블릿을 활용한 기술이다. 하지만 서블릿은 개발자가 웹 응답이냐 요청 처리에
관여를 해야하고 동적 HTML 생성도 불편하기 때문에 불편함을 느끼게 되었다. 어떤 것이 불편했을까 살펴 보자.

어떤 것이 불편 했을까

  • 웹 서버 요청, 응답에 대한 처리를 직접 해야 함.
  • 동적 HTML 생성이 아주 불편함.
  • 등 여러가지

아래 코드는 서블릿으로 구현한 회원 가입 기능이다.

보는 바와 같이 브라우저에 응답할 Content-Typer과 같은 정보들을 직접 컨트롤 해야 하고
특히 HTML 처리가 엄청나다. 이런 징그러운 코드를 사용하지 않기 위해 다음 JSP가 도입되게 되었다.

package hello.servlet.web;

import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;

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;
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");

        response.setContentType("text/html");
        response.setCharacterEncoding("utf-8");

        String username = request.getParameter("username");
        int age = Integer.parseInt(request.getParameter("age"));

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

        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>");
    }
}

JSP


서블릿의 단점을 보완하기 위해 탄생했다.
html 작성이 편리해졌다. 하지만 큰 단점이 있었다. 먼저 코드를 살펴 보자.

html과 동일하게 때문에 html코드 작성은 아주 편해진게 확인된다.
하지만 비지니스 로직 또한 jsp 파일에 같이 존재하게 된다. 즉, 한 가지일만 하지 않고, 한 가지 이유에 의해서만 변경이 발생하지 않는다.
SRP를 어기게 되는 코드이다. SRP를 어기게 되면 코드는 유지보수가 어려워진다.
자바 코드를 html 사이에 사용해서 동적 제어도 가능하기에 참 편리해 보인다. 하지만 이는 소스를 점점 꼬이게 만드는 요소가 된다.
그래서 MVC라는 SRP를 지키도록 분리한 패턴이 나오게 되었다.

<%@ page import="hello.servlet.domain.member.Member" %>
<%@ page import="hello.servlet.domain.member.MemberRepository" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    // jsp도 서블릿으로 변환되기 때문에 request, response는 그냥 사용가능.
    MemberRepository memberRepository = MemberRepository.getInstance();
    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);
%>
<html>
<head>
    <title>Title</title>
</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>
<%@ page import="hello.servlet.domain.member.Member" %>
<%@ page import="java.util.List" %>
<%@ page import="hello.servlet.domain.member.MemberRepository" %>
<%@ 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>

MVC(Model View Contoller)


앞에서 많이 개선된 JSP는 너무 많은 역할을 하고 있었다. 그렇기 때문에 나온 패턴이 MVC패턴이다.
즉, jsp는 화면 렌덩링만 서버는 서버일만 하도록 구조를 가져가는 것이다. 조금 더 상세하게 알아 보자.

Controller

  1. HTTP 요청을 받고 처리 후 비지니스 로직을 실행 한다.
  2. 그리고 뷰에 전달할 결과 데이터를 모델에 담는다.
  3. 자바에서 컨트롤러는 서블릿을 사용하게 된다.

Model

뷰에 출력할 데이터를 담아둔다. 뷰에서는 이 모델만 보고 꺼내와서 쓰면 되기 때문에 렌더링에 집중할 수 있다.

View

화면이다.

MVC1과 MVC2

MVC1은 컨트롤러와 비지니스 로직이 섞인 패턴이다.
컨트롤러의 역할과 실제 비지니스 로직을 컨트롤러에서 하는 것이다. 두 가지 역할을 하기 때문에 유지보수에 좋지 않다.

MVC2는 컨트롤러와 비지니스 로직을 분리한 것 뿐이다. 그림을 확인하면 아래와 같다.

출처 : 김영한의 스프링 MVC1편

코드를 살펴 보면 View와 컨트롤러가 분리 되어있다.

setAttribute : 모델에 해당한다. 가공한 데이터를 뷰가 사용하기 편하게 담아두는 곳이다.
forward : 가공한 데이터를 응답하는 과정이다. 하지만 클라이언트로 viewPath를 응답 시키는 것이 아닌
서버 내부에서 처리를 하기 때문에 url은 변경되지 않고 클라이언트도 당연히 인지하지 못한다.
반면 redirect였다면 클라이언트에 해당 경로로 응답이 나가므로 url이 변경되며, 클라이언트도 인지할 수 있다.

package hello.servlet.web.servletmvc;

import hello.servlet.domain.member.Member;
import hello.servlet.domain.member.MemberRepository;

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;
import java.util.List;

@WebServlet(name = "mvcMemberListServlet", urlPatterns = "/servlet-mvc/members")
public class MvcMemberListServlet extends HttpServlet {

    private MemberRepository memberRepository = MemberRepository.getInstance();

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        List<Member> members = memberRepository.findAll();
        request.setAttribute("members", members);

        String viewPaht = "/WEB-INF/views/members.jsp";
        RequestDispatcher dispatcher = request.getRequestDispatcher(viewPaht);
        dispatcher.forward(request, response);
    }
}

members.jsp

// /WEB-INF/views/members.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<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>

WEB-INF

MVC 패턴을 보면 화면 경로에 /WEB-INF라는 경로가 있다. “/”는 절대 경로를 의미하며,
WEB-INF는 클라이언트가 직접 접근할 수 없는 디렉토리를 의미한다.
webapp 밑에 있는 파일은 클라이언트가 localhost:8080/index.html으로 접근이 가능하지만
WEB-INF는 불가능하다. WEB-INF는 규칙이기 때문에 그대로 사용해야 한다.

반응형

'Spring' 카테고리의 다른 글

스프링 MVC 구조 알아보기  (0) 2022.01.24
프론트 컨트롤러 패턴  (0) 2022.01.24
서블릿과 톰캣의 관계  (0) 2022.01.24
서블릿과 동시 요청 처리(멀티 쓰레드)  (0) 2022.01.24
Web Server와 WAS  (0) 2022.01.24
복사했습니다!