쌍용교육(JAVA)/SpringBoot

쌍용교육 -JSP수업 94일차 ch15SpringPage(5)

구 승 2024. 7. 1. 15:01

static/css =>common.css

/* 공통 테이블
---------------------*/
table.basic-table{
	width:100%;
	border:1px solid #000;
	border-collapse:collapse;
	margin-top:5px;
}
table.basic-table td, table.basic-table th{
	border:1px solid #000;
	padding:5px;
}
table.striped-table{
	width:100%;
	border:1px solid #FFF;
	border-collapse:collapse;
	font-size:15px;
	margin:7px 0;
}
table.striped-table td, table.striped-table th{
	padding:.7em .5em;  /* 0.7 0.5인데 생략가능 */
	vertical-align:middle;
}
table.striped-table th{
	font-weight:bold;
	background:#E1E1E1;
}
table.striped-table td{
	border-bottom:1px solid rgba(0,0,0.1);
}
table.striped-table tr:nth-child(odd){
	background-color:rgb(250,250,247);
}

BoardMapper.xml

 <!-- sql 태그와 include 태그를 이용해서 SQL문을 재사용 -->
	 <sql id="boardSearch"> <!-- 검색할 떄 -->
	 	<where>
	 		<if test="category != null and category != ''"> <!-- 카테고리가 있을 때 where절과 카테고리값 생성 -->
	 			category = #{category}
	 		</if>
	 		<if test="keyword !=null and keyword !=''">
	 			And
	 		</if>
	 		<if test="keyfield ==1">
	 			title LIKE '%' || #{keyword} || '%'
	 		</if>
	 		<if test="keyfield ==2">
	 			(id LIKE '%' || #{keyword} || '%' OR
	 			nick_name LIKE '%' || #{keyword} || '%')
	 		</if>
	 		<if test="keyfield ==3">
	 			content LIKE '%' || #{keyword} || '%'
	 		</if>
	 		<if test="keyfield ==4">
	 			(title LIKE '%' || #{keyword} || '%' OR
	 			content LIKE '%' || #{keyword} || '%')
	 		</if>
	 	</where>
	 </sql>
	 <sql id="boardOrder"> <!-- 정렬할 때 -->
	 	<if test="order ==1">
	 		ORDER BY board_num DESC
	 	</if>
	 	<if test="order ==2">
	 		ORDER BY hit DESC
	 	</if>
	 	<if test="order ==3">
	 		ORDER BY fav_cnt DESC NULLS LAST
	 	</if>
	 	<if test="order ==4">
	 		ORDER BY re_cnt DESC NULLS LAST
	 	</if>
	 </sql>
	 
	 <!-- 게시판 글 총레코드 수/검색 레코드 수 -->
	 <select id="selectRowCount" parameterType="map" resultType="integer">
	 	SELECT
	 		COUNT(*)
	 	FROM spboard JOIN spmember USING (mem_num)
	 	<include refid="boardSearch"></include>
	 </select>
	 <!-- 게시판 전체 목록/검색 목록 -->
	 <select id="selectList" parameterType="map" resultType="boardVO">
	 
	 
	 	SELECT
	 		*
	 	FROM (SELECT
	 			a.*,
	 			rownum rnum
	 		  FROM (SELECT
	 		  		  *
	 		  		FROM spboard JOIN spmember USING(mem_num)
	 		  		<include refid="boardSearch"></include>
	 		  		<include refid="boardOrder"></include>
	 		  		)a)
	<![CDATA[
	WHERE rnum >= #{start} AND rnum <= #{end}
	]]>
	</select>

BoardServiceImpl

@Override
	public List<BoardVO> selectList(Map<String, Object> map) {
		return boardMapper.selectList(map);
	}

	@Override
	public Integer selectRowCount(Map<String, Object> map) {
		return boardMapper.selectRowCount(map);
	}

BoardController

Map<String,Object> map = new HashMap<String,Object>();
		map.put("category",category);
		map.put("keyfield",keyfield);
		map.put("keyword",keyword);
		
		//전체,검색 레코드 수
		int count = boardService.selectRowCount(map);

ch12에 있던 pagingUtil.java 그대로 가져와서 같은 위치에 사용

 

그 후 BoardController 재수정

<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-3.7.1.min.js"></script>
//페이지 처리
		PagingUtil page = 
				new PagingUtil(keyfield,keyword, pageNum,
									count,20,10,"list",
									"&category="+category+"&order="+order);
		List<BoardVO> list = null;
		if(count >0) {
			map.put("order",order);
			map.put("start",page.getStartRow());
			map.put("end",page.getEndRow());
			
			list = boardService.selectList(map);
		}
		model.addAttribute("count",count);
		model.addAttribute("list",list);
		model.addAttribute("page",page.getPage());

 

boardList.jsp 

<div>
		<a href="list">전체</a> |
		<a href="list?category=1">자바</a> |
		<a href="list?category=2">데이터베이스</a> |
		<a href="list?category=3">자바스크립트</a> |
		<a href="list?category=4">기타</a>
	</div>
	<form action="list" id="search_form" method="get">
		<input type="hidden" name="category" value="${param.category}">
		<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="찾기">
			</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>
			<script type="text/javascript">
				$('#order').change(function(){
					location.href='list?category=${param.category}&keyfield='
								+$('#keyfield').val()
								+'&keyword='+$('#keyword').val()
								+'&order='+$('#oredr').val();
				});
			</script>
			<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 class="align-left"><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>

원하는 종류로 검색할 수 있으며, 전체❘자바❘데이터베이스 등으로 클릭해서 리스트를 볼 수있다.
자바스크립트 게시물이 없기 떄문에 표시가 안됨.
기타클릭시 기타로 분류된 게시물만 나온다.
최신순과 조회수는 작동하지만, 좋아요와 댓글수는 아직 만들지 않았기 때문에 에러가 난다.

BoardMapper.java

	@Select("SELECT * FROM spboard JOIN spmember USING (mem_num) LEFT OUTER JOIN spmember_detail USING(mem_num) WHERE board_num=#{board_num}") //LEFT OUTER를 하는 이유는 탈퇴했을 때 에러나는 것을 방지하기 위해
	public BoardVO selectBoard(Long board_num);
    @Update("UPDATE spboard SET hit=hit+1 WHERE board_num=#{board_num}")
	public void updateHit(Long board_num);

BoardServiceImpl

@Override
	public BoardVO selectBoard(Long board_num) {
	
		return boardMapper.selectBoard(board_num);
	}
@Override
	public void updateHit(Long board_num) {
		boardMapper.updateHit(board_num);
		
	}

BoardController.java

StringUtil.java
0.00MB

이 파일의 클래스를 사용하기위해 다운

/*=======================
	 * 게시판 글상세
	 *=======================*/
	@GetMapping("/board/detail")
	public ModelAndView process(long board_num) {
		log.debug("<<게시판 글 상세 - board_num>> :"+board_num);
		
		//해당 글의 조회수 증가
		boardService.updateHit(board_num);
		
		BoardVO board = boardService.selectBoard(board_num);
		
		//제목에 태그를 허용하지 않음
		board.setTitle(StringUtil.useNoHTML(board.getTitle()));
		
		//내용에 태그를 허용하지 않으면서 줄바꿈 처리
		board.setContent(StringUtil.useBrNoHTML(board.getContent()));
		
		
		
		return new ModelAndView("boardView","board",board);
	}

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.reg_date}
			</c:if>
			<c:if test="${!empty board_modify_date}">
			최근 수정일: ${board.modify_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>
	<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>
			const delete_btn = document.getElementById('delete_btn');
			delete_btn.onclick=function(){
				const choice = confirm('삭제하시겠습니까?');
				if (choice){
					location.replace('delete?board_num=${board.board_num}');
				}
			};
		</script>
		</c:if>
		<input type="button" value="목록" onclick="location.href='list'">
	</div>
	<hr size="1" width="100%">
	<!-- 댓글 UI 시작 -->
	
	<!-- 댓글 목록 출력 -->
	<!-- 댓글 UI 끝 -->
</div>
<!-- 게시판 글 상세 끝 -->

tile-def => board.xml

<definition name="boardView" extends="main">
		<put-attribute name="title" value="글상세"/>
		<put-attribute name="css" value="/WEB-INF/views/board/boardCSS.jsp"/>
		<put-attribute name="body" value="/WEB-INF/views/board/boardView.jsp"/>
	</definition>

상세페이지. 다만 아직 프로필은 나오지않는다. 첨부파일로 다운로드되지않음.
작성자 아이디로 로그인을 안하면 버튼이 목록만 나온다.

memberController

프로필 설정

//프로필 사진 출력(회원번호 지정)
	@GetMapping("/member/viewProfile")
	public String getProfileByMem_num(long mem_num,
									  HttpServletRequest request,
									  Model model) {
		MemberVO memberVO = memberService.selectMember(mem_num);
		
		viewProfile(memberVO,request,model);
		
		return "imageView";
	}

BoardController

첨부파일 다운로드하기

DownloadView.java
0.00MB

kr.spring.view에 넣기

//파일 다운로드
	@GetMapping("/board/file")
	public String download(long board_num, HttpServletRequest request, Model model) {
		
		BoardVO board = boardService.selectBoard(board_num);
		byte[] downloadFile = FileUtil.getbytes(request.getServletContext()
				                 .getRealPath("/upload")+"/"+board.getFilename());
		
		model.addAttribute("downloadFile",downloadFile);
		model.addAttribute("filename",board.getFilename());
		
		return "downloadView";
	}

기여운 강아지가 다운됐다

BoardMapper.java

파일삭제 하기위해

	@Update("UPDATE spboard SET filename='' WHERE board_num=#{board_num}")
	public void deleteFile(Long board_num);

BoardServiceImpl

@Override
	public void deleteFile(Long board_num) {
		boardMapper.deleteFile(board_num);
		
	}

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.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
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;

@Slf4j
@Controller
public class BoardAjaxController {
	@Autowired
	private BoardService boardService;
	
	/*========================
	 * 부모글
	 *========================*/
	//업로드 파일 삭제
	@PostMapping("/board/deleteFile")
	@ResponseBody
	public Map<String,String> processFile(long 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 db_board = boardService.selectBoard(board_num);
			//로그인한 회원번호와 작성자 회원번호 일치 여부 체크
			if(user.getMem_num() != db_board.getMem_num()) {
				//불일치
				mapJson.put("result","wrongAccess");
			}else {
				//일치
				boardService.deleteFile(board_num);
				FileUtil.removeFile(request,db_board.getFilename());
				
				mapJson.put("result","success");
			}
		}
		return mapJson;
	}
}

 

BoardController

/*=======================
	 * 게시판 글 수정
	 *=======================*/
	//수정 폼 호출
	@GetMapping("/board/update")
	public String formUpdate(long board_num, Model model) {
		BoardVO boardVO = boardService.selectBoard(board_num);
		
		model.addAttribute("boardVO",boardVO);
		
		return "boardModify"; 
	}

board.xml

<definition name="boardModify" extends="main">
		<put-attribute name="title" value="글수정"/>
		<put-attribute name="css" value="/WEB-INF/views/board/boardCSS.jsp"/>
		<put-attribute name="body" value="/WEB-INF/views/board/boardModify.jsp"/>
	</definition>

boardModify.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="form" uri="http://www.springframework.org/tags/form" %>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-3.7.1.min.js"></script>
<!-- 게시판 글수정 시작 -->
<div class="page-main">
	<h2>글쓰기</h2>
	<form:form action="update" id="board_modify"
				enctype="multipart/form-data"
	                            modelAttribute="boardVO">
	    <form:hidden path="board_num"/>
		<ul>
			<li>
				<form:label path="category">분류</form:label>
				<form:select path="category"> <!-- 기존에 지정해둔 value값으로 자동으로 지정되어있음 -->
					<form:option value="1">자바</form:option>
					<form:option value="2">데이터베이스</form:option>
					<form:option value="3">자바스크립드</form:option>
					<form:option value="4">기타</form:option>
				</form:select>
				<form:errors path="category" cssClass="error-color"/>                            
			</li>
			<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(){
							const choice = confirm('삭제하시겠습니까?');
							if(choice){
								$.ajax({
									url:'deleteFile',
									data:{board_num:${boardVO.board_num}},
									type:'post',
									dataType:'json',
									success:function(param){
										if(param.result=='logout'){
											alert('로그인 후 사용하세요');
										}else if(param.result == 'wrongAccess'){
											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 class="default-btn">전송</form:button>
			<input type="button" value="목록"
			  class="default-btn"
			  onclick="location.href='list'">
		</div>                           
	</form:form>
</div>
<!-- 게시판 글수정 끝 -->

파일이 삭제된 모습을 볼 수 있다.


BoardMapper.xml

로 SQL 작성

-- 글 수정 부분

<!-- 게시판 글수정 -->
	<update id="updateBoard" parameterType="boardVO">
		UPDATE spboard SET
			<if test="filename != null">
			filename = #{filename},
			</if>
			title = #{title},
			content = #{content},
			ip = #{ip},
			modify_date = SYSDATE
		WHERE board_num = #{board_num}
	</update>

BoardServiceImpl

@Override
	public void updateBoard(BoardVO board) {
		boardMapper.updateBoard(board);
		
	}

BoardController

//수정 폼에서 전송된 데이터 처리
	@PostMapping("/board/update")
	public String submitUpdate(@Valid BoardVO boardVO,  //Valid 다음에는 바로 BindingResult가 나와야된다. 사이에 다른 값을 넣으면 에러남
								BindingResult result,
								Model model,
								HttpServletRequest request) throws IllegalStateException, IOException {
		log.debug("<<게시판 글 수정>> : "+boardVO);
		
		//유효성 체크 결과 오류가 있으면 폼 호출
		if(result.hasErrors()) {
			//title 또는 content가 입력되지 않아서 유효성 체크에 걸리면
			//파일 정보를 잃어버리기 때문에 폼을 호출할 때 다시 파일 정보를 세팅해야 함.
			BoardVO vo = boardService.selectBoard(boardVO.getBoard_num());
			boardVO.setFilename(vo.getFilename());
			
			return "boardModify";
		}
		//오류가 나지 않았을 때
		//DB에 저장된 파일 정보 구하기
		BoardVO db_board = boardService.selectBoard(boardVO.getBoard_num());
		
		//파일명 세팅(FileUtil.createFile에서 파일이 없으면 null 처리함)
		boardVO.setFilename(FileUtil.createFile(request,boardVO.getUpload()));
		
		return "common/resultAlert";
	}