728x90
이제까지 게시글 조회/ 수정 / 삽입을 구현해보았다!
그렇다면 게시글 삭제를 진행하면 된다! 역시 수정 이후로 하는거라 조금 수월했다!
🌝 게시글 삭제
📚 VS code
📕 boardDetail.js
// 게시글 삭제 버튼이 클릭 되었을 때
document.getElementById("deleteBtn").addEventListener("click", () => {
if(confirm("정말 삭제 하시겠습니까?")){
location.href
= location.pathname.replace("board","board2")
+ "/delete";
+ location.search;
// /board2/1/2006/delete(GET)
}
})
📚 Spring
📗 BoardController2.java
// 게시글 삭제
@GetMapping("/{boardCode}/{boardNo}/delete")
public String boardDelete(
@PathVariable("boardCode") int boardCode
,@PathVariable("boardNo") int boardNo
,RedirectAttributes ra
,@RequestHeader("referer") String referer ) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("boardCode", boardCode);
map.put("boardNo", boardNo);
int result = service.boardDelete(map);
String path = "redirect:";
String message = null;
if(result > 0) {
message = "삭제 되었습니다.";
path += "/board/"+ boardCode;
}else {
message = "삭제 실패";
path += "/board/" + boardCode + "/" + boardNo;
//path += referer;
}
ra.addFlashAttribute("message", message);
return path;
}
📗 BoardService2.java
/** 게시글 삭제
* @param map
* @return result
*/
int boardDelete(Map<String, Object> map);
📗 BoardServiceImpl2.java
// 게시글 삭제
@Transactional(rollbackFor = Exception.class)
@Override
public int boardDelete(Map<String, Object> map) {
return dao.boardDelete(map);
}
📗 BoardDAO2.java
/** 게시글 삭제
* @param map
* @return result
*/
public int boardDelete(Map<String, Object> map) {
return sqlSession.update("boardMapper.boardDelete", map);
}
📗 board-mapper.xml
<!-- 게시글 삭제 -->
<update id="boardDelete">
UPDATE "BOARD" SET
BOARD_DEL_FL = 'Y'
WHERE BOARD_CODE = #{boardCode}
AND BOARD_NO = #{boardNo}
</update>
🌝 게시글 검색
게시글 검색 기능은 이전에 만들어 놓았던 목록 조회에서 사용한 메소드를 활용하여 구현 할 수 있다.
[ Spring ] 게시글 목록 조회 ⑥
이번부터는 좀 심화 버전이고 이전과 다른 개념이 추가된 게시글 목록을 조회하는 기능을 구현해 볼 것이다. 구현하기에 앞서 Request 가 들어오는 타입에 따라 받는 방법 2개로 나눌 수 있다. 기
jnaa.tistory.com
총 2개의 검색창이 있고, 상단에 있는 검색 창은 '제목'으로만 검색할 수 있게 구현할 예정이고
하단에는 제목, 내용, 제목+내용, 작성자로 각각 구분해서 검색 할 수 있게 구현해보자!
이전에 SERVELT 에서는 swicth문을 이용해서 어렵게 했던 것 같은데 과연 spring에서는 어떻게 할지 코드로 살펴보자!
📚 VS code
📕 header.jsp (상단 검색창)
<form action="/board/1" method="GET">
<fieldset> <!-- form태그 내 영역 구분 -->
<!--
input의 name 속성 == 제출 시 key
input에 입력된 내용 == 제출 시 value
autocomplete="off" : 브라우저 제공 자동완성 off
-->
<input type="search" name="query" id="query"
placeholder="검색어를 입력해주세요."
autocomplete="off" value="${param.query}">
<%-- 제목 검색 --%>
<input type="hidden" name="key" value="t">
<!-- 검색 버튼 -->
<!-- button type="submit" 이 기본값 -->
<button id="searchBtn" class="fa-solid fa-magnifying-glass"></button>
</fieldset>
</form>
📕 boardList.jsp (하단 검색창)
<%@ 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}"/>
<%-- <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>게시판 이름</title>
<link rel="stylesheet" href="/resources/css/board/boardList-style.css">
</head>
<body>
<main>
<jsp:include page="/WEB-INF/views/common/header.jsp"/>
<%-- 검색을 진행한 경우 파라미터(key, query)를
쿼리스트링 형태로 저장한 변수를 선언 --%>
<c:if test="${!empty param.key}" >
<c:set var="sp" value="&key=${param.key}&query=${param.query}"/>
</c:if>
<section class="board-list">
<h1 class="board-name">${boardName}</h1>
<c:if test="${!empty param.key}" >
<h3 style="margin:30px">"${param.query}" 검색 결과</h3>
</c:if>
<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">
<!-- 로그인 상태일 경우 글쓰기 버튼 노출 -->
<c:if test="${!empty loginMember}" >
<button id="insertBtn">글쓰기</button>
</c:if>
</div>
<div class="pagination-area">
<ul class="pagination">
<!-- 첫 페이지로 이동 -->
<li><a href="/board/${boardCode}?cp=1${sp}"><<</a></li>
<!-- 이전 목록 마지막 번호로 이동 -->
<li><a href="/board/${boardCode}?cp=${pagination.prevPage}${sp}"><</a></li>
<!-- 특정 페이지로 이동 -->
<c:forEach var="i" begin="${pagination.startPage}"
end="${pagination.endPage}" step="1">
<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}${sp}">${i}</a></li>
</c:otherwise>
</c:choose>
</c:forEach>
<!-- 다음 목록 시작 번호로 이동 -->
<li><a href="/board/${boardCode}?cp=${pagination.nextPage}${sp}">></a></li>
<!-- 끝 페이지로 이동 -->
<li><a href="/board/${boardCode}?cp=${pagination.maxPage}${sp}">>></a></li>
</ul>
</div>
<!-- 검색창 -->
<form action="${boardCode}" 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"/>
<%-- boardList.js 연결 --%>
<script src="/resources/js/board/boardList.js"></script>
</body>
</html>
📕 boardList.js
const insertBtn = document.getElementById("insertBtn");
// 글쓰기 버튼 클릭 시
if(insertBtn != null){
insertBtn.addEventListener("click", ()=>{
// JS BOM 객체 중 location
// location.href = "주소"
// 해당 주소 요청 (GET 방식)
location.href = `/board2/${location.pathname.split("/")[2]}/insert`
// "/board2/" + location.pathname.split("/")[2];
// /board2/1/insert
})
}
// 검색창 이전 검색 기록을 남겨 놓기
const boardSearch = document.querySelector("#boardSearch");
const searchKey = document.querySelector("#searchKey");
const searchQuery = document.querySelector("#searchQuery");
const options = document.querySelectorAll("#searchKey > option");
// 즉시 실행 함수
(()=>{
const params = new URL(location.href).searchParams;
const key = params.get("key"); // t, c, tc, w 중 하나
const query = params.get("query"); // 검색어
if(key != null){ // 검색을 했을 때
searchQuery.value = query; // 검색어를 화면에 출력
// option 태그를 하나씩 순차 접근해서 value가 key랑 같으면
// selected 속성 추가
for(let op of options){
if(op.value == key){
op.selected = true;
}
}
}
})();
// 검색어 입력 없이 제출된 경우
boardSearch.addEventListener("submit", e=>{
if(searchQuery.value.trim().length == 0){ // 검색어 미입력 시
e.preventDefault(); // form 기본 이벤트 제거
location.href = location.pathname; // 해당 게시판 1페이지로 이동
// location.pathname : 쿼리스트링을 제외한 실제 주소
}
})
📚 Spring
📗 BoardController.java
이전에 진행했던 '게시글 목록 조회'에서
검색어가 있을때! 없을때로 나눠서 진행해보자
// 게시글 목록 조회
@GetMapping("/{boardCode:[0-9]+}")
public String selectBoardList(@PathVariable("boardCode") int boardCode
, @RequestParam(value = "cp", required = false, defaultValue = "1") int cp
, Model model
, @RequestParam Map<String, Object> paramMap // 파라미터 전부 다 담겨 있음
) {
// boardCode 확인
// System.out.println("boardCode : " + boardCode);
if(paramMap.get("key") == null) { // 검색어가 없을 때(검색 X)
// 게시글 목록 조회 서비스 호출
Map<String, Object> map = service.selectBoardList(boardCode, cp);
// 조회 결과를 request scope에 세팅 후 forward
model.addAttribute("map", map);
} else { // 검색어가 있을 때(검색 O)
paramMap.put("boardCode", boardCode);
Map<String, Object> map = service.selectBoardList(paramMap, cp);
model.addAttribute("map", map);
}
return "board/boardList";
}
📗BoardService.java
/** 게시글 목록 조회 (검색)
* @param paramMap
* @param cp
* @return boardList
*/
Map<String, Object> selectBoardList(Map<String, Object> paramMap, int cp);
📗BoardServiceImpl.java
// 게시글 목록 조회 (검색)
@Override
public Map<String, Object> selectBoardList(Map<String, Object> paramMap, int cp) {
// 1. 특정 게시판의 삭제되지 않고 검색 조건이 일치하는 게시글 수 조회
int listCount = dao.getListCount(paramMap);
// 2. 1번 조회 결과 + cp를 이용해서 Pagination 객체 생성
// -> 내부 필드가 모두 계산되어 초기화
Pagination pagination = new Pagination(cp, listCount);
// 3. 특정 게시판에서
// 현재 페이지에 해당하는 부분에 대한 게시글 목록 조회
// + 단, 검색 조건이 일치하는 글만
List<Board> boardList = dao.selectBoardList(pagination, paramMap);
// 4. pagination, boardList를 Map에 담아서 반환
Map<String, Object> map = new HashMap<>();
map.put("pagination", pagination);
map.put("boardList", boardList);
return map;
📗BoardDAO2.java
/** 게시글 수 조회 (검색)
* @param paramMap
* @return listCount
*/
public int getListCount(Map<String, Object> paramMap) {
return sqlSession.selectOne("boardMapper.getListCount_search", paramMap);
}
/** 게시글 목록 조회
* @param pagination
* @param paramMap
* @return boardList
*/
public List<Board> selectBoardList(Pagination pagination, Map<String, Object> paramMap) {
// 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_search", paramMap, rowBounds);
}
📗 board-mapper.xml
하단 검색창에는 다양한 옵션으로 나눠서 검색이 달라졌다.
해서 이전에 배웠던 동적 SQL를 이용해서 검색 조건에 따라 조회할 수 있도록 구현했다.
JOIN문을 이용하여 안에 if태그와 choose태그로 작성해준다!
💭 제목 + 내용 검색 시엔
AND (BOARD_TITLE LIKE '%${query}%' OR BOARD_CONTENT LIKE '%${query}%')
<!-- 특정 게시판의 삭제되지 않고 검색 조건이 일치하는 게시글 수 조회 -->
<select id="getListCount_search" resultType="_int">
SELECT COUNT(*)
FROM BOARD
<!-- 작성자 검색 -->
<if test='key == "w"'>
JOIN MEMBER USING(MEMBER_NO)
</if>
WHERE BOARD_DEL_FL = 'N'
AND BOARD_CODE = #{boardCode}
<choose>
<when test='key == "t"'>
<!-- 제목 -->
AND BOARD_TITLE LIKE '%${query}%'
</when>
<when test='key == "c"'>
<!-- 내용 -->
AND BOARD_CONTENT LIKE '%${query}%'
</when>
<when test='key == "tc"'>
<!-- 제목 + 내용 -->
AND (BOARD_TITLE LIKE '%${query}%' OR BOARD_CONTENT LIKE '%${query}%')
</when>
<when test='key == "w"'>
<!-- 작성자(닉네임) -->
AND MEMBER_NICKNAME LIKE '%${query}'
</when>
</choose>
</select>
<!-- 게시글 목록 조회(검색)-->
<!-- CDATA 태그 : 해당 태그 내부에 작성된 것은 모두 문자로 취급 -->
<select id="selectBoardList_search" 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}
<choose>
<when test='key == "t"'>
<!-- 제목 -->
AND BOARD_TITLE LIKE '%${query}%'
</when>
<when test='key == "c"'>
<!-- 내용 -->
AND BOARD_CONTENT LIKE '%${query}%'
</when>
<when test='key == "tc"'>
<!-- 제목 + 내용 -->
AND (BOARD_TITLE LIKE '%${query}%' OR BOARD_CONTENT LIKE '%${query}%')
</when>
<when test='key == "w"'>
<!-- 작성자(닉네임) -->
AND MEMBER_NICKNAME LIKE '%${query}'
</when>
</choose>
ORDER BY BOARD_NO DESC
</select>
728x90
'ON > spring' 카테고리의 다른 글
[ Spring ] @Scheduled 이론 ⑯ (0) | 2023.09.01 |
---|---|
[ Spring ] 게시글 목록으로 이동 ⑮ (0) | 2023.08.31 |
[ Spring ] 댓글 / 대 댓글 기능 ⑬ (0) | 2023.08.30 |
[ Spring ] 게시글 수정 ⑫ (0) | 2023.08.29 |
[ Spring ] 게시글 작성(제목+내용+사진) ⑪ (0) | 2023.08.28 |