Bigfat

[Web] 리스트 화면 만들기 (게시판 구현) 본문

Java/web

[Web] 리스트 화면 만들기 (게시판 구현)

kyou 2017. 4. 6. 16:36

브라우저로 게시판 리스트 화면을 출력해보자

  저장한 게시글을 리스트로 출력하고, 글쓰기 기능을 링크로 추가해보자. 먼저 프로젝트가 어떤 구조로 진행될 지 미리 확인해보자. list.jsp 화면과 ListImpl클래스, jstl-1.2.jar가 추가된 것을 확인할 수 있다. JSTL Jar파일을 다운로드하여 WEB-INF/lib에 추가시키자. JSTL(JSP Tag Library)은 XML 데이터 처리와 조건문, 반복문, 국제화와 지역화와 같은 일을 처리하기 위한 JSP 태그 라이브러리다.


  우선 bbs.properties 파일에 /list.bbs=com.edu.bbs.ListImpl을 추가하자.

#bbs.properties
/writeForm.bbs=com.edu.bbs.WriteFormImpl
/write.bbs=com.edu.bbs.WriteImpl
/list.bbs=com.edu.bbs.ListImpl


  그리고 게시판 리스트 화면인 list.jsp를 만들고, 아래 코드를 삽입하자. '글쓰기' 링크를 클릭하게 되면 지난번에 만들었던 /bbs/writeForm.bbs로 접근하도록 하였다. 로그인 검증이나 페이징 처리, 쿼리 스트링 부분은 주석 처리해놓았다. 기능들을 추가해가면서 주석을 제거하도록 하자.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!-- Core Tag Library, prefix(접두사), jstl jar를 추가해줘야 한다. -->
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>BBS List</title>
</head>
<body>
	<div align="right">
		<!-- Login 검증 -->
		<!-- jstl의 if문은 else가 없어서 따로 검증해야함. -->
		<c:if test="${id != null}">
			<%-- <%@include file="loginOk.jsp" %> --%>
		</c:if>
		<c:if test="${id == null}">
			<%-- <%@include file="login.jsp" %> --%>
		</c:if>
	</div>

	<div align="center">
		<b>글목록(전체 글:${totalCount})</b>
		<table width="700">
		  <tr>
		    <td align="right" >
		       <a href="/bbs/writeForm.bbs">글쓰기</a>
		       <%-- <a href="/bbs/writeForm.bbs?pageNum=${pageNum}">글쓰기</a> --%>
		    </td>
		  </tr>
		</table>
	
		<table border="2" width="700">
			<tr>
				<th width="50">번호</th>
				<th width="250">제목</th>
				<th width="100">작성자</th>
				<th width="150">작성일</th>
				<th width="50">조회</th>
			</tr>
			<c:forEach var="article" items="${articles}" varStatus="status">
				<tr align="center" height="30">
					<td>${article.articleNumber}</td>
					<td align="left">
						<c:if test="${article.depth > 0}">
							&nbsp;&nbsp;
							<!-- 공백 이미지 -->
							<%-- <img src="" width="${10 * article.depth}" height="16"> --%>
						</c:if>
						<%-- <c:if test="${article.depth == 0}">
							<img src="" width="0" height="16">
						</c:if> --%>
						<a href="">${article.title}</a>
						<!-- URL query의 파라미터들은 request에 자동으로 심어지는 듯 하다. -->
						<%-- <a href="/bbs/content.bbs?articleNumer=${article.articleNumber}&pageNum=${pageNum}">${article.title}</a> --%>
						<c:if test="${article.hit >= 20}">
							hit!
							<!-- 조회수 이미지 -->
							<!-- <img src="" border="0" height="16"> -->
						</c:if>
					</td>
					<td>${article.id}</td>
					<td>${article.writeDate}</td>
					<td>${article.hit}</td>
				<tr>
			</c:forEach>
			
			<!-- Paging 처리 -->
			<tr>
				<td colspan="5" align="center" height="40">	
					<%-- ${pageCode} --%>
				</td>
			</tr>
		</table>
	</div>
</body>
</html>


  리스트 기능을 구현한 ListImpl클래스를 만든다. 모든 게시글의 수(totalCount)와 최신 게시글 10개를 들고오도록 한다.

package com.edu.bbs;

import java.util.ArrayList;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ListImpl implements BBSService {

	@Override
	public String bbsService(HttpServletRequest req, HttpServletResponse resp) throws ServletException {

		int totalCount = 0;
		ArrayList<BBSDto> articles = null;
		
		try {
			totalCount = BBSOracleDao.getInstance().getArticleTotalCount();
			articles = BBSOracleDao.getInstance().selectArticles(1, 10);
		} catch (Exception e) {
			e.printStackTrace();
		}

		req.setAttribute("totalCount", totalCount);
		req.setAttribute("articles", articles);
		
		return "/list.jsp";
	}

}

  BBSOracleDao클래스에 아래 메서드들을 추가한다.
/**
 * BBSOracleDao.java에 추가한다.
 * @param startRow
 * @param endRow
 * @return ArrayList<BBSDto>
 */
public ArrayList<BBSDto> selectArticles(int startRow, int endRow) throws ClassNotFoundException, SQLException {
	conn = orclDbc.getConnection();
	query = new StringBuffer();
	// BETWEEN ? AND ? 일 때는 Inline View 두 번 해야한다.
	// 만약 BETWEEN 1 AND ? 이라면 인라인뷰 한 번만으로 가능하다.
	query.append("SELECT bbs.* ");
	query.append("  FROM (SELECT rownum AS row_num, bbs.* ");
	query.append("		    FROM (SELECT article_number, id, title, depth, hit, write_date ");
	query.append("				        FROM bbs ");
	query.append("				       ORDER BY group_id DESC, pos ");
	query.append("				    ) bbs ");
	query.append("		  ) bbs ");
	query.append(" WHERE row_num BETWEEN ? AND ?");
	pstmt = conn.prepareStatement(query.toString());
	pstmt.setInt(1, startRow);
	pstmt.setInt(2, endRow);
	rs = pstmt.executeQuery();
	
	articles = new ArrayList<>();
	
	while(rs.next()) {
		article = new BBSDto();
		article.setArticleNumber(rs.getInt("article_number"));
		article.setId(rs.getString("id"));
		article.setTitle(rs.getString("title"));
		article.setDepth(rs.getInt("depth"));
		article.setHit(rs.getInt("hit"));
		article.setWriteDate(rs.getTimestamp("write_date"));
		articles.add(article);
	}
	
	disconnect();
	
	return articles;
}

public int getArticleTotalCount() throws ClassNotFoundException, SQLException {
	conn = orclDbc.getConnection();
	pstmt = conn.prepareStatement("SELECT count(*) AS total_count FROM bbs");
	rs = pstmt.executeQuery();
	
	int totalCount = 0;
	
	if(rs.next()) {
		totalCount = rs.getInt(1);		// index로 받아오는 것이 속도면에선 좋다.
//		totalCount = rs.getInt("total_count");		// 하지만 컬럼명이 보기엔 명확한 듯 하다.
	}
	
	disconnect();
	
	return totalCount;
}


  이 코드에서 selectArticles()메서드의 쿼리를 보면 페이징 처리를 위해 startRow와 endRow를 조건절에 주게 된다. 쿼리를 보면 인라인뷰(Inline View, FROM절 subquery)를 두 번 사용하는 것을 확인할 수 있다. 이 쿼리문이 어떻게 만들어졌는가 알아볼 필요가 있다 생각하며 아래 쿼리문들과 주석을 참고하도록 한다.

-- **
-- 게시글 리스트 출력하는 방법의 과정
-- **

SELECT rownum, bbs.*
  FROM bbs
 WHERE rownum BETWEEN 10 AND 20;
--BETWEEN A AND B에서 A의 값이 1이 아니라면 rownum은 순서를 찾을 수 없다.
--아무런 결과가 출력되지 않을 것이다.
--쿼리를 실행하면 WHERE절부터 수행하기 때문에 SELECT 하기 전까지 rownum을 찾을 수 없기 때문이다.


SELECT bbs.*
  FROM (
        SELECT rownum AS row_num, bbs.*   --rownum은 익명으로 사용해야한다
          FROM bbs
        --결과는 출력되지만,
        --게시글의 순서를 최신순(group_id DESC), 본글과 답글순(pos ASC)으로 정렬이 필요하다.
       ) bbs
 WHERE row_num BETWEEN 10 AND 20;


SELECT bbs.*
  FROM (
        SELECT rownum AS row_num, bbs.*   --rownum은 익명으로 사용해야한다.
          FROM bbs
         ORDER BY group_id DESC, pos ASC  --게시글 순서 정렬
       ) bbs
 WHERE row_num BETWEEN 10 AND 20;
--원하는 데이터를 가져오지 못한다.
--ORDER BY가 rownum값을 만드는 데 영향을 주지 않는 듯 하다.


SELECT rownum, bbs.*
  FROM (
        SELECT bbs.*
          FROM bbs
         ORDER BY group_id DESC, pos ASC
       ) bbs
 WHERE rownum BETWEEN 10 AND 20;   --처음과 같이 rownum을 찾을 수 없다.
--만약 rownum이 무조건 1부터 시작한다면 더이상의 과정은 필요없다.
--'답글 보기' 기능에는 이 방식을 사용할 것이다.


--위와 같은 과정을 통해서 이러한 쿼리문이 만들어졌다.
SELECT bbs.*
  FROM (
        SELECT rownum AS row_num, bbs.*
          FROM (
                SELECT bbs.*
                  FROM bbs
                 ORDER BY group_id DESC, pos ASC
               ) bbs
       ) bbs
 WHERE row_num BETWEEN 10 AND 20;

  

  지난 번에 만들었던 WriteImpl클래스의 resp.sendRedirect메서드의 파라미터를 /bbs/list.bbs로 변경하자. 이제 글쓰기 이후 게시판 화면으로 돌아올 수 있게 된다.

package com.edu.bbs;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class WriteImpl implements BBSService {

	@Override
	public String bbsService(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		req.setCharacterEncoding("utf-8");
		BBSDto article = new BBSDto();
		
		article.setId(req.getSession().getAttribute("id").toString());
		article.setTitle(req.getParameter("title"));
		article.setContent(req.getParameter("content"));
		
		try {
			if(BBSOracleDao.getInstance().insertArticle(article) == 1) {
				System.out.println("게시글이 삽입되었습니다.");
			} else {
				System.out.println("게시글 삽입이 실패하였습니다.");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

//		resp.sendRedirect("/bbs/writeForm.bbs");
		resp.sendRedirect("/bbs/list.bbs");
		
		return null;
	}

}


  서버를 구동한 뒤 http://localhost/bbs/list.bbs로 접속하면 아래와 같은 게시판 화면을 출력할 수 있을 것이다. 저장된 게시글들을 최신순으로 10개를 불러온 리스트고, 아직 글쓰기 기능만 가능하다.

  list.jsp에서 글제목 링크를 주석처리해놨기 때문에 글을 바로 확인할 방법은 없다. 다음 시간에는 페이징 처리를 하면서 가능하면 글내용을 확인하는 기능까지 추가할 계획이다.



[위키백과, 자바서버 페이지 표준 태그 라이브러리 참고]

[rownum을 이용한 게시판 페이징 참고]