글목록 HTML
자바스크립트를 이용해 각 제목의 <a> 태그를 클릭하면 <table> 태그 밑의 hidden input value에 a 태그의 input value 값을 넣고 submit해서,
제목을 클릭하면 각 줄마다의 bno 값을 다음 페이지로 전송한다.
조회수가 10이 넘어가면 hot.gif 이미지가 붙도록 설정한다.
답글일 경우엔 앞에 re.gif 이미지를 붙인다.
blevel로 몇 번째의 답글인지 표시하여 해당 답글만큼 level.gif 이미지의 크기를 늘려 더 크게 들여쓰기 한다.
jstl 태그 라이브러리 중 function을 이용해 게시글의 개수를 파악해 게시글이 없으면 없음을 띄운다.
jstl 태그 라이브러리 중 core를 이용하여 게시글을 forEach 반복문을 통해 나열한다.
또한, 페이지를 관리하는 박스를 제작하기 위해 jstl 라이브러리 중 core를 사용하여 전달 받은 페이지 개수만큼 forEach 반복문으로 페이지를 만들고,
클릭 시 해당 페이지에 속하는 게시글만 뜨도록 현재 페이지인 p를 전달해 해당 페이지의 start와 end 변수를 구하도록 하여 쿼리문을 다루는 DAO에 전달한다.
페이지 이동 시에는 주소 뒤에 쿼리를 붙여 정보를 전달해준다.
<%@ 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="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>게시글 목록</title>
<link rel="stylesheet" href="css/list.css" type="text/css">
<script src="jquery/jquery-3.7.0.min.js"></script>
<script src="js/list.js"></script>
</head>
<body>
<div class="btn">
<a href="writeForm?curPage=${pdto.curPage}">글쓰기</a>
</div>
<table>
<thead>
<tr><th colspan="6">게시글 목록 (전체 게시글 수 : ${totalCnt})</th></tr>
<tr>
<th>번호</th>
<th>제목</th>
<th>작성자</th>
<th>최종수정일</th>
<th>조회수</th>
<th>아이피</th>
</tr>
</thead>
<tbody>
<c:choose>
<c:when test = "${fn:length(articles)==0}">
<tr>
<td colspan="6">게시글이 없습니다.</td>
</tr>
</c:when>
<c:when test = "${fn:length(articles)>0}">
<c:forEach var = "article" items = "${articles}" varStatus="i">
<tr>
<td class="col1">${article.rn}</td>
<td class="col2">
<c:choose>
<c:when test="${article.blevel==0}">
<img src="images/level.gif" width="5px;">
</c:when>
<c:when test="${article.blevel>0 }">
<img src="images/level.gif" width="${article.blevel*10}px;" style="height:15px;">
<img src="images/re.gif">
</c:when>
</c:choose>
<a class="content">
${article.subject}
<input type = "hidden" name="no" value="${article.bno}">
</a>
</td>
<td class="col3">${article.writer}</td>
<td class="col4">${article.regdate}</td>
<td class="col5">
${article.readcount}
<c:if test="${article.readcount>10}">
<img src="images/hot.gif" style="width:30px; height:15px;">
</c:if>
</td>
<td class="col6">${article.ip}</td>
</tr>
</c:forEach>
</c:when>
</c:choose>
</tbody>
<tfoot>
<tr>
<td colspan="6" style="text-align:center;">
<c:if test = "${pdto.startPg > pBlock}">
<a href="list?curPage=${pdto.startPg-1}&curBlock=${pdto.curBlock-1}">[이전]</a>
</c:if>
<c:forEach begin="${pdto.startPg}" end="${pdto.endPg}" var="p" step="1">
<c:choose>
<c:when test="${p==pdto.curPage}">
<a href="list?curPage=${p}&curBlock=${pdto.curBlock}"><span style="font-weight:bold;"><c:out value="${p}"/></span></a>
</c:when>
<c:when test="${p!=pdto.curPage}">
<a href="list?curPage=${p}&curBlock=${pdto.curBlock}"><span><c:out value="${p}"/></span></a>
</c:when>
</c:choose>
</c:forEach>
<c:if test = "${pdto.endPg<pdto.pgCnt}">
<a href="list?curPage=${pdto.startPg+pBlock}&curBlock=${pdto.curBlock+1}">[다음]</a>
</c:if>
</td>
</tr>
</tfoot>
</table>
<form action="" method="post" name="content">
<input type="hidden" name="bno" value="${article.bno}">
<input type="hidden" name="curPage" value="${pdto.curPage}">
<input type="hidden" name="curBlock" value="${pdto.curPage}">
</form>
</body>
</html>
글목록 CSS
@charset "UTF-8";
*{margin:0; padding: 0;}
table {border:1px solid black; border-collapse: collapse; width:90%; margin:0 auto;}
th, td {border:1px solid black; border-collapse: collapse; padding:10px; margin:5px;}
th {background-color: #cfcfff;}
td {background-color: #ffffff; font-weight: 200;}
.btn {height:30px; width:850px; text-align:right;}
a {color:black; text-decoration: none; }
a:hover {font-weight: bold;}
.col1 {width:8%;}
.col2 {width:35%;}
.col3 {width:20%;}
.col4 {width:15%;}
.col5 {width:10%;}
.col6 {width:12%;}
글목록 JavaScript
/**
*
*/
$().ready(function(){
let $item = $('a.content').on("click",function(){
let idx = $item.index(this);
//alert($("a.content input[type=hidden]").eq(idx).val());
var f = $('form[name=content]')
$('form input[name=bno]').attr('value', $("a.content input[name=no]").eq(idx).val());
f.attr('action','content');
f.submit();
});
})
글목록 컨트롤러
package com.ecom4.hi.board.control;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.ecom4.hi.HomeController;
import com.ecom4.hi.board.model.BoardDTO;
import com.ecom4.hi.board.model.PageDTO;
import com.ecom4.hi.board.model.RowInterPage;
import com.ecom4.hi.board.service.BoardService;
@Controller
public class BoardController {
private static final Logger logger = LoggerFactory.getLogger(BoardController.class);
//JavaBoard에서 작업했던 Action 역할을 하는 객체 - 비즈니스 로직 BIZ를 갖고 있다
@Autowired
private BoardService boardService;
@RequestMapping(value = "/list")
public String getArticles(HttpServletRequest request, HttpServletResponse response,
BoardDTO dto, Model model, PageDTO pdto){
logger.info("리스트 컨트롤러");
Map<String, Object> resultSet = boardService.getArticles(dto, pdto);
//List<BoardDTO> articles = boardService.getArticles(dto);
model.addAttribute("articles",resultSet.get("articles"));
model.addAttribute("totalCnt",resultSet.get("totalCnt"));
model.addAttribute("pdto",resultSet.get("pdto"));
model.addAttribute("pBlock",RowInterPage.PAGE_OF_BLOCK);
return "board/BoardList";
}
}
글목록 Service
RowInterPage 인터페이스
Service에서 사용할, 한 페이지당 게시글 개수와 한 블럭당 페이지 개수를 지정하는 인터페이스 설정
언제든지 다른 코드 수정 없이 쉽게 바꿀 수 있도록 따로 빼주어서 사용한다.
인터페이스를 작성하여 final과 대문자 변수명을 사용해 상수를 선언한다.
package com.ecom4.hi.board.model;
public interface RowInterPage {
public static final int ROW_OF_PAGE = 6;
public static final int PAGE_OF_BLOCK = 3;
}
boardService 인터페이스
package com.ecom4.hi.board.service;
import java.util.Map;
import com.ecom4.hi.board.model.BoardDTO;
import com.ecom4.hi.board.model.PageDTO;
public interface BoardService {
Map<String, Object> getArticles(BoardDTO dto, PageDTO pdto);
}
boardServiceImpl 구현 클래스
package com.ecom4.hi.board.service;
import java.sql.ResultSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.ecom4.hi.board.dao.BoardDAO;
import com.ecom4.hi.board.model.BoardDTO;
import com.ecom4.hi.board.model.PageDTO;
import com.ecom4.hi.board.model.RowInterPage;
@Service(value = "boardService")
public class BoardServiceImpl implements BoardService {
// DAO DI(Dependency Injection)
@Autowired
private BoardDAO boardDao;
@Override
public Map<String, Object> getArticles(BoardDTO dto, PageDTO pdto) {
Map<String, Object> resultSet = new HashMap<String, Object>();
if(pdto.getCurBlock()<0) pdto.setCurBlock(1);
if(pdto.getCurPage()<0) pdto.setCurPage(1);
int totalCnt = boardDao.getTotalCnt();
resultSet.put("totalCnt",totalCnt);
//현재 페이지 계산
int start = (pdto.getCurPage()-1)*RowInterPage.ROW_OF_PAGE + 1;
int end = (pdto.getCurPage()*RowInterPage.ROW_OF_PAGE)>totalCnt?
totalCnt:pdto.getCurPage()*RowInterPage.ROW_OF_PAGE;
dto.setStart(start);
dto.setEnd(end);
int pgCnt = (totalCnt%RowInterPage.ROW_OF_PAGE==0)?
totalCnt/RowInterPage.ROW_OF_PAGE : totalCnt/RowInterPage.ROW_OF_PAGE+1;
//페이지 블럭
int pgBlock = (pgCnt%RowInterPage.PAGE_OF_BLOCK==0)?
pgCnt/RowInterPage.PAGE_OF_BLOCK : pgCnt/RowInterPage.PAGE_OF_BLOCK+1;
int startPg = (pdto.getCurBlock()-1)*RowInterPage.PAGE_OF_BLOCK+1;
int endPg = (pdto.getCurBlock()*RowInterPage.PAGE_OF_BLOCK > pgCnt)?
pgCnt : pdto.getCurBlock()*RowInterPage.PAGE_OF_BLOCK;
pdto.setPgCnt(pgCnt);
pdto.setPgBlock(pgBlock);
pdto.setStartPg(startPg);
pdto.setEndPg(endPg);
resultSet.put("pdto",pdto);
resultSet.put("articles", boardDao.getArticles(dto));
return resultSet;
}
}
현재 페이지 변수를 선정하고 처음엔 1로 초기화해준다.
전체 게시글 수와,
전체 페이지 수,
그리고 페이지마다 시작하는 게시글의 번호와 마지막 게시글의 번호를 작성한다.
시작하는 게시글 번호 변수인 start는, '이전 페이지 수 * 페이지당 게시글 수'로 구하고,
마지막 게시글 번호 변수인 End는,
'최종 게시글 수 >= 현재 페이지 수 * 페이지당 게시글 수'일 경우엔 '현재 페이지수 * 페이지당 게시글 수'가 되도록 하고,
'최종 게시글 수 <= 현재 페이지 수 * 페이지당 게시글 수'일 경우엔 '최종 게시글 수'가 되도록 한다.
글목록 DAO
package com.ecom4.hi.board.dao;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.ecom4.hi.board.model.BoardDTO;
@Repository(value = "boardDao")
public class BoardDAO {
// 자원을 DI (의존성 주입)
@Autowired
private SqlSession sqlSession;
private String namespace = "board.BoardDAO.";
public List<BoardDTO> getArticles(BoardDTO dto) {
return sqlSession.selectList(namespace + "getArticles",dto);
}
}
글목록 Mapper
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace = "board.BoardDAO">
<select id="getArticles" resultType="bvo">
SELECT *
FROM(SELECT ROWNUM AS rn, A.*
FROM(SELECT bno, bref, bstep, blevel, readcount,
subject, content, writer, regdate, ip, passwd
FROM board
ORDER BY bref DESC, bno, bstep) A)
WHERE rn BETWEEN #{start} AND #{end}
</select>
<select id="getTotalCnt" resultType="int">
SELECT COUNT(bno) FROM BOARD
</select>
<insert id="writeAction" parameterType="bvo">
<selectKey keyProperty="nbno" resultType="int" order="BEFORE">
SELECT NVL(MAX(bno),0)+1 AS nbno FROM board
</selectKey>
INSERT INTO board
(bno, bref, bstep, blevel, readcount,
subject, content, writer, regdate, ip, passwd)
VALUES (
#{nbno},
<choose>
<when test="bno>0">
#{bno},
#{bstep}+1,
#{blevel}+1,
</when>
<when test="bno==0 and bref==0">
#{nbno},
0,
0,
</when>
</choose>
#{readcount}, #{subject}, #{content}, #{writer}, SYSDATE, #{ip}, #{passwd}
)
</insert>
</mapper>
완성 모습
페이지 박스도, 이미지 적용과 답글이 잘 적용되는 것도 확인할 수 있다.