Controller 작성
Controller 작성은 게시판의 기능을 모두 만들어야 하므로 할 일이 많은 부분입니다.
우선 만들어볼 게시판 기본 기능은 아래와 같습니다.
1. 전체 도서 목록 보기 (== 전체 글 목록 보기)
2. 선택한 글 한 개 읽기 (글 상세 보기)
3. 글 작성
4. 글 수정
5. 글 삭제
전체 도서 목록은 list.do로 요청받을 것이고, 글 한 개 읽기는 view.do로 요청받습니다.
글 작성은 write.do로 요청받는데, 글을 작성하는 페이지와 작성한 글을 데이터베이스에 저장하기 위해 넘어가는 페이지가 필요하므로 writeOk.do도 생성합니다. 글 수정 update도 write와 유사합니다.
글 삭제는 글 작성, 글 수정과는 달리 따로 데이터를 입력하는 폼이 필요하지 않으므로 deleteOk.do 만 있으면 됩니다.
위 내용을 페이지 흐름도로 작성해보았습니다. 각 페이지에서 필요로 하는 DML도 함께 적어놓았습니다.
각 기능을 구현하기 위해 앞서 생성했던 BookController 파일을 수정합니다.
package com.controller;
import java.io.IOException;
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 com.command.Command;
@WebServlet("*.do")
public class BookController extends HttpServlet {
private static final long serialVersionUID = 1L;
public BookController() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
actionDo(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
actionDo(request, response);
}
protected void actionDo(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
Command command = null; // 어떠한 로직을 수행할지
String viewPage = null; // 어떠한 페이지를 보여줄지
// URL로부터 command 분리
String uri = request.getRequestURI();
String conPath = request.getContextPath();
String com = uri.substring(conPath.length());
switch(com) {
case "/list.do":
// TODO
break;
case "/view.do":
// TODO
break;
case "/write.do":
// TODO
break;
case "/writeOk.do":
// TODO
break;
case "/update.do":
// TODO
break;
case "/updateOk.do":
// TODO
break;
case "/deleteOk.do":
// TODO
break;
}
// view page로 forward
if (viewPage != null) {
RequestDispatcher dispatcher =
request.getRequestDispatcher("/WEB-INF/views/book/" + viewPage);
dispatcher.forward(request, response);
}
}
}
actionDo() 메서드를 만듭니다.
doGet() 메서드와 doPost() 메서드가 실행될 때 actionDo() 메서드가 실행되게 하여 웹 페이지를 Get 방식으로 접근하든 Post 방식으로 접근하든 actionDo()가 실행되게 합니다.
한글 지원을 위해 request 받은 내용을 UTF-8로 인코딩합니다.
Command 부분은 각 게시판의 기능들을 분리하기 위해 선언하였습니다.
viewPage는 요청에 따라 알맞은 페이지를 보여주기 위해 선언하였습니다.
URL로부터 command 부분을 분리하기 위해 세 가지 변수를 선언했는데 아래 예시 URL을 통해 어떤 식으로 값이 가져오는지 확인해봅니다.
http://localhost:8080/Book/example.do라는 request가 들어왔다고 가정합니다.
uri 부분은 /Book/example.do가 될 것이고,
conPath 부분은 /Book이 될 것이고,
com 부분은 /example.do가 될 것입니다.
사용할 변수는 com 변수인데 com 변수를 구하기 위하여 uri와 ContectType이 필요합니다.
예를 들어 com 변수에 list.do가 들어오면 전체 글 목록을 불러오고, write.do가 들어오면 글 작성 폼으로 가야 합니다.
이제 TODO 부분을 채워 넣어야 합니다. 각 기능에 해당하는 Command들을 만들어보겠습니다.
com.command 패키지를 생성하고 해당 패키지 내에 Command 인터페이스를 생성합니다.
package com.command;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface Command {
void execute(HttpServletRequest request, HttpServletResponse response);
}
앞으로 만들 Command들은 위 interface Command를 구현합니다.
전체 글 목록 보기 구현
ListCommand
전체 글 목록 보기 기능을 수행하는 ListCommand를 구현합니다.
우선 BookController에서 case "/list.do"의 TODO 부분을 다음과 같이 작성합니다.
case "/list.do":
command = new ListCommand();
command.execute(request, response);
viewPage = "list.jsp";
break;
com이 list.do인 경우 ListCommand 객체를 생성하여 execute 메서드를 실행합니다.
그리고 사용자에게 보여줄 View에서는 list.jsp를 띄웁니다.
아직 list.jsp는 만들지 않은 상태입니다.
ListCommand class를 생성하지 않은 상태라 오류가 발생합니다.
코드 번호 왼쪽의 전구 버튼을 누른 뒤 Create class 'ListCommand'를 클릭하면 Class 생성창이 뜹니다.
Package를 com.command로 해준 뒤 Finish를 눌러 클래스를 생성합니다.
package com.command;
import java.sql.SQLException;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.beans.BookDAO;
import com.beans.BookDTO;
public class ListCommand implements Command {
@Override
public void execute(HttpServletRequest request, HttpServletResponse response) {
List<BookDTO> list = null;
try {
list = new BookDAO().select();
} catch (SQLException e) {
e.printStackTrace();
}
request.setAttribute("list", list);
}
}
List<BookDTO>로 list 변수를 선언합니다.
데이터베이스에 저장된 데이터를 읽어와 list에 저장할 것입니다.
트랜잭션 전담은 DAO에서 하기로 했으므로 BookDAO 객체를 생성합니다. 아직 BookDAO에는 select() 메서드를 만들어주지 않았으므로 BookDAO 클래스에 select() 메서드를 만들어줍니다.
select() 메서드를 사용해 데이터베이스에 저장된 도서 목록 정보를 List 형태로 반환하여 list 인스턴스에 저장합니다.
list 인스턴스를 "list"라는 이름으로 request의 attribute에 담습니다. 이후 list.jsp에서 request를 사용할 때 getAttribute() 메서드를 사용해 "list"라는 이름의 Object를 불러올 수 있습니다.
select() 메서드는 ListCommand 클래스를 만들 때와 똑같은 방법으로 만들면 됩니다.
select() 메서드에서 데이터베이스에 저장된 모든 정보를 불러와 ArrayList에 저장하는 기능을 구현합니다.
BookDAO().select(), buildList()
BookDAO 클래스에서 select() 메서드를 작성해봅니다.
private List<BookDTO> buildList(ResultSet rs) throws SQLException {
List<BookDTO> list = new ArrayList<>();
while (rs.next()) {
int uid = rs.getInt("uid");
String title = rs.getString("title");
String summary = rs.getString("summary");
if (summary == null)
summary = "";
int price = rs.getInt("price");
int viewCnt = rs.getInt("viewcnt");
LocalDateTime regDate = rs.getObject("regdate", LocalDateTime.class);
BookDTO dto = new BookDTO(uid, title, summary, price, viewCnt, regDate);
list.add(dto);
}
return list;
}
public List<BookDTO> select() throws SQLException {
List<BookDTO> list = null;
try {
pstmt = conn.prepareStatement(D.SQL_BOOK_SELECT);
rs = pstmt.executeQuery();
list = buildList(rs);
} finally {
close();
}
return list;
}
코드는 BookDAO 클래스에서 새롭게 만든 buildList() 메서드와 select() 메서드만 작성했습니다. 두 메서드 모두 SQL을 사용하므로 번거롭게 try~catch 문을 사용하지 않기 위해 SQLException을 throws 해주었습니다.
쿼리문을 사용해 가져온 ResultSet을 buildList() 메서드의 매개변수로 넣어 메서드를 실행합니다.
buildList() 메서드는 데이터베이스에서 가져온 ResultSet에 담긴 정보들을 BookDTO 타입인 List로 저장하는 메서드입니다.
Query문
우선 데이터베이스에서 어떠한 정보를 가져오는지 살펴보기 위해 D 인터페이스에 있는 SQL_BOOK_SELECT 쿼리문 먼저 만든 뒤 살펴보도록 하겠습니다.
public static final String SQL_BOOK_SELECT =
"SELECT bk_uid uid, bk_title title, bk_summary summary, bk_price price, "
+ "bk_viewcnt viewcnt, bk_regdate regdate " + "FROM book ORDER BY bk_uid DESC";
book 테이블에서 bk_uid, bk_title, bk_summary, bk_price, bk_viewcnt, bk_regdate 데이터를 가져옵니다. 이때 bk_uid를 기준으로 내림차순 정렬하여 SELECT 합니다.
값을 가져올 때 별칭을 사용하여 모두 bk_ 를 떼어낸 형식으로 가져오도록 하였습니다.
따라서 buildList() 메서드에서 ResultSet.getInt()와 ResultSet.getString() 메서드를 사용할 때 인자 값을 별칭으로 넣어도 됩니다.
uid, title, summary, price, viewcnt, regdate 값을 가져와 저장한 뒤, BookDTO 객체를 생성하여 BookDTO 타입으로 dto 인스턴스에 값을 저장합니다.
이후 List<BookDTO> list에 dto를 추가하고, 모든 데이터가 읽어 들여지고 나면 while 문이 종료되며 list를 반환합니다.
따라서 select() 메서드에서 buildList(rs) 메서드의 반환 값은 book 테이블의 모든 정보를 담은 list입니다.
select() 메서드의 반환 값 또한 앞서 buildList(rs) 메서드를 통해 받은 list이므로 ListCommand 클래스의 list에 담기는 값도 book 테이블의 모든 정보일 것입니다.
이 list를 사용하여 전체 도서 목록 보기를 구현합니다.
지금까지 필요한 데이터 값을 불러와 저장까지 하였으니 사용자가 보는 부분(view)에 데이터를 띄워주도록 합시다.
현재까지의 MVC2 패턴은 다음과 같습니다. Model 부분인 DTO, DAO에서 데이터를 관리합니다.
Controller 부분인 Command 부분에서 사용자가 요청하는 것을 처리하는 등의 행위를 합니다.
(현재 ListCommand는 사용자가 전체 도서 목록 보기를 요청한 것을 처리하는 부분인 것입니다.)
마지막으로 앞으로 구현할 View 부분은 jsp를 사용하여 사용자가 요청한 값을 볼 수 있게 할 것입니다.
list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="com.beans.*"%>
<%@ page import="java.util.*"%>
<%
List<BookDTO> list = (List<BookDTO>) request.getAttribute("list");
%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>전체 도서 목록</title>
<style>
table, th, td {
border: 1px solid black;
border-collapse: collapse;
text-align: center;
}
table {
width: 100%;
}
</style>
</head>
<body>
<h1>전체 도서 목록</h1>
<hr><br>
<table>
<tr>
<th>uid</th>
<th>제목</th>
<th>가격</th>
<th>조회수</th>
<th>등록일</th>
</tr>
<%
if (list != null) {
for (BookDTO dto : list) {
%>
<tr>
<td><%=dto.getUid()%></td>
<td><a href="view.do?uid=<%=dto.getUid()%>"><%=dto.getTitle()%></a></td>
<td><%=dto.getPrice()%></td>
<td><%=dto.getViewCnt()%></td>
<td><%=dto.getRegDateTime()%></td>
</tr>
<%
}
}
%>
</table>
<br>
<button onclick="location.href='write.do'">신규 등록</button>
</body>
</html>
com.beans 패키지 내에 있는 BookDTO를 사용해야 하므로 해당 파일을 import 합니다.
java.util.* 패키지 또한 import 합니다.
ListCommand에서 request의 attribute에 list를 담아놓았습니다. 이를 request.getAttribute() 메서드를 사용해 가져와 List<BookDTO> 타입으로 형변환해준 뒤 저장합니다.
html 부분에선 <table>을 만듭니다.
그리고 request의 attribute 값에서 가져온 list가 null이 아닌 경우 for문을 반복하여 <td>를 만듭니다.
<td>에 들어가는 값들은 BookDTO 클래스에 있는 get 메서드를 사용하여 값을 가져오는 것입니다.
이때 title은 <a> 태그로 감쌌는데, 도서 제목을 클릭하는 경우 해당 도서의 상세 정보 보기 페이지로 넘어가기 위해 감싼 것입니다.
상세 정보 보기 페이지는 view.jsp 파일로 만들 것이므로 view.do로 하였습니다. GET 방식으로 도서 고유 uid 값을 가져와 해당 uid에 맞는 도서 상세 정보를 볼 수 있게끔 할 것입니다.
전체 글 목록 보기에서 신규 등록이라는 <button>을 클릭하면 새로운 도서를 등록할 수 있는 write.jsp 페이지로 넘어가게 만들 것입니다. 따라서 <button>을 클릭할 때 이동하는 location.href를 write.do로 설정해두었습니다.
실행 화면
서버를 실행시킨 뒤 localhost:8080:Book/list.do로 접속하면 아래와 같이 전체 도서 목록이 뜨는 걸 볼 수 있습니다.
현재 구현하는 기능만 이해해도 다른 기능 구현이 쉬워집니다 😎
Github
https://github.com/MJKim99/JSP_Board_Book.git