<복습>
검색 기능 넣기
1.
boardMapper.xml에 sql태그 사용하여 where절 생성
2.
1에서 만든 sql태그를 총개수/검색개수, 목록/검색목록에 include
정렬도 똑같은 형태로 생성하면 됨 정렬하는 order절 만들고 include
<게시판에 정렬 버튼 만들기>
BoardController에
정렬하기 위해 이미 @RequestParam(value="order", defaultValue="1")을 넣었음
정렬버튼 추가 + 유효성 체크 + 정렬 이벤트 삽입
views
board
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" %>
<!-- 내용 시작 -->
<div class="page-main">
<h2>게시판 목록</h2>
<form action="list" id="search_form" method="get">
<ul class="search">
<li>
<select name="keyfield" id="keyfield">
<option value="1" <c:if test="${param.keyfield == 1}">selected</c:if>>제목</option>
<option value="2" <c:if test="${param.keyfield == 2}">selected</c:if>>ID+별명</option>
<option value="3" <c:if test="${param.keyfield == 3}">selected</c:if>>내용</option>
<option value="4" <c:if test="${param.keyfield == 4}">selected</c:if>>제목+내용</option>
</select>
</li>
<li>
<input type="search" name="keyword" id="keyword" value="${param.keyword}">
</li>
<li>
<input type="submit" value="찾기">
<input type="button" value="목록" onclick="location.href='list'">
</li>
</ul>
<div class="align-right">
<select id="order" name="order">
<option value="1" <c:if test="${param.order == 1}">selected</c:if>>최신순</option>
<option value="2" <c:if test="${param.order == 2}">selected</c:if>>조회수</option>
<option value="3" <c:if test="${param.order == 3}">selected</c:if>>좋아요</option>
<option value="4" <c:if test="${param.order == 4}">selected</c:if>>댓글수</option>
</select>
<c:if test="${!empty user}">
<input type="button" value="글쓰기" onclick="location.href='write'">
</c:if>
</div>
</form>
<c:if test="${count == 0}">
<div class="result-display">표시할 게시물이 없습니다.</div>
</c:if>
<c:if test="${count > 0}">
<table class="striped-table">
<tr>
<th>번호</th>
<th width="400">제목</th>
<th>작성자</th>
<th>작성일</th>
<th>조회수</th>
<th>좋아요수</th>
</tr>
<c:forEach var="board" items="${list}">
<tr>
<td class="align-center">${board.board_num}</td>
<td><a href="detail?board_num=${board.board_num}">${board.title}(${board.re_cnt})</a></td>
<td class="align-center">
<c:if test="${empty board.nick_name}">${board.id}</c:if>
<c:if test="${!empty board.nick_name}">${board.nick_name}</c:if>
</td>
<td class="align-center">${board.reg_date}</td>
<td class="align-center">${board.hit}</td>
<td class="align-center">${board.fav_cnt}</td>
</tr>
</c:forEach>
</table>
<div class="align-center">${page}</div>
</c:if>
</div>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
$(function(){
//검색 유효성 체크
$('#search_form').submit(function(){
if($('#keyword').val().trim()==''){
alert('검색어를 입력하세요');
$('#keyword').val('').focus();
return false;
}
});//end of submit
//정렬 선택
$('#order').change(function(){
location.href='list?keyfield='+$('#keyfield').val()+'&keyword='+$('#keyword').val()+'&order='+$('#order').val();
});
});
</script>
<!-- 내용 끝 -->
1.
<option value="1" <c:if test="${param.order == 1}">selected</c:if>
전송된 value값이 화면에 남아있게끔 처리
2.
http://localhost:8000/board/list?keyfield=1&keyword=%EC%A3%BC%EC%8A%A4&order=1
실행 후 검색, 정렬을 선택 -> url을 보면 keyword/order 값이 get방식으로 전달된걸 알 수 있음
3.
정렬 필터는 jquery(ajax) 사용해서 해당 화면에서 움직이게함
파라미터네임 연결할 땐 ?
여러 개 명시할 땐 & 사용
아직 좋아요/ 댓글수는 넣지 않았기 때문에 정렬 1,2만 되는 상태
페이지 매핑 시 정렬+검색된 것에 대한 페이지 처리도 추가해야함
BoardController
//페이지 처리
PageUtil page = new PageUtil(keyfield, keyword,
currentPage, count, 20, 10, "list","&order="+order);
"&order="+order 추가
order도 연동 시키므로 인해 keyfield/keyword/order 가 동시에 작동할 수 있게 함
<상세페이지 구현/ 글쓴이 프로필 사진 표시 -db저장된 사진>
MemberController
//2. 프로필 사진 출력(회원번호 지정/ 게시판)
@RequestMapping("/member/viewProfile")
public String getProfileByMem_num(@RequestParam int mem_num,HttpServletRequest request, Model model) {
MemberVO memberVO = memberService.selectMember(mem_num);
viewProfile(memberVO, request, model); //3번 코드
return "imageView";
}
2 추가
제목에 HTML태그 비허용, 글 상세 sql문 추가
BoardMapper.xml
<!-- 게시판 전체 목록/검색 목록 -->
<select id="selectList" parameterType="map" resultType="boardVO">
SELECT
*
FROM (SELECT
a.*,
rownum rnum
FROM (SELECT
board_num,
<![CDATA[
REPLACE(REPLACE(title,'<','<'),'>','>') title,
]]>
hit,
reg_date,
mem_num,
id,
nick_name
FROM spboard JOIN spmember
USING(mem_num)
<include refid="boardSearch"></include>
<include refid="boardOrder"></include>)a)
<![CDATA[
WHERE rnum >= #{start} AND rnum <= #{end}
]]>
</select>
<!-- 글 상세 -->
<select id="selectBoard" parameterType="integer">
SELECT
*
FROM spboard
JOIN spmember USING(mem_num)
LEFT OUTER JOIN spmember_detail USING(mem_num)
WHERE board_num=#{board_num}
</select>
replace(replace(title, '<', '<'), '>', '>') title
<를 < >를 > 로 변경하여 html태그를 사용하지 못하게 함 (일반문자로 replace)
조회수 증가
BoardMapper.java
@Update("UPDATE spboard SET hit=hit+1 WHERE board_num=#{board_num}")
public void updateHit(int board_num);
메서드 묶어서 처리
BoardServiceImpl
@Override
public BoardVO selectBoard(int board_num) {
return boardMapper.selectBoard(board_num);
}
@Override
public void updateHit(int board_num) {
boardMapper.updateHit(board_num);
}
kr.spring.util에 mbox에 올려주신 StringUtil 파일 붙여넣기
BoardController
/*===========================
* 게시판 글 상세
* ==========================*/
@RequestMapping("/board/detail")
public ModelAndView process(@RequestParam int board_num) {
log.debug("<<게시판 글 상세 board_num>> : " + board_num);
//해당 글의 조회수 증가
boardService.updateHit(board_num);
BoardVO board = boardService.selectBoard(board_num);
//제목에 태그를 허용하지 않음
board.setTitle(StringUtil.useNoHtml(board.getTitle()));
return new ModelAndView("boardView", "board", board); //tiles 설정명, 속성명, 속성값
}
설정파일에 글 상세 기재하기
tiles-def
board.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN"
"http://tiles.apache.org/dtds/tiles-config_3_0.dtd">
<tiles-definitions>
<definition name="boardList" extends="main">
<put-attribute name="title" value="게시판 목록"/>
<put-attribute name="body" value="/WEB-INF/views/board/boardList.jsp"/>
</definition>
<definition name="boardWrite" extends="main">
<put-attribute name="title" value="글쓰기"/>
<put-attribute name="body" value="/WEB-INF/views/board/boardWrite.jsp"/>
</definition>
<definition name="boardView" extends="main">
<put-attribute name="title" value="글 상세"/>
<put-attribute name="body" value="/WEB-INF/views/board/boardView.jsp"/>
</definition>
</tiles-definitions>
상세폼 수정
views
board
boardView.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="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<!-- 내용 시작 -->
<div class="page-main">
<h2>${board.title}</h2>
<ul class="detail-info">
<li>
<img src="${pageContext.request.contextPath}/member/viewProfile?mem_num=${board.mem_num}" width="40" height="40" class="my-photo">
</li>
<li>
<c:if test="${empty board.nick_name}">${board.id}</c:if>
<c:if test="${!empty board.nick_name}">${board.nick_name}</c:if>
<br>
<c:if test="${!empty board.modify_date}">
최근 수정일 : ${board.modify_date}
</c:if>
<c:if test="${empty board.modify_date}">
작성일 : ${board.reg_date}
</c:if>
조회 : ${board.hit}
</li>
</ul>
<c:if test="${!empty board.filename}">
<ul>
<li>첨부파일 : <a href="file?board_num=${board.board_num}">${board.filename}</a></li>
</ul>
</c:if>
<hr size="1" width="100%">
<c:if test="${fn:endsWith(board.filename, '.jpg') ||
fn:endsWith(board.filename, '.JPG') ||
fn:endsWith(board.filename, '.jpeg') ||
fn:endsWith(board.filename, '.JPEG') ||
fn:endsWith(board.filename, '.gif') ||
fn:endsWith(board.filename, '.GIF') ||
fn:endsWith(board.filename, '.png') ||
fn:endsWith(board.filename, '.PNG')}">
<div class="align-center">
<img src="${pageContext.request.contextPath}/upload/${board.filename}" class="detail-img">
</div>
</c:if>
<div class="detail-content">
${board.content}
</div>
<div>
<%-- 좋아요 --%>
<%-- 댓글수 --%>
</div>
<hr size="1" width="100%">
<div class="align-right">
<c:if test="${!empty user && user.mem_num == board.mem_num}">
<input type="button" value="수정" onclick="location.href='update?board_num=${board.board_num}'">
<input type="button" value="삭제" id="delete_btn">
<script type="text/javascript">
let delete_btn = document.getElementById('delete_btn');
delete_btn.onclick=function(){
let choice = confirm('삭제하시겠습니까?');
if(choice){
location.href='delete?board_num=${board.board_num}'
}
};
</script>
</c:if>
<input type="button" value="목록" onclick="location.href='list'">
</div>
<hr size="1" width="100%">
<%-- 댓글 작성 --%>
</div>
<!-- 내용 끝 -->
첨부파일 형태로 다운로드 받는 기능 + 만약 확장자가 jpeg,png 등 이미지라면 화면에 보여지는 (view) 형식도 추가
다운로드받는 downloadview는 선생님이 주신다 하심
화면에 이미지 출력하는 기능은 functions lib사용
endsWith=끝에 있는 글자 검색
fn:endsWith(board.filename, '.jpg')
board.filename의 끝 내용이 .jpg인것 찾기
=============================
제목 눌러서 글 상세 페이지 들어가면 사진, 글 모두 볼 수 있고 파일은 다운로드 링크도 생김 (아직 다운로드는 x)
<첨부파일 다운로드 기능 추가>
BoardController
/*===========================
* 파일 다운로드
* ==========================*/
@RequestMapping("/board/file")
public ModelAndView download(@RequestParam int board_num, HttpServletRequest request) {
BoardVO board = boardService.selectBoard(board_num);
//파일을 절대경로에서 읽어들여 byte[]로 변환
byte[] downloadFile = FileUtil.getBytes(request.getServletContext().
getRealPath("/upload")+"/"+board.getFilename());
//imageView->무조건view download View->무조건download
ModelAndView mav = new ModelAndView();
mav.setViewName("downloadView");
mav.addObject("downloadFile", downloadFile);
mav.addObject("filename", board.getFilename());
return mav;
}
imageView 는 view가 컨텐트타입
downloadView는 download가 컨텐트 타입
샘이 mbox에 공유해준 downloadView 받고 kr.spring.view에 넣기
byte[]로 변환한 downloadFile을 downloadView에서 받고 다운로드 받게 해줌
<수정 작업 시 파일만 삭제하기>
BoardMapper.java
@Update("UPDATE spboard SET filename='' WHERE board_num=#{board_num}")
public void deleteFile(int board_num);
사실상 delete가 아닌 ''로 update하는 것이기 때문에 filename='' 이라 코드를 작성
BoardService.java
public void deleteFile(int board_num);
BoardServiceImpl
@Override
public void deleteFile(int board_num) {
boardMapper.deleteFile(board_num);
}
컨트롤러에서 호출
BoardController
/*===========================
* 게시판 글 수정
* ==========================*/
//수정폼 호출
@GetMapping("/board/update")
public String formUpdate(@RequestParam int board_num,Model model){
BoardVO boardVO = boardService.selectBoard(board_num);
model.addAttribute("boardVO", boardVO);
return "boardModify"; //tiles설정
}
javascript로 jsp에서 삽입할거임
뷰 파일
views
board
boardModify.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!-- 내용 시작 -->
<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-3.6.0.min.js"></script>
<div class="page-main">
<h2>글 수정</h2>
<form:form action="update" modelAttribute="boardVO" id="update_form" enctype="multipart/form-data">
<form:hidden path="board_num"/>
<form:errors element="div" cssClass="error-color"/>
<ul>
<li>
<form:label path="title">제목</form:label>
<form:input path="title"/>
<form:errors path="title" cssClass="error-color"/>
</li>
<li>
<form:label path="content">내용</form:label>
<form:textarea path="content"/>
<form:errors path="content" cssClass="error-color"/>
</li>
<li>
<form:label path="upload">파일업로드</form:label>
<input type="file" name="upload" id="upload">
<c:if test="${!empty boardVO.filename}">
<div id="file_detail">(${boardVO.filename})파일이 등록되어 있습니다.
<input type="button" value="파일삭제" id="file_del">
</div>
<script type="text/javascript">
$(function(){
$('#file_del').click(function(){
let choice = confirm('삭제하시겠습니까?');
if(choice){
$.ajax({
url:'deleteFile',
data:{board_num:${boardVO.board_num}}, //el로 data읽어옴 ()가 ajax
type:'post',
dataType:'json',
success:function(param){
if(param.result == 'logout'){
alert('로그인 후 사용하세요');
}else if(param.result == 'success'){
$('#file_detail').hide();
}else{
alert('파일 삭제 오류 발생');
}
},
error:function(){
alert('네트워크 오류 발생');
}
});
}
});
});
</script>
</c:if>
</li>
</ul>
<div class="align-center">
<form:button>전송</form:button>
<input type="button" value="글 상세" onclick="location.href='detail?board_num=${boardVO.board_num}'">
</div>
</form:form>
</div>
<!-- 내용 끝 -->
upload 파일업로드 구문 script 추가 (파일삭제 버튼 id=file_del)
#file_del.click 시 confirm('삭제하시겠습니까?');
if(choice) -> ajax로 처리 url=deleteFile / data={board_num:${boardVO.board_num}}
result=logout 시 로그인 후 사용
success시 file_detail을 hide (숨김처리) 그외 오류는 alert창 띄움
**
()=ajax
{}=el로 데이터 읽어옴
매핑
tiles-def
board.xml
<definition name="boardModify" extends="main">
<put-attribute name="title" value="글 수정"/>
<put-attribute name="body" value="/WEB-INF/views/board/boardModify.jsp"/>
</definition>
=============================
로그인 후 글수정 들어가면 폼 보임
ajax 통합관리하는 컨트롤에서 호출
src/main/java
kr.spring.board.controller
BoardAjaxController
package kr.spring.board.controller;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import kr.spring.board.service.BoardService;
import kr.spring.board.vo.BoardVO;
import kr.spring.member.vo.MemberVO;
import kr.spring.util.FileUtil;
import lombok.extern.slf4j.Slf4j;
@Controller
@Slf4j
public class BoardAjaxController {
@Autowired
private BoardService boardService;
/* ===========================
* 부모글 업로드 파일 삭제
* =========================== */
@RequestMapping("/board/deleteFile")
@ResponseBody
public Map<String,String> processFile(int board_num,HttpSession session,HttpServletRequest request){
Map<String,String> mapJson = new HashMap<String,String>();
MemberVO user = (MemberVO)session.getAttribute("user");
if(user==null) { //로그아웃
mapJson.put("result", "logout");
}else {
BoardVO vo = boardService.selectBoard(board_num); //한건의 데이터 읽어오기
boardService.deleteFile(board_num);//db에서 파일 삭제
FileUtil.removeFile(request, vo.getFilename());//업로드 경로에서 파일 삭제
mapJson.put("result", "success");
}
return mapJson;
}
}
위 jsp에서 ajax에 설정한 url=deleteFile == RequestMapping
session으로부터 user정도 읽어온 후
user==null 일 시 result=logout
그외 상황인 경우
boardService.selectBoard (한건의 데이터 읽어와서 자바빈에 담기)
boardService.deleteFile (파일삭제)
FileUtil.removeFile(쓰레기파일 삭제)
result=success 순대로 처리
Map return
=============================
수정폼에서 파일삭제 누르면 폼에 저장되어 있던 파일이 안보이게 됨 (hide처리)
수정 기능은 아직 x