<복습>
Intercept
공용으로 사용할 수 있는 폼을 설정할 때 주로 사용
로그인을 안했다? -> 로그인 체크 후 로그인 폼으로 보내기 등 작업 수행 가능
return 하는 방식이 redirect도 되고 forward방식으로도 가능 (우리는 redirect로 했음)
그후
LoginCheckInterceptor -> AppConfig (매핑/설정) 로 가서 설정함
<좋아요 표시>
Mapper에서 sql문 작성한 후 ServiceImpl에서 호출하기
ServiceImpl
@Override
public BoardFavVO selectFav(BoardFavVO fav) {
return boardMapper.selectFav(fav);
}
@Override
public int selectFavCount(int board_num) {
return boardMapper.selectFavCount(board_num);
}
컨트롤러에서 Ajax 넣기
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.BoardFavVO;
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;
/*=============================
* 부모글 업로드 파일 삭제
============================*/
@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);
//삭제(DB)
boardService.deleteFile(board_num);
//업로드 경로에 있는 파일 삭제
FileUtil.removeFile(request, vo.getFilename());
mapJson.put("result", "success");
}
return mapJson;
}
/*=============================
* 부모글 좋아요 읽기
============================*/
@RequestMapping("/board/getFav")
@ResponseBody
public Map<String,Object> getFav(BoardFavVO fav, HttpSession session){
log.debug("<<게시판 좋아요 BoardFavVO>> : " + fav);
Map<String,Object> mapJson = new HashMap<String,Object>();
//유저 구하기
MemberVO user = (MemberVO)session.getAttribute("user");
if(user==null) {
mapJson.put("status", "noFav");
}else {
//로그인된 회원번호 셋팅
fav.setMem_num(user.getMem_num());
BoardFavVO boardFav = boardService.selectFav(fav); //user에게 있는지 없는지 알려줌
if(boardFav!=null) { //해당 user가 좋아요를 눌렀을 때
mapJson.put("status", "yesFav");
}else {
mapJson.put("status", "noFav");
}
}
//좋아요 수
mapJson.put("count", boardService.selectFavCount(fav.getBoard_num()));
return mapJson;
}
}
1.
if(user==null){ // 로그인한 유저가 아닐 경우 >> status=noFav
2.
else{
(boardFav!=null) 로그인한 유저가 좋아요를 눌렀을 때 >> yesFav
누르지 않았을 때 >> noFav
mbox에서 내려받은 좋아요 사진 static.images에 넣기
사진 폴더에 넣을 때에는 contextroot로 인식해서 파일 경로 만들 때에 webapp에 함
but 이미 서버에 있는 링크는 static으로 인식, 그래서 static.images로 빼온다 함
js파일 만들기
static
js
board.fav.js (javaScript로 생성)
$(function(){
/*---------------------------
* 좋아요 읽기
*---------------------------*/
//좋아요 선택 여부와 선택한 총 개수를 표시
function selectFav(board_num){
$.ajax({
url:'getFav',
type:'post',
data:{board_num:board_num},
dataType:'json',
success:function(param){
displayFav(param);
},
error:function(){
alert('네트워크 오류');
}
});
}
/*---------------------------
* 좋아요 표시 공통 함수
*---------------------------*/
function displayFav(param){
let output;
if(param.status == 'yesFav'){
output = '../images/fav02.gif';
}else if(param.status == 'noFav'){
output = '../images/fav01.gif';
}else{
alert('좋아요 표시 오류 발생');
}
//문서 객체에 정보 추가
$('#output_fav').attr('src',output);
$('#output_fcount').text(param.count);
}
//초기 데이터 표시
//맨 위에서 명시한 메서드 호출
selectFav($('#output_fav').attr('data-num'));
});
selectFav
data:{board_num:board_num} >> 데이터로 board_num을 보내는 것 (데이터 관련해서는 {} 씀)
dataType:'json' >> 반환받는 데이터 타입이 json
success 시 displayFav(param); 호출
==============================
displayFav
param에 값이 전달되면 status와 count가 날아와서 status로 처리,
$('#output_fav').attr('src',output); >> 상황에 따라 이미지 보이기
$('#output_fcount').text(param.count); >> count (좋아요 수) 화면에 표시
==============================
selectFav($('#output_fav').attr('data-num'));
#output_fav >> 커스텀 태그를 넣어서 board_num값을 읽고 표시하려고 그런다함 (?)..
상세페이지 수정
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" %>
<!-- 내용 시작 -->
<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-3.6.0.min.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/videoAdapter.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/board.fav.js"></script>
<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>
<%-- 좋아요 --%>
<img id="output_fav" data-num="${board.board_num}" src="${pageContext.request.contextPath}/images/fav01.gif" width="40">
<span id="output_fcount"></span>
<%-- 댓글수 --%>
</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>
<!-- 내용 끝 -->
1.
script 추가 > 아까 생성한 board.jav.js
2.
좋아요 부분 코드 작성
==============================
실행하면 빈 하트+0 이 보임
페이지 우클릭 > 검사 >
1) 콘솔에 에러가 있는지 없는지
2) network - 페이로드 board_num
미리보기 count status가 보임
좋아요 스타일 추가
css
layout.css
/* 좋아요 */
#output_fav{
cursor:pointer;
}
#output_fcount{
width:50px;
display:inline-block;
}
<좋아요 기능 구현>
Mapper에서 sql문 작성
BoardMapper.java
insert, delete 코드 작성
@Insert("INSERT INTO spboard_fav (board_num,mem_num) VALUES (#{board_num},#{mem_num})")
public void insertFav(BoardFavVO fav);
@Delete("DELETE FROM spboard_fav WHERE board_num=#{board_num} AND mem_num=#{mem_num}")
public void deleteFav(BoardFavVO boardFav);
서비스 파일에서 호출
BoardServiceImpl
@Override
public void insertFav(BoardFavVO fav) {
boardMapper.insertFav(fav);
}
@Override
public void deleteFav(BoardFavVO boardFav) {
boardMapper.deleteFav(boardFav);
}
컨트롤러 등록
BoardAjaxController
/*=============================
* 부모글 좋아요 등록/ 삭제
* ============================*/
@RequestMapping("/board/writeFav")
@ResponseBody
public Map<String,Object> writeFav(BoardFavVO fav, HttpSession session){
log.debug("<<부모글 좋아요 등록/삭제 BoardFavVO>> : " + fav);
Map<String,Object> mapJson = new HashMap<String,Object>();
//로그인 상태 확인 후 동작 처리
MemberVO user = (MemberVO)session.getAttribute("user");
if(user==null) {
mapJson.put("result", "logout");
}else {
//로그인된 회원번호 셋팅
fav.setMem_num(user.getMem_num());
//이전에 좋아요를 등록했는지 여부 확인 (토글 형태)
BoardFavVO boardFavVO = boardService.selectFav(fav);
if(boardFavVO!=null) { //좋아요를 이미 등록
boardService.deleteFav(fav);
mapJson.put("status", "noFav");
}else {//좋아요 미등록
boardService.insertFav(fav);
mapJson.put("status", "yesFav");
}
mapJson.put("result", "success");
mapJson.put("count", boardService.selectFavCount(fav.getBoard_num()));
}
return mapJson;
}
컨트롤러의 @RequestMapping("/board/writeFav") --> js파일의 url 명과 동일
js파일 수정
board.jav.js
/*---------------------------
* 좋아요 등록/삭제
*---------------------------*/
$('#output_fav').click(function(){
$.ajax({
url:'writeFav',
type:'post',
data:{board_num:$('#output_fav').attr('data-num')},
dataType:'json',
success:function(param){
if(param.result == 'logout'){
alert('로그인 후 좋아요를 눌러주세요');
}else if(param.result == 'success'){
displayFav(param);
}else{
alert('등록/삭제 시 오류 발생');
}
},
error:function(){
alert('네트워크 오류!');
}
});
});
data:{board_num:$('#output_fav').attr('data-num')}
data-num에 board_num을 넣어놨는데 data-num이라는 설정 태그로부터 데이터를 읽어와서 전송한다는 뜻
==============================
하고 실행하면
비로그인 시 --> 로그인 후 좋아요를 눌러주세요
로그인 후 좋아요 체크 정상적으로 작동
정렬도 좋아요 순으로 정렬이 됨
<댓글 기본 설정>
테이블 생성하기
table.sql
--게시판 댓글
create table spboard_reply(
re_num number not null,
re_content varchar2(900) not null,
re_date date default sysdate not null,
re_mdate date, --글 수정시 사용
re_ip varchar2(40) not null,
board_num number not null,
mem_num number not null,
constraint spboard_reply_pk primary key (re_num),
constraint spboard_reply_fk1 foreign key (board_num) references spboard (board_num),
constraint spboard_reply_fk2 foreign key (mem_num) references spmember (mem_num)
);
create sequence spreply_seq;
댓글도 vo, servicedao, controller, ajax통신 진행하는 과정 모두 똑같음
VO 생성하기
kr.spring.board.vo
BoardReplyVO (class)
package kr.spring.board.vo;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class BoardReplyVO {
private int re_num;
private String re_content;
private String re_date;
private String re_mdate; //연월일시분초가 아니라 하루전 이틀전 이렇게 변환 작업할거라서 string 처리
private String re_ip;
private int board_num;
private int mem_num;
private String id;
private String nick_name;
}
댓글 부분 DAO 작성
BoardMapper.java 그대로 사용
//댓글
public List<BoardReplyVO> selectListReply(Map<String,Object> map);
public int selectRowCountReply(Map<String,Object> map);
public BoardReplyVO selectReply(int re_num); //한건의 데이터
public void insertReply(BoardReplyVO boardReply);
public void updateReply(BoardReplyVO boardReply);
public void deleteReply(int re_num);
@Delete("DELETE FROM spboard_reply WHERE board_num=#{board_num}")
public void deleteReplyByBoardNum(int board_num); //부모글 삭제 시 댓글이 존재하면 부모글 삭제 전 댓글 삭제
댓글도 부모글 지울 때 자식글도 같이 지워야 오류가 안남
그래서 좋아요와 똑같이 deleteReplyByBoardNum 메서드 추가
부모글 삭제 메서드에 댓글 삭제도 추가하기
BoardServiceImpl
@Override
public void deleteBoard(int board_num) {
//부모글 좋아요 삭제
boardMapper.deleteFavByBoardNum(board_num);
//댓글이 존재하면 댓글을 우선 삭제하고 부모글 삭제
boardMapper.deleteReplyByBoardNum(board_num);
//부모글 삭제
boardMapper.deleteBoard(board_num);
}
게시판 정렬 댓글순 코드도 추가
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,
re_cnt,
fav_cnt
FROM spboard
LEFT OUTER JOIN (SELECT COUNT(*) re_cnt, board_num FROM spboard_reply GROUP BY board_num) USING(board_num)
LEFT OUTER JOIN (SELECT COUNT(*) fav_cnt, board_num FROM spboard_fav GROUP BY board_num) USING(board_num)
JOIN spmember USING(mem_num)
<include refid="boardSearch"></include>
<include refid="boardOrder"></include>)a)
<![CDATA[
WHERE rnum >= #{start} AND rnum <= #{end}
]]>
</select>
re_cnt, left outer join 코드 추가
정렬 1~4 (최신순~댓글수)
다 테스트 해보고 에러만 안나면 됨
댓글 준비 완료
<댓글창 표시하기>
서비스 파일에 Mapper에서 설정한 dao 붙여넣기
BoardService,java
//댓글
public List<BoardReplyVO> selectListReply(Map<String,Object> map);
public int selectRowCountReply(Map<String,Object> map);
public BoardReplyVO selectReply(int re_num); //한건의 데이터
public void insertReply(BoardReplyVO boardReply);
public void updateReply(BoardReplyVO boardReply);
public void deleteReply(int re_num);
BoardServiceImpl >> 에러창 뜰텐데 그럼 눌러서 메서드 추가하면 됨
mbox에서 로딩 이미지 내려받고 static images 안에 붙여넣기
상세 화면 UI 변경
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" %>
<!-- 내용 시작 -->
<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-3.6.0.min.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/videoAdapter.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/board.fav.js"></script>
<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>
<%-- 좋아요 --%>
<img id="output_fav" data-num="${board.board_num}" src="${pageContext.request.contextPath}/images/fav01.gif" width="40">
<span id="output_fcount"></span>
<%-- 댓글수 --%>
<span id="output_rcount"></span>
</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 id="reply_div">
<span class="re-title">댓글 달기</span>
<form id="re_form">
<input type="hidden" name="board_num" value="${board.board_num}" id="board_num">
<textarea rows="3" cols="50" name="re_content" id="re_content" class="rep-content"
<c:if test="${empty user}">disabled="disabled"</c:if>
><c:if test="${empty user}">로그인해야 작성할 수 있습니다.</c:if></textarea>
<c:if test="${!empty user}">
<div id="re_first">
<span class="letter-count">300/300</span>
</div>
<div id="re_second" class="align-right">
<input type="submit" value="전송">
</div>
</c:if>
</form>
</div>
<!-- 댓글 목록 출력 시작 -->
<div id="output"></div>
<div class="paging-button" style="display:none;">
<input type="button" value="더보기">
</div>
<div id="loading" style="display:none;"> <%-- 로딩바 생성 --%>
<img src="${pageContext.request.contextPath}/images/loading.gif" width="100" height="100">
</div>
<!-- 댓글 목록 출력 끝 -->
<!-- 댓글 끝 -->
</div>
<!-- 내용 끝 -->
<c:if test="${empty user}">disabled="disabled"</c:if>
로그인하지 않았을 때엔 댓글 작성 칸 비활성화
스타일 처리
layout.css
/* 댓글 */
div#reply_div{
padding:5px 10px 40px 10px;
margin-top:10px;
background-color:#eeeeee;
}
form#re_form{
width:650px;
border:none;
}
span.re-title{
color:#000;
font-size:12pt;
line-height:200%;
}
span.letter-count{
font-size:10pt;
color:#999;
}
textarea.rep-content{
width:90%;
height:50px;
margin:10px 10px;
}
div#re_first,div#mre_first{
float:left;
width:70%;
padding-left:15px;
margin-bottom:10px;
}
div#loading{
width:100px;
height:50px;
/* 정중앙에 div를 배치하기 위한 설정 */
position:absolute;
top:50%;
left:50%;
transform:translate(-50%,-50%);
/* 하위요소를 수직으로 쌓을 수 있는 공간 만들기 - flex*/
display:flex;
/* 세로 정렬 */
align-items:center;
/* 가로 정렬 */
justify-content:center;
}
div.paging-button{
text-align:right;
}
div#output{
clear:both;
}
form#mre-form{
border:none;
margin:5px;
}
실행 후 상세페이지 들어갔을 때
비로그인 상태 -> 댓글창 비활성화 / 로그인하라고 메시지 뜸
로그인 상태 -> 댓글창 활성화
<댓글 등록하기>
dao파일에 sql문 작성하기
BoardMapper.xml
(insert가 좀 길어서 xml 파일에 기재)
<!-- 댓글 등록 -->
<insert id="insertReply" parameterType="boardReplyVO">
INSERT INTO spboard_reply(
re_num,
re_content,
re_ip,
board_num,
mem_num)
VALUES (
spreply_seq.nextval,
#{re_content},
#{re_ip},
#{board_num},
#{mem_num})
</insert>
Service파일에 호출하기
BoardServiceImpl
@Override
public void insertReply(BoardReplyVO boardReply) {
boardMapper.insertReply(boardReply);
}
'학원 > spring' 카테고리의 다른 글
1.23 (tiles-댓글 수정,삭제,duration / 채팅 테이블 생성) (0) | 2024.01.23 |
---|---|
1.22 (tiles-댓글 작성, 목록 처리/더보기 버튼) (0) | 2024.01.22 |
1.16 (tiles-게시판 기본폼 호출, 글등록, 목록처리(+검색)) (0) | 2024.01.17 |
1.15 (spring tiles (db연동)- 회원관리 끝 / 게시판 시작) (0) | 2024.01.15 |
1.12 (ch15(tiles db파일 저장)-회원가입,로그인,로그아웃,my페이지) (1) | 2024.01.14 |