Servlet/JSP 답변형 게시판 만들기 - 게시글 목록
개발 순서
- VO
- DAO 테스트
- DAO
- Service
- Controller
- View
com.coco.vo.BoardVO
테이블 이름과 동일하게 변수를 선언한다.
public class BoardVO {
private int bno;
private int p_bno, level; //level은 답글의 개수다.
private String title, content, imgName, id;
private Date regdate;
...Getter Setter toString 생성자 생략...
level은 답글의 개수를 나타내는 칼럼이다.
실제로 존재하지 않는 컬럼이지만 쿼리로 직접 만들어서 쓴다.
com.test.dao.BoardDAOTest
게시물 목록을 불러오는 쿼리를 테스트한다.
public class BoardDAOTest {
private final static Logger log = Logger.getGlobal();
private static final String DRIVER = "com.mysql.cj.jdbc.Driver";
private static final String URL = "jdbc:mysql://127.0.0.1:3306/servletex?serverTimezone=Asia/Seoul";
private static final String USER = "servlet";
private static final String PW = "1234";
@Test
public void getListTest() throws ClassNotFoundException {
List<BoardVO> list = new ArrayList<>();
Class.forName(DRIVER);
String sql = "SELECT CASE WHEN LEVEL-1 > 0 then CONCAT(CONCAT(REPEAT(' ', level - 1),''), t.title)\r\n" +
" ELSE t.title\r\n" +
" END AS title\r\n" +
" , t.bno\r\n" +
" , t.p_bno\r\n" +
" ,t.content\r\n" +
" ,t.id\r\n" +
" ,regdate\r\n" +
" , fnc.level\r\n" +
" FROM\r\n" +
" (SELECT fnc_hierarchy() AS id, @level AS level\r\n" +
" FROM (SELECT @start_with:=0, @id:=@start_with, @level:=0) vars\r\n" +
" JOIN t_board\r\n" +
" WHERE @id IS NOT NULL) fnc\r\n" +
" JOIN t_board t ON fnc.id = t.bno";
log.info(sql);
try( //try with resources
Connection conn = DriverManager.getConnection(URL, USER, PW);
PreparedStatement pstmt = conn.prepareCall(sql);
ResultSet rs = pstmt.executeQuery();
) {
log.info("conn : " + conn);
log.info("pstmt : " + pstmt);
assertNotNull(conn);
while(rs.next()) {
int level = rs.getInt("level");
int bno = rs.getInt("bno");
int p_bno = rs.getInt("p_bno");
String title = rs.getString("title");
String content = rs.getString("content");
String id = rs.getString("id");
Date regdate = rs.getDate("regdate");
BoardVO vo = new BoardVO(bno, p_bno, level, title, content, id, regdate);
list.add(vo);
assertNotNull(vo);
log.info("vo : " + vo);
} // while
} catch (Exception e) {
e.printStackTrace();
}
}
}
테스트 결과
BoardDAO
테스트 코드와 약간 다르다.
JNID사용
package com.coco.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import com.coco.vo.BoardVO;
public class BoardDAO {
private final static Logger log = Logger.getGlobal();
DataSource ds;
public BoardDAO() {
try {
Context ctx = new InitialContext();
Context envCtx = (Context)ctx.lookup("java:/comp/env");
ds = (DataSource)envCtx.lookup("jdbc/mysql");
} catch (NamingException e) {
e.printStackTrace();
}
} //BoardDAO()
public List<BoardVO> getList() {
List<BoardVO> list = new ArrayList<>();
String sql = "SELECT CASE WHEN LEVEL-1 > 0 then CONCAT(CONCAT(REPEAT(' ', level - 1),''), t.title)\r\n" +
" ELSE t.title\r\n" +
" END AS title\r\n" +
" , t.bno\r\n" +
" , t.p_bno\r\n" +
" ,t.content\r\n" +
" ,t.id\r\n" +
" ,regdate\r\n" +
" , fnc.level\r\n" +
" FROM\r\n" +
" (SELECT fnc_hierarchy() AS id, @level AS level\r\n" +
" FROM (SELECT @start_with:=0, @id:=@start_with, @level:=0) vars\r\n" +
" JOIN t_board\r\n" +
" WHERE @id IS NOT NULL) fnc\r\n" +
" JOIN t_board t ON fnc.id = t.bno";
log.info(sql);
try( //try with resources
Connection conn = ds.getConnection();
PreparedStatement pstmt = conn.prepareCall(sql);
ResultSet rs = pstmt.executeQuery();
) {
while(rs.next()) {
int level = rs.getInt("level");
int bno = rs.getInt("bno");
int p_bno = rs.getInt("p_bno");
String title = rs.getString("title");
String content = rs.getString("content");
String id = rs.getString("id");
Date regdate = rs.getDate("regdate");
BoardVO vo = new BoardVO(bno, p_bno, level, title, content, id, regdate);
list.add(vo);
} // while
return list;
} catch (Exception e) {
e.printStackTrace();
}
return null;
} //getList()
}
com.coco.BoardService
public interface BoardService {
List<BoardVO> getList();
}
public class BoardServiceImpl implements BoardService {
private BoardDAO dao;
public BoardServiceImpl() {
dao = new BoardDAO();
}
@Override
public List<BoardVO> getList() {
return dao.getList();
}
}
com.coco.filter.SetFilter
모든 요청과 응답이 해당 필터를 거치도록 만들었다.
컨트롤러에서 직접 인코딩 해주지 않아도 된다.
@WebFilter("/*")
public class SetFilter implements Filter {
public void init(FilterConfig fConfig) throws ServletException {
System.out.println("set Encoding");
System.out.println("set ContentType");
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("utf-8");
chain.doFilter(request, response);
response.setContentType("text/html;charset=utf-8");
}
public void destroy() {
}
}
com.coco.controller.BoardController
//'/board' 경로로 들어오는 모든 요청은 이 컨트롤러를 거친다.
@WebServlet("/board")
public class BoardController extends HttpServlet {
private final static Logger log = Logger.getGlobal();
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doHandle(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doHandle(request, response);
}
private void doHandle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//서비스 - 컨트롤러 연결
BoardService boardService = new BoardServiceImpl();
String nextPage = ""; //이동할 페이지
String action = request.getPathInfo(); //어떤 url을 요청했는가?
log.info("action : " + action);
/* 클라이언트가 요청한 URL이 '/board' 또는 '/board/list'일 경우
* service에서 게시글 목록을 불러오는 메서드를 바인딩 해서 화면으로 보낸다.
*/
if(action == null || action == "/list") {
request.setAttribute("boardList", boardService.getList());
nextPage="list";
}
RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/board/"+nextPage+".jsp");
dispatcher.forward(request, response);
}
}
/WEB-INF/board/list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>게시판</title>
<style>
.cls1 {
text-decoration:none;
}
.cls2 {
text-align:center;
font-size:30px;
}
</style>
</head>
<body>
<h1 align='center'>자유 게시판</h1>
<table align='center' border='1' width='80%'>
<tr height='10' align='center' bgcolor='lightgreen'>
<td>번호</td>
<td>제목</td>
<td>작성자</td>
<td>작성일</td>
</tr>
<c:choose>
<c:when test='${boardList == null}'>
<td colspan="4">
<p align="center">
<b><span style='font-size=9pt;'>등록된 글이 없습니다.</span></b>
</p>
</td>
</c:when>
<c:when test="${boardList != null }">
<c:forEach var="post" items="${boardList }" varStatus="postNum">
<tr align="center">
<td width="5%">${postNum.count }</td>
<td width="10%">${post.id }</td>
<td align='left' width="35%">
<span style='padding-right:30px'></span>
<c:choose>
<c:when test="${post.level > 1 }">
<c:forEach begin="1" end="${post.level }">
<span style='padding-left:20px'></span>
</c:forEach>
<span style='font-size:12px;'>[답변]</span>
<a class="cls1" href='/board/post?no=${post.bno }'>${post.title }</a>
</c:when>
<c:otherwise>
<a class='cls1' href='/board/post?no=${post.bno }'>${post.title }</a>
</c:otherwise>
</c:choose>
</td>
<td width="10%">
<fmt:formatDate value="${post.regdate }"/>
</td>
</tr>
</c:forEach>
</c:when>
</c:choose>
</table>
<a class='cls1' href='#'><p class='cls2'>글쓰기</a>
</body>
</html>
출력하는 태그를 하나씩 뜯어보면
- 게시글이 하나도 없을 경우 등록된 글이 없다고 출력한다.
<c:choose> <c:when test='${boardList == null}'> <td colspan="4"> <p align="center"> <b><span style='font-size=9pt;'>등록된 글이 없습니다.</span></b> </p> </td> </c:when>
- 게시글이 있을 경우, <c:forEach>를 사용해서 목록을 출력한다.
- var : 변수 이름
- itemts : 바인딩된 이름 (request.setAttribute("boardList",list);
- varStatus : varStatus의 속성에 접근하기 위한 변수명
자세한 JSTL 사용법은 링크
<c:when test="${boardList != null }">
<c:forEach var="post" items="${boardList }" varStatus="postNum">
<tr align="center">
<td width="5%">${postNum.count }</td>
<td width="10%">${post.id }</td>
<td align='left' width="35%">
<span style='padding-right:30px'></span>
2번에는 두 가지 경우가 있다. 답글이 있는 경우와 없는 경우.
- test="${post.level > 1 }" 가 true면 답글이 있는 것이다.
style속성으로 들여쓰기를 넣어서 [답글]임을 명시한다.<c:choose> <c:when test="${post.level > 1 }"> <c:forEach begin="1" end="${post.level }"> <span style='padding-left:20px'></span> </c:forEach> <span style='font-size:12px;'>[답변]</span> <a class="cls1" href='/board/post?no=${post.bno }'>${post.title }</a> </c:when>
답글이 없는 경우는 그냥 출력한다.
<c:otherwise>
<a class='cls1' href='/board/post?no=${post.bno }'>${post.title }</a>
</c:otherwise>
fmt태그로 날짜 출력
<fmt:formatDate value="${post.regdate }"/>
최종
'Servlet-JSP > Servlet-JSP 답변형 게시판 만들기' 카테고리의 다른 글
Servlet/JSP 답변형 게시판 만들기 - 수정 (0) | 2020.09.11 |
---|---|
Servlet/JSP 답변형 게시판 만들기 - 조회 (0) | 2020.09.11 |
게시물 등록 (0) | 2020.09.11 |
[문제점]MySQL에서 계층형 쿼리와 그 외... (0) | 2020.09.11 |
구조 및 세팅 (0) | 2020.09.11 |