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

나영·2023년 7월 10일

Spring-MVC

목록 보기
3/7
post-thumbnail

회원 관리 웹 애플리케이션을 서블릿, JSP, MVC 패턴 으로 간단하게 만들어보자 !

요구사항

  • 회원 정보

    • 이름 : username

    • 나이 : age

  • 기능 요구사항

    • 회원 저장
    • 회원 목록 조회

서블릿

코드

다음은 단순하게 회원 정보를 입력 (등록)할 수 있는 HTML Form 을 제공하는 MemberFormServlet 코드이다.

package hello.servlet.web.servlet;
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 = "memberFormServlet", urlPatterns = "/servlet/members/newform")
public class MemberFormServlet extends HttpServlet {
 	@Override
   protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
   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 을 마음껏 만들 수 있다.
    (정적인 HTML 문서라면 화면이 계속 달라지는 회원의 저장 결과나 회원 목록 같은 동적인 HTML 을 만드는 일이 불가능할 것)

  • BUT, 코드가 매우 복잡하고 비효율적
    (자바 코드로 HTML 을 만들어내는 것보다 동적으로 변경해야 하는 부분만 자바 코드를 넣을 수 있다면 ,,,ㅜ)

    -> 템플릿 엔진 등장 !! (JSP, Thymeleaf, Velocity 등등)

JSP

코드

jsp 파일의 첫줄은 이렇게 시작해야 한다 !

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
  

다음은 회원 등록 폼을 제공하는 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="hello.servlet.domain.member.MemberRepository" %>
<%@ page import="hello.servlet.domain.member.Member" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
	// request, response 사용 가능
	 MemberRepository memberRepository = MemberRepository.getInstance();
 	System.out.println("save.jsp");
 	String username = request.getParameter("username");
 	int age = Integer.parseInt(request.getParameter("age"));
 	Member member = new Member(username, age);
 	System.out.println("member = " + member);
 	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>

JSP서버 내부에서 서블릿으로 변환되어, 위에서 만든 MemberFormServlet 과 거의 비슷한 모습으로 변환된다.

서블릿과 JSP 의 한계

  • 서블릿뷰 화면을 위한 HTML 을 만드는 작업이 자바 코드에 섞여서 지저분하고 복잡했다.
  • 그러나 JSP 를 사용하면서 뷰를 생성하는 HTML 작업은 깔끔하게 가져가고, 중간중간 동적으로 변경이 필요한 부분에만 자바 코드를 추가하게 되었다.
  • 위의 jsp 코드들을 보면 알 수 있듯이, 절반은 비즈니스 로직, 절반은 영역이다. 즉 JSP너무 많은 역할을 하고 있다. (유지보수 hard ,,)

-> MVC 패턴의 등장 !

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

MVC 패턴

개념

MVC 패턴은 지금까지 위에서 하나의 서블릿이나 하나의 JSP 로 처리하던 것을 컨트롤러 (Controller) 뷰 (View) 라는 영역으로 서로 역할을 나눈 것을 말한다. 웹 애플리케이션은 보통 이 패턴을 사용한다.

  • 컨트롤러 : HTTP 요청을 받아 파라미터 검증, 비즈니스 로직 실행 -> 뷰에 전달할 결과 데이터 조회 후 모델에 담음.
  • 모델 : 뷰에 출력할 데이터를 담아둠. 비즈니스 로직이나 데이터 접근 몰라도 되고, 화면을 렌더링하는 일에 집중 가능 !
  • : 모델에 담겨있는 데이터 -> 화면을 그리는 일 (HTML 생성) 에 집중

💡 구조

MVC 패턴 이전에는 클라이언트가 호출하면 비즈니스 로직과 로직이 각각 따로 이루어졌다.

MVC 패턴이 적용되면 클라이언트 호출에 의해 컨트롤러에서 비즈니스 로직을 수행하고 모델데이터를 전달한다. 그런 후, 모델의 데이터를 참조해 로직을 수행하는 것이다.

컨트롤러에서 너무 많은 역할을 담당하는 것을 방지하기 위해서는 비즈니스 로직에서 서비스 (Service) 계층을 별도로 만들어서 컨트롤러에서 호출해 처리한다.

코드

  • 회원 저장 - 컨트롤러 코드
    • ModelHttpServletRequest 객체 사용
      request 는 내부에 데이터 저장소를 가지고 있기 때문에, request.setAttribute(), request.getAttribute() 를 통해 데이터 보관 및 조회 가능
@WebServlet(name = "mvcMemberSaveServlet", urlPatterns = "/servlet-mvc/members/save")
public class MvcMemberSaveServlet extends HttpServlet {
   private MemberRepository memberRepository = MemberRepository.getInstance();
  
   @Override
   protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     String username = request.getParameter("username");
     int age = Integer.parseInt(request.getParameter("age"));
     Member member = new Member(username, age);
     System.out.println("member = " + member);
     memberRepository.save(member);
  
     // Model에 데이터를 보관한다.
     request.setAttribute("member", member);
  
     String viewPath = "/WEB-INF/views/save-result.jsp";
     RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
     dispatcher.forward(request, response); // 다른 서블릿이나 JSP 로 이동 (서버 내부에서 다시 호출)
   }
}

💡 /WEB-INF

이 경로 안에 JSP 가 있으면 외부에서 직접 JSP 를 호출할 수 없으므로, 항상 컨트롤러를 통해 JSP 호출이 필요 !

💡 Model 에 데이터 보관

위 코드에서는 HttpServletRequestModel 로 사용해 request 가 제공하는 메서드를 사용해 데이터를 보관해 뷰에 전달할 수 있다.

  • setAttribute() : request 객체에 데이터 보관
  • getAttribute() : request 객체로부터 데이터 꺼냄

💡 redirect / forward

  • redirect : 실제 클라이언트에 응답이 나갔다가, 클라리언트가 redirect 경로로 다시 요청하는 것 -> 클라이언트는 인지 가능, URL 경로도 실제로 변경됨.
  • forward : 서버 내부에서 일어나는 호출 -> 클라이언트는 전혀 인지 X
  • 회원 저장 - 코드
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
 	<meta charset="UTF-8">
 	<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>
  

MVC 패턴 한계

MVC 패턴을 통해 컨트롤러의 역할과 뷰 렌더링 역할을 명확하게 구분하며 코드를 깔끔하고 직관적으로 짤 수 있었다.

BUT,

  • forward 중복 : View 로 이동하는 코드가 항상 중복 호출된다.

    RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
    dispatcher.forward(request, response);
  • ViewPath 의 중복 : jsp 말고 다른 뷰로 변경하면 전체 코드 다 변경해야한다는 문제도 발생

    String viewPath = "/WEB-INF/views/new-form.jsp";
  • 사용하지 않는 코드 : 특히 response 는 현재 코드에서 사용되지 X

    HttpServletRequest request, HttpServletResponse response
  • 공통 처리의 어려움 : 컨트롤러에서 공통으로 처리해야하는 부분의 증가 -> 해당 메서드 항상 호출됨 ,,

    -> 컨트롤러 호출 전에 먼저 공통 기능 처리해야 함 ! (Front Controller) - 문지기의 역할로 입구를 하나로 관리 🚪

0개의 댓글