Spring template engine jsp 소개 및 mvc 패턴 소개

강정우·2023년 11월 30일
0

Spring-boot

목록 보기
26/73

jsp

  • 사실상 사장된 기능 부트에서는 스프링회사 차원에서도 thymeleaf를 강권하고 밀어주고 있다.
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

</body>
</html>
  • 대충 이렇게 생겼고 가장 최상단에 저게 명시되어있어야 jsp라는 것을 알 수 있다.
<%@ page import="hello.servlet.domain.member.MemberRepository" %>
<%@ page import="hello.servlet.domain.member.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>
  • jsp도 servlet으로 결국은 바뀌기 때문에 request, response, out은 그냥 사용이 가능하다.

결국 servlet과 jsp의 한계

  • 서블릿으로 개발할 땐 java로 html 코드를 짜야해서 IDE의 도움도 못 받고 굉장히 불편함.
    jsp가 이를 해결해줬지만 jsp가 비즈니스~뷰의 영역까지 포함하기 때문에 너무 많은 역할을 하고 있다.

  • 그리고 java코드, 데이터조회로직, 뷰로직 까지 하면 조금만 복잡한 코드가 되면 굉장히 가독성이 떨어질 것이다.
    이는 유지보수가 굉장히 힘들어진다.

  • 진짜 문제는 둘 사이(비즈니스로직과 뷰)의 변경 라이프 사이클이 다르다는 점이다.
    예를들어 "UI를 일부 수정하는 일" 과 "비즈니스 로직을 수정하는 일" 은 각각 다르게 발생할 가능성이 매우 높고 대부분 서로에게 영향을 주지 않는다.
    이렇게 변경의 라이프 사이클이 다른 부분을 하나의 코드로 관리하는 것은 유지보수하기 좋지 않다.

  • 이를 해결하고자 MVC 패턴이 등장한 것이다.
    그래서 비즈니스 로직을 servlet에서 처리하고 jsp는 뷰 역할만 하는 것이다.

mvc 패턴

  • 컨트롤러: 사용자가 호출하는 대상이며 해당 컨트롤러가 사용자 요청이 제대로 된 것인지 HTTP 스펙이 맞는지 등등 을 검사한다.

  • 서비스: 비즈니스 로직이 실행되는 곳 DB접근을 하고 로직을 거쳐 결과를 전달한다.

  • 컨트롤러: 서비스에서 받은 결과값을 모델로 정의하여 값을 넣어서 뷰에 전달함 (reqeust.setAttribute)

  • 뷰: 컨트롤러가 넣어둔 데이터를 컨트롤러가 정의한 모델로 가져와서 뿌려줌. (reqeust.getAttribute)

  • 컨트롤러 아래 코드는 그냥 소규모 예제이기 때문에 따로 서비스 로직 없이 바로 뷰로 보내주는 컨트롤러이다.

@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>
    <title>jsp/servlet으로 mvc 폼 구현</title>
</head>
<body>
<form action="save" method="post">
  username: <input type="text" name="username"/>
  age: <input type="number" step="1" name="age"/>
  <button type="submit">전송</button>
</form>
</body>
</html>
  • 이때 위 form에 action을 그냥 상대경로로 적었다. 이러면 현재 위치에서 마지막 path를 빼고 해당 위치를 +/xxx 이런식으로 교체되는거다. 그래서 해당 url이 호출되는 것이다.

WEB-INF

  • 사용자가 그냥 경로로 들어가지 않고 항상 컨트롤러를 거쳐서 들어가길 원하는 파일들만 넣어두는 곳이다.
    이거는 WAS의 rule이다.
<%@ page import="hello.servlet.domain.member.Member" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
  <title>save-result/jsp/servlet</title>
</head>
<body>
성공
<ul>
  <li>id=${member.id}></li>
  <li>username=${member.username}</li>
  <li>age=<%= ((Member)request.getAttribute("member")).getAge(); %></li>
</ul>
<a href="/index.html">메인</a>
</body>
</html>
  • 그래서 컨트롤러에서 넘어온 값을 이렇게 표현 가능하다 프로퍼티 접근법으로 그냥 필드값을 넣어주면 알아서 .getxxx() 메서드로 바꿔준다는 것이다.

  • 그래고 원래는 getAttribute를 해서 갖고온 값을 캐스팅하여 .getxxx() 해야하는데 표현식을 통해 편리하게 작성할 수 있다.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<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>
  <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>
  • JSTL을 이용한 for문 까지 굉장히 오랜만에 보았다.

jsp/servlet mvc 패턴의 단점

  • 굉장히 반복되는 구조가 많다.
    prefix와 subfix가 항상 붙고 (/WEB-INF/views/, .jsp) 혹시 구조가 바뀐다면 일일이 모든 파일을 다 변경해야한다.

  • 그리고 dispatcher를 가져와 forward하는 코드도 중복이다.

  • response는 거의 쓰이지도 않을 뿐더러 request도 가끔은 안 쓰인다.

  • 테스트 코드 작성이 굉장히 어렵다.

  • 공통 처리가 어렵다.

    • 기능이 복잡해질 수록 컨트롤러에서 공통적으로 처리해야하는 부분이 증가하는데 이를 어떻게 구현하면 좋을까?
    • 바로 프론트 컨트롤러가 처리해주면 된다. 일종의 수문장 역할로 스프링 mvc 도입도 바로 이 프론트 컨트롤러에 있다.

맥 단축키

cmd + e : 이전 파일 위치 목록
ctrl + shift + T : 테스트 파일 즉석 작성

profile
智(지)! 德(덕)! 體(체)!

0개의 댓글