🔑 인덱스 활용 !!
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%-- map에 저장된 값들을 각각 변수에 저잗 --%>
<c:set var="pagination" value="${map.pagination}"/>
<c:set var="boardList" value="${map.boardList}"/>
<%-- boardList의 인덱스를 활용하기 !
인덱스는 0부터 시작인데 boardCode는 1부터 시작이라서 -1 처리 함 --%>
<%-- <c:set var="boardName" value="${boardTypeList[boardCode-1].BOARD_NAME}"/> --%>
<c:forEach items="${boardTypeList}" var="boardType">
<c:if test="${boardType.BOARD_CODE == boardCode}" >
<c:set var="boardName" value="${boardType.BOARD_NAME}"/>
</c:if>
</c:forEach>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${boardName}</title>
<link rel="stylesheet" href="/resources/css/board/boardList-style.css">
</head>
<body>
<main>
<jsp:include page="/WEB-INF/views/common/header.jsp"/>
<section class="board-list">
<h1 class="board-name">${boardName}</h1>
<div class="list-wrapper">
<table class="list-table">
<thead>
<tr>
<th>글번호</th>
<th>제목</th>
<th>작성자</th>
<th>작성일</th>
<th>조회수</th>
<th>좋아요</th>
</tr>
</thead>
<tbody>
<c:choose>
<c:when test="${empty boardList}">
<%-- 조회된 게시글 목록이 비어있거나 null인 경우 --%>
<tr>
<th colspan="6">게시글이 존재하지 않습니다.</th>
</tr>
</c:when>
<c:otherwise>
<!-- 게시글 목록 조회 결과가 있다면 -->
<c:forEach items="${boardList}" var="board">
<tr>
<td>${board.boardNo}</td>
<td>
<%-- 썸네일이 있을 경우 --%>
<c:if test="${!empty board.thumbnail}" >
<img class="list-thumbnail" src="${board.thumbnail}">
</c:if>
<%-- ${boardCode} : @Pathvariable로 request scope에 추가된 값 --%>
<a href="/board/${boardCode}/${board.boardNo}?cp=${pagination.currentPage}">${board.boardTitle}</a>
[${board.commentCount}]
</td>
<td>${board.memberNickname}</td>
<td>${board.boardCreateDate}</td>
<td>${board.readCount}</td>
<td>${board.likeCount}</td>
</tr>
</c:forEach>
</c:otherwise>
</c:choose>
</tbody>
</table>
</div>
<div class="btn-area">
<!-- 로그인 상태일 경우 글쓰기 버튼 노출 -->
<button id="insertBtn">글쓰기</button>
</div>
<div class="pagination-area">
<ul class="pagination">
<!-- 첫 페이지로 이동 -->
<li><a href="/board/${boardCode}?cp=1"><<</a></li>
<!-- 이전 목록 시작 번호로 이동 -->
<li><a href="/board/${boardCode}?cp=${pagination.prevPage}"><</a></li>
<!-- 특정 페이지로 이동 -->
<c:forEach var="i" begin="${pagination.startPage}" end="${pagination.endPage}">
<!-- 현재 보고있는 페이지 -->
<c:choose>
<c:when test="${i == pagination.currentPage}">
<li><a class="current">${i}</a></li>
</c:when>
<c:otherwise>
<li><a href="/board/${boardCode}?cp=${i}">${i}</a></li>
</c:otherwise>
</c:choose>
<!-- 현재 페이지를 제외한 나머지 -->
</c:forEach>
<!-- 다음 목록 시작 번호로 이동 -->
<li><a href="/board/${boardCode}?cp=${pagination.nextPage}">></a></li>
<!-- 끝 페이지로 이동 -->
<li><a href="/board/${boardCode}?cp=${pagination.maxPage}">>></a></li>
</ul>
</div>
<!-- 검색창 -->
<form action="#" method="get" id="boardSearch">
<select name="key" id="searchKey">
<option value="t">제목</option>
<option value="c">내용</option>
<option value="tc">제목+내용</option>
<option value="w">작성자</option>
</select>
<input type="text" name="query" id="searchQuery" placeholder="검색어를 입력해주세요.">
<button>검색</button>
</form>
</section>
</main>
<!-- 썸네일 클릭 시 모달창 출력 -->
<div class="modal">
<span id="modalClose">×</span>
<img id="modalImage" src="/resources/images/user.png">
</div>
<jsp:include page="/WEB-INF/views/common/footer.jsp"/>
</body>
</html>
📍 @PathVariable
- url 경로에 있는 값을 매개변수로 이용할 수 있게 하는 어노테이션
- request scope에 세팅
🔑 Query String과 비교하며 사용하기
package edu.kh.project.board.controller;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
import edu.kh.project.board.model.service.BoardService;
@SessionAttributes("loginMember")
@RequestMapping("/board")
@Controller
public class BoardController {
@Autowired
private BoardService service;
/* 목록 조회 : /board/1?cp=1 (cp : current page(현재 페이지))
* 상세 조회 : /board/1/1500?cp=1
*
* ** 컨트롤러 따로 생성 예정 **
* 삽입 : /board2/insert?code=1 (code == BOARD_CODE, 게시판 종류)
* 수정 : /board2/udpate?code=1&no=1500 (no = BOARD_NO, 게시글 번호)
* 삭제 : /board2/delete?code=1&no=1500
* */
// @PathVariable
// url 경로에 있는 값을 매개변수로 이용할 수 있게 하는 어노테이션
// + request scope에 세팅
// /board?code=1
// /board?code=2
// @PathVariable을 사용하는 경우
// - 자원(resource) 구분/식별
// ex) https://github.com/hj-1008
// ex) /board/1 -> 1번(공지사항) 게시판
// ex) /board/2 -> 2번(자유 게시판) 게시판
// Query String을 사용하는 경우
// - 정렬, 필터링
// ex) search.naver.com?query=날씨
// ex) search.naver.com?query=점심
// ex) /board2/insert?code=1
// ex) /board2/insert?code=2
// -> 삽입이라는 공통된 동작 수행
// 단, code에 따라 어디에 삽입할지 지정(필터링)
// ex) /board/list?order=recent(최신순)
// ex) /board/list?order=most(인기순)
// 게시글 목록 조회
@GetMapping("/{boardCode}")
public String selectBoardList(@PathVariable("boardCode") int boardCode
, @RequestParam(value="cp", required=false, defaultValue = "1") int cp
, Model model) {
// boardCode 확인
// System.out.println("boardCode : " + boardCode);
// 게시글 목록 조회 서비스 호출
Map<String, Object> map = service.selectBoardList(boardCode, cp);
// 조회 결과를 request scope에 세팅 후 forward
model.addAttribute("map", map);
return "board/boardList";
}
}
/** 게시글 목록 조회
* @param boardCode
* @param cp
* @return map
*/
Map<String, Object> selectBoardList(int boardCode, int cp);
// 게시글 목록 조회
@Override
public Map<String, Object> selectBoardList(int boardCode, int cp) {
// 1. 특정 게시판의 삭제되지 않은 게시글 수 조회
int listCount = dao.getListCount(boardCode);
// 2. 1번의 조회 결과 + cp를 이용해서 페이지네이션 객체 생성
// -> 내부 필드가 계산되어서 모두 초기화 됨
Pagination pagination = new Pagination(cp, listCount);
// 3. 특정 게시판에서 현재 페이지에 해당하는 부분에 대한 게시글 목록 조회
// 어떤 게시판(boardCode)에서 몇 페이지(pagination.currentPage)에 대한
// 게시글 몇 개 (pagination.limit) 조회
List<Board> boardList = dao.selectBoardList(pagination, boardCode);
// 4. pagination, boardList를 Map에 담아서 반환
Map<String, Object> map = new HashMap<String, Object>();
map.put("pagination", pagination);
map.put("boardList", boardList);
return map;
}
/** 특정 게시판 삭제되지 않은 게시글 수 조회 DAO
* @param boardCode
* @return listCount
*/
public int getListCount(int boardCode) {
return sqlSession.selectOne("boardMapper.getListCount", boardCode);
}
<!-- 특정 게시판 삭제되지 않은 게시글 수 조회 -->
<select id="getListCount" resultType="_int">
SELECT COUNT(*) FROM BOARD
WHERE BOARD_CODE = #{boardCode}
AND BOARD_DEL_FL = 'N'
</select>
📍 RowBounds
- 마이바티스에서 페이징 처리를 위해 제공하는 객체
- offset만큼 건너뛰고 그 다음 지정된 행 개수(limit)만큼 조회
🔑 Mybatis에서 SQL 수행 시 전달할 수 있는 파라미터의 개수는 하나이다. 하지만 RowBounds는 예외
/** 특정 게시판에서 현재 페이지에 해당하는 부분에 대한 게시글 목록 조회 DAO
* @param pagination
* @param boardCode
* @return boardList
*/
public List<Board> selectBoardList(Pagination pagination, int boardCode) {
// RowBounds 객체
// - 마이바티스에서 페이징 처리를 위해 제공하는 객체
// - offset만큼 건너뛰고
// 그 다음 지정된 행 개수(limit)만큼 조회
// 1) offset 계산
int offset
= (pagination.getCurrentPage() - 1) * pagination.getLimit();
// 2) Rowbounds 객체 생성
RowBounds rowBounds = new RowBounds(offset, pagination.getLimit());
// 3) selectList("namespace.id", 파라미터, Rowbounds) 호출
return sqlSession.selectList("boardMapper.selectBoardList", boardCode, rowBounds);
}
📍 CDATA 태그
해당 태그 내부에 작성된 것은 모두 문자로 취급
🔑 서브쿼리 공부 파이팅...
<!-- 게시글 목록 조회 -->
<select id="selectBoardList" resultMap="board_rm">
SELECT BOARD_NO, BOARD_TITLE, MEMBER_NICKNAME, READ_COUNT,
<![CDATA[
CASE
WHEN SYSDATE - B_CREATE_DATE < 1/24/60
THEN FLOOR( (SYSDATE - B_CREATE_DATE) * 24 * 60 * 60 ) || '초 전'
WHEN SYSDATE - B_CREATE_DATE < 1/24
THEN FLOOR( (SYSDATE - B_CREATE_DATE) * 24 * 60) || '분 전'
WHEN SYSDATE - B_CREATE_DATE < 1
THEN FLOOR( (SYSDATE - B_CREATE_DATE) * 24) || '시간 전'
ELSE TO_CHAR(B_CREATE_DATE, 'YYYY-MM-DD')
END B_CREATE_DATE,
]]>
(SELECT COUNT(*) FROM "COMMENT" C
WHERE C.BOARD_NO = B.BOARD_NO) COMMENT_COUNT,
(SELECT COUNT(*) FROM BOARD_LIKE L
WHERE L.BOARD_NO = B.BOARD_NO) LIKE_COUNT,
(SELECT IMG_PATH || IMG_RENAME FROM BOARD_IMG I
WHERE I.BOARD_NO = B.BOARD_NO
AND IMG_ORDER = 0) THUMBNAIL
FROM "BOARD" B
JOIN "MEMBER" USING(MEMBER_NO)
WHERE BOARD_DEL_FL = 'N'
AND BOARD_CODE = #{boardCode}
ORDER BY BOARD_NO DESC
</select>