이번 글에서는 Servlet이 컨트롤러를 담당하고 JSP는 뷰만 담당하는 모델2로 방명록 애플리케이션을 만들어보도록 하겠습니다.
이전글 모델1과 비교하는 것이 가장 큰 목적입니다.
1. 방명록 개요
모델2의 흐름은 다음과 같습니다.
- 모든 요청은 Servlet에서 받습니다.
- DAO를 통해 DB에 접근해서 요청을 처리한 후, 클라이언트에게 JSP로 응답합니다.
- 여기에 forward라는 개념이 있는데, 이는 뒤에서 살펴보도록 하겠습니다.
프로젝트 구성은 모델1과 같습니다.
- 방명록을 작성하는 입력 form 페이지
- 게시글 리스트 페이지
- 삭제하기 위해 비밀번호를 입력하는 페이지
모델1과 모델2로 똑같은 애플리케이션을 구현하는 이유는 모델1이 왜 불편한지, Servlet이 왜 중요한지를 알기 위함입니다.
디렉토리 구조는 조금 변경이 있지만, guestbook 테이블은 그대로입니다.
2. 디렉토리 구조 살펴보기
- 패키지에 대해
- Contoller 패키지를 만든 이유는 여러 요청에 대한 Servlet을 만들어서 처리하기 위함입니다.
- 애플리케이션의 규모가 커지면 방명록에 대한 요청 뿐만 아니라, 회원가입, 로그인 등 여러가지 요청을 처리하게 될텐데, 그 때 LoginServlet , SignupSerlvet 등 Servlet 파일을 저장하기 위해서입니다.
- 또 테이블 매핑을 위한 VO , DAO 객체들을 모아두기 위해 VO, DAO 패키지도 있습니다.
- Web-Content - views 폴더에 대해
- JSP 파일은 WebContent에 직접 위치하지 않고, WEB-INF 폴더 하위의 views폴더에 위치합니다.
- JSP 파일이 WEB-INF 폴더 하위에 위치하게 되면, 위부에서 직접 접근을 할 수 없게 됩니다. ( 보안을 위해 )
애플리케이션 내부에서는 WEB-INF 폴더에 접근할 수 있으므로, Servlet이 요청을 처리한 후 응답할 페이지를 WEB-INF 폴더에 접근하여 응답하는 구조입니다.
이제 모델2로 방명록 애플리케이션을 구현해보도록 하겠습니다.
GuestBookDAO , GuestBookVO는 이전 글과 내용이 모두 같습니다.
그럴 수 밖에 없는 것이 guestbook 테이블은 변함이 없고, 애플리케이션이 수행하는 서비스도 변함이 없기 때문입니다.
GuestBookDAO와 GuestBookVo 클래스는 코드가 길기 때문에 이전 글을 참고해주시기 바랍니다. ( 링크 )
3. GuestBookServlet
먼저 Serlvet 클래스를 작성해보도록 하겠습니다.
GuestBookServlet.java
package Controller;
import java.io.IOException;
import java.util.List;
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 DAO.GuestBookDAO;
import VO.GuestBookVO;
@WebServlet("/gb")
public class GuestBookServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public GuestBookServlet() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
String actionName = request.getParameter("a");
if("deleteform".equals(actionName)) {
RequestDispatcher rd = request.getRequestDispatcher("/WEB-INF/views/deleteform.jsp");
rd.forward(request, response);
}
else if("add".equals(actionName)) {
request.setCharacterEncoding("utf-8");
String name = request.getParameter("name");
String pwd = request.getParameter("pwd");
String content = request.getParameter("content");
GuestBookVO vo = new GuestBookVO();
vo.setName(name);
vo.setPwd(pwd);
vo.setContent(content);
GuestBookDAO dao = new GuestBookDAO();
dao.insert(vo);
response.sendRedirect("/guestbook2/gb");
}
else if("delete".equals(actionName)) {
request.setCharacterEncoding("utf-8");
Integer no = Integer.parseInt(request.getParameter("no"));
String Inputpwd = request.getParameter("pwd");
GuestBookDAO vo = new GuestBookDAO();
String dbPwd = vo.getPwd(no);
String parseInputPwd = vo.getInputPwd(Inputpwd);
if( dbPwd.equals(parseInputPwd)){
vo.delete(no);
}
response.sendRedirect("/guestbook2/gb");
}
else {
GuestBookDAO dao = new GuestBookDAO();
List<guestbookvo> list = dao.getList();
request.setAttribute("list", list);
RequestDispatcher rd = request.getRequestDispatcher("/WEB-INF/views/index.jsp");
rd.forward(request, response);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
Servlet 클래스에서 짚고 넘어가야 할 부분이 2가지가 있습니다.
- 1) actionName
- reqeust.getParameter() 메서드는 요청 파라미터 값을 얻을 수 있는 메서드입니다. ( get 방식, post 방식 둘 다 얻을 수 있음 )
- 예를 들어, 클라이언트가 localhost:8080/guestbook2/gb?a=add 으로 요청을 했다면, actionName의 값은 add가 됩니다.
- 이렇게 파라미터 값을 받는 이유는 Servlet에게 어떤 요청인지 알려줘서 각 요청에 맞게 처리를 나누기 위해서입니다.
- 그래서 보시는 바와 같이 actionName 값에 따라 if - else if - else 로 처리되고 있는 것을 볼 수 있습니다.
- 사용자가 URI 파라미터 a에 이상한 값을 넣을 경우 또는 기본 값으로 처리하기 위한 action은 else{ } 부분에 처리했습니다.
- 뒤에서 JSP 파일을 작성할 때 보시겠지만 <a> 태그이든 <form> 태그이든 URI 파라미터 a의 값을 넘겨줍니다. 따라서 Servlet은 적절한 처리를 수행할 수 있게 됩니다.
- 물론 RESTful하게 localhost:8080/guestbook2/gb/add와 같이 작성해도 됩니다.
- 하지만 URL을 파싱하는 작업이 번거로워서 간단하게 애플리케이션을 구성하고자, 파라미터로 action 이름을 넘겨주는 방식을 사용했습니다.
- 2) forward와 redirect
- redirect는 HTTP 응답 코드로 302를 클라이언트에 넘겨줘서, 해당 URL로 다시 요청 하도록 합니다.
- 즉, 모든 자원을 반환한 상태로 새롭게 요청을 하는 것이죠.
- 반면 forward는 요청 받은 자원을 그대로 유지한 채로 제어권을 넘기는 request의 연장성입니다.
- 즉, Servlet에서 요청을 처리한 후 그 자원을 그대로 유지한채 JSP에 넘겨줘서 JSP가 이를 사용할 수 있도록 하는 메서드입니다.
- Servlet이 컨트롤러 역할을 할 수 있게 하는 핵심 메서드라고 할 수 있습니다.
- 여기서 주의 할 점은 JSP 파일이 /WEB-INF 폴더 내부에 있으므로 외부에 보이지 않습니다.
- 따라서 redirect() 메서드를 호출할 때, 인자로 /WEB-INF 폴더로 시작하는 경로명을 작성하는 것이 아닌 URL을 작성해야 합니다.
이 두 가지 외의 코드는 DAO 객체의 메서드를 실행하는 것이 전부입니다.
보시는 바와 같이 Servlet은 요청을 받아서 처리를 하고 forward 또는 redirect 하는 역할을 담당합니다.
4. JSP - 방명록 쓰기 / 리스트
이제 JSP 파일을 구현해보도록 하겠습니다.
먼저 방명록을 쓰거나 리스트를 보여주는 페이지입니다.
index.jsp
<%@page import="VO.GuestBookVO"%>
<%@page import="java.util.List"%>
<%@page import="java.util.ArrayList"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%
List<GuestBookVO> list = (List<GuestBookVO>)request.getAttribute("list");
%>
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>방명록</title>
</head>
<body>
<form action="/guestbook2/gb" method="post">
<input type="hidden" name="a" value="add">
<table border="1" width="500">
<tr>
<td>이름</td><td><input type="text" name="name"></td>
<td>비밀번호</td><td><input type="password" name="pwd"></td>
</tr>
<tr>
<td colspan=4><textarea name="content" cols=60 rows=5></textarea></td>
</tr>
<tr>
<td colspan=4 align=right><input type="submit" VALUE=" 확인 "></td>
</tr>
</table>
</form>
<br>
<% if(list != null){
for(GuestBookVO vo : list){ %>
<table width="510" border="1">
<tr>
<td><%= vo.getNo() %></td>
<td><%= vo.getName() %></td>
<td><%= vo.getRegDate() %></td>
<td><a href="/guestbook2/gb?a=deleteform&no=<%= vo.getNo() %>">삭제</a></td>
</tr>
<tr>
<td><%= vo.getContent() %></td>
</tr>
</table>
<br>
<% } %>
<% } %>
</body>
</html>
- 모델1의 index.jsp와 크게 다른 것은 없는데, <% %> 부분이 줄었다는 것을 확인할 수 있습니다.
- Servlet에서 request.setAttribute("list", list); 메서드를 호출하여, request 객체에 애트리뷰트를 추가했습니다.
- 이 자원을 공유하기 위해 forward() 메서드를 호출했고, index.jsp에 제어권을 넘겨줬습니다.
- 따라서 index.jsp에서는 DAO에 접근할 필요없이 Servlet이 준비해둔 list 객체만 가져와서 쓰기만 하면 됩니다. 이것이 모델2의 핵심입니다.
5. JSP - 삭제
마지막으로 삭제 form을 구현해보겠습니다.
deleteform.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%
String no = request.getParameter("no");
%>
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>방명록</title>
</head>
<body>
<form method="post" action="/guestbook2/gb">
<input type='hidden' name="no" value="<%= no %>">
<input type='hidden' name="a" value="delete">
<table>
<tr>
<td>비밀번호</td>
<td><input type="password" name="pwd"></td>
<td><input type="submit" value="확인"></td>
</tr>
</table>
</form>
<a href="/guestbook2/gb">메인으로 돌아가기</a>
</body>
</html>
- deleteform.jsp은 모델1과 동일합니다.
이제 프로젝트 폴더를 톰캣에 등록하고,
URL에 localhost:8080/guestbook/gb 으로 요청을 보내서 테스트를 해보세요.
참고로 Servlet에서 어노테이션을 /gb으로 정의했기 때문에 guestbook/gb 로 요청합니다.
이상으로 모델2로 방명록 애플리케이션을 만들어 보았습니다.
다음 글에서는 모델1과 모델2를 비교하고, Factory 디자인 패턴으로 모델2를 리팩토링 해보도록 하겠습니다.
'웹 프로그래밍 > JSP & Servlet' 카테고리의 다른 글
[JSP/Servlet] 객체 범위 ( Scope ) (0) | 2018.03.18 |
---|---|
[JSP/Servlet] Servlet 생명주기 ( Life Cycle ) (1) | 2018.03.18 |
[JSP/Servlet] 방명록 애플리케이션(3) - 모델1과 모델2 비교 그리고 Factory 패턴 (0) | 2018.03.18 |
[JSP/Servlet] 방명록 애플리케이션(1) - 모델1 (0) | 2018.03.18 |
[JSP/Servlet] DAO와 VO 그리고 Model2 (2) | 2018.03.18 |
[JSP/Servlet] 서블릿( Servlet ) 개념 (7) | 2018.03.18 |
[Java EE] 톰캣( Tomcat )과 이클립스 연동 (0) | 2018.03.17 |