서블릿만으로 HTML 생성
모든 회원 조회 서블릿 작성
@WebServlet(urlPatterns = "/servlet/members")
public class MemberListServlet extends HttpServlet {
private MemberRepository memberRepository = MemberRepository.getInstance();
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<Member> members = memberRepository.findAll();
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
PrintWriter w = response.getWriter();
w.write("<html>");
w.write("<head>");
w.write(" <meta charset=\"UTF-8\">");
w.write(" <title>Title</title>");
w.write("</head>");
w.write("<body>");
w.write("<a href=\"/index.html\">메인</a>");
w.write("<table>");
w.write(" <thead>");
w.write(" <th>id</th>");
w.write(" <th>username</th>");
w.write(" <th>age</th>");
w.write(" </thead>");
w.write(" <tbody>");
for (Member member : members) {
w.write(" <tr>");
w.write(" <td>" + member.getId() + "</td>");
w.write(" <td>" + member.getUsername() + "</td>");
w.write(" <td>" + member.getAge() + "</td>");
w.write(" </tr>");
}
w.write(" </tbody>");
w.write("</table>");
w.write("</body>");
w.write("</html>");
}
}
- db에 저장된 데이터를 활용하여 HTML을 동적으로 만들기 위해서 서블릿 만을 이용할 경우 위와 같이 String 하나하나 타이핑하여 HTML을 작성해야한다.
단점
- String이기 때문에 오타 확률이 굉장히 높다.
- 비지니스 로직(
memberRepository.findAll()
)과 화면을 뿌리는 역할(w.write()
)가 섞여있다.
- 그래서 동적으로 HTML을 생성할 수 있는 템플릿 엔진을 활용하면 문제를 해결할 수 있다.
- 예) JSP, Thymeleaf, Freemarket, Velocity
JSP로 HTML 생성
JSP 라이브러리 추가
implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'
implementation 'jakarta.servlet:jakarta.servlet-api'
implementation 'jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api'
implementation 'org.glassfish.web:jakarta.servlet.jsp.jstl'
- build.gradle에 위 코드를 추가하고 refresh한다.
모든 회원 조회 JSP 작성
<%@ page import="com.thuthi.servlet.domain.member.Member" %>
<%@ page import="java.util.List" %>
<%@ page import="com.thuthi.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>
<%@ page import="com.thuthi.servlet.domain.member.Member" %>
- Java의 import문과 동일하다.
<% ~ %>
- 이 부분에는 자바 코드를 입력할 수 있다.
<%= ~ %>
- 이 부분에는 자바 코드를 출력할 수 있다.
단점
- Java코드로 HTML을 생성했던 서블릿 방식과 달리 JSP를 사용하면 HTML에서 Java코드를 추가하여 동적으로 HTML을 생성할 수 있다.
- 하지만, 여전히 비지니스 로직(Java코드)와 UI(HTML)코드가 뒤섞여 SRP가 지켜지지않는다.
- MVC 패턴으로 해결해보자.
MVC 패턴
![](https://blog.kakaocdn.net/dn/F18UP/btskQjmA4uv/nbDJ4hWohwL7rNN85gIKL0/img.png)
- Model: 뷰에 출력할 데이터를 담아둔다. 뷰가 필요한 데이터를 모두 모델에 담아서 전달해주는 덕분에 뷰는 비지니스 로직이나 데이터 접근을 몰라도 되고, 화면을 렌더링하는 일에 집중할 수 있다.
- View: 모델에 담겨있는 데이터를 활용해서 화면을 그리는 일에 집중한다. 여기서는 HTML을 생성하는 부분을 말한다.
- Controller: HTTP요청을 받아서 파라미터를 검증하고, 비지니스 로직을 실행하고, 모델에 데이터를 담는 역할을 한다.
- 딱 봐도 Controller의 역할이 많으므로, 비지니스 로직을 실행하는 파트는 Service계층을 따로 만들어서 별도로 처리한다.
- Controller는 Service를 호출하는 역할을 하게 된다.
모든 회원 조회 MVC 패턴으로 작성
@WebServlet(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 viewPath = "/WEB-INF/views/members.jsp";
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
}
}
- WEB-INF폴더는 외부에서 접근할 수 없고, 서버 내부에서만 접근할 수 있는 파일들을 넣을 때 사용되는 폴더다.
RequestDispatcher.forward()
를 사용하면 해당 파일로 요청을 포워딩 시킨다.- redirect: HTTP status code 302로, 웹 브라우저가 실제로 “재요청”을 해서 웹 브라우저의 url자체가 바뀐다.
- forward: 서버 내부에서 재 호출하여 결과를 포워딩 시키기 때문에 웹 브라우저의 url은 그대로다.
<%@ 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>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
- tag 라이브러리를 땡겨오는 코드다.
${}
- request의 attribute에 담긴 데이터를 쉽게 조회할 수 있게 해주는 JSP문법이다.
단점
포워드 중복
RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
dispatcher.forward(request, response);
- view로 포워딩하는 코드가 항상 중복으로 발생한다. 물론, 메서드로 공통화시켜도 되지만, 해당 메서드도 항상 직접 호출해줘야한다는 단점이 있다.
ViewPath에 중복
String viewPath = "/WEB-INF/views/new-form.jsp";
- prefix는
/WEB-INF/views
, suffix는.jsp
로 고정되어 있다.
- 만약 이 prefix나 suffix의 변경이 빌요할 경우, 모든 파일을 다 돌아가면서 변경해야한다.
사용하지 않는 코드
HttpServletResponse response
는 사용되지 않는다.
공통 처리가 어렵다.
- 지금은 굉장히 간단한 로직이라 공통 처리할 부분이 별로 없지만, 실무 프로젝트로가면 공통인 코드가 굉장히 많이 발생하는데, 항상 같은 메서드를 호출해야하고, 실수로 호출하지 않으면 문제가 발생하게 된다.
Uploaded by N2T
(23.06.21 22:15)에 작성된 글 입니다.