목차
sql => table.sql
--답글
create table spboard_response(
te_num number not null,
te_content varchar2(900) not null,
te_date date default sysdate not null,
te_mdate date,
te_parent_num number not null, --부모글의 번호가 들어감, 자식 글이 아니라 부모글인 경우 0
te_depth number not null, --자식의 깊이, 부모글의 자식글 A 1, 자식글A의 자식글B 2, 부모글일 경우 0
te_ip varchar2(40) not null,
re_num number not null,
mem_num number not null,
constraint spboard_treply_pk primary key (te_num),
constraint treply_reply_fk1 foreign key (re_num) references spboard_reply (re_num),
constraint treply_spmember_fk2 foreign key (mem_num) references spmember (mem_num)
);
create sequence response_seq;
BoardResponseVO 생성
package kr.spring.board.vo;
import kr.spring.util.DurationFromNow;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class BoardResponseVO {
private long te_num;
private String te_content;
private long te_parent_num; //부모번호
private int te_depth; //숫자
private String te_date;
private String te_mdate;
private String te_ip;
private long re_num;
private long mem_num;
private String id;
private String nick_name;
private String parent_id;
private String pnick_name;
public void setTe_date(String te_date) {
this.te_date = DurationFromNow.getTimeDiffLabel(te_date);
}
public void setTe_mdate(String te_mdate) {
DurationFromNow.getTimeDiffLabel(te_mdate);
}
}
BoardMapper.java
//답글(대댓글)
public List<BoardResponseVO> selectList(Long re_num);
public BoardResponseVO selectResponse(Long te_num);
public void insertResponse(BoardResponseVO boardResponse);
public void updateResponse(BoardResponseVO boardResponse);
public void deleteResponse(Long te_num);
//댓글 삭제시 자식글인 답글을 모두 삭제
public void deleteResponseByReNum(Long re_num);
//부모글 삭제시 댓글의 답글이 존재하면 댓글 번호를 구해서 답글 삭제
@Delete("DELETE FROM spboard_response WHERE re_num IN (SELECT re_num FROM spboard_reply WHERE board_num=#{board_num})")
public void deleteResponseByBoardNum(Long board_num);
BoardServiceImpl
//답글 삭제
boardMapper.deleteResponseByBoardNum(board_num);
//답글
boardMapper.deleteResponseByReNum(re_num);
133라인
<!-- 댓글 목록 -->
라인의 sql문에 LEFT OUTER JOIN 추가
LEFT OUTER JOIN (SELECT re_num,COUNT(*) resp_cnt
FROM spboard_response GROUP BY re_num)
USING(re_num)
<!-- 답글등록 -->
<insert id="insertResponse" parameterType="boardResponseVO">
INSERT INTO spboard_response(
te_num,
te_content,
te_parent_num,
te_depth,
te_ip,
re_num,
mem_num)
VALUES (
response_seq.nextval,
#{te_content},
#{te_parent_num},
#{te_depth},
#{te_ip},
#{re_num},
#{mem_num})
</insert>
BoardService 인터페이스
//답글(대댓글)
public List<BoardResponseVO> selectList(Long re_num);
public BoardResponseVO selectResponse(Long te_num);
public void insertResponse(BoardResponseVO boardResponse);
public void updateResponse(BoardResponseVO boardResponse);
public void deleteResponse(Long te_num);
BoardServiceImpl
@Override
public void insertResponse(BoardResponseVO boardResponse) {
boardMapper.insertResponse(boardResponse);
}
BoardAjaxController
/*===================
답글 등록
===================*/
@PostMapping("/board/writeResponse")
@ResponseBody
public Map<String,String> writeREsponse(BoardResponseVO boardResponseVO,
HttpSession session,
HttpServletRequest request){
log.debug("<<답글 등록>> :" + boardResponseVO);
Map<String,String> mapJson = new HashMap<String, String>();
MemberVO user = (MemberVO)session.getAttribute("user");
if(user==null) {
//로그인이 되지 않는 경우
mapJson.put("result","logout");
}else {
//회원번호 저장
boardResponseVO.setMem_num(user.getMem_num());
//ip 저장
boardResponseVO.setTe_ip(request.getRemoteAddr());
//답글 등록
boardService.insertResponse(boardResponseVO);
mapJson.put("result", "success");
}
return mapJson;
}
board.reply.js
83라인부터
//답글 시작
if(param.user_num){
output +=' <input type="button" data-num='+item.re_num+'" data-parent="0" data-depth="0" value="답글 작성" class="response-btn">';
}
if(item.resp_cnt > 0){
output +='<div><input type="button" data-status="0" data-num='+item.re_num+'" value="▲ 답글 '+item_resp_cnt+'" class="rescontent-btn"></div>';
}else{
output +='<div><input type="button" data-status="0" data-num='+item.re_num+'" value="▲ 답글 0" class="rescontent-btn" style="display:none;"></div>';
}
//답글 끝
output += '</div>';
output += '</div>';
383라인 시작
183라인에있는 //댓글 수정폼 UI 아래값들을 복붙 후 modifyUI를 responseUI로 변경 및 태그명 등을 변경
/*===================
답글 등록
===================*/
//답글 작성 버튼 클릭시 답글 작성 폼 노출
$(document).on('click','.response-btn,.response2-btn',function(){
//모든 폼 초기화
initResponseForm();
//클릭하면 모든 답글 작성 버튼을 노출시키고 클릭한 답글 작성 버튼만 숨기기
$(this).hide();
//댓글번호
let re_num = $(this).attr('data-num');
//부모 글번호
let te_parent_num = $(this).attr('data-parent');
//깊이
let te_depth = $(this).attr('data-depth');
console.log(te_parent_num + ',' + te_depth);
//답글 작성 폼 UI
let responseUI = '<form id="resp_form">';
responseUI += '<input type="hidden" name="re_num" id="re_num" value="'+re_num+'">';
responseUI += '<input type="hidden" name="te_parent_num" value="'+te_parent_num+'">';
responseUI += '<input type="hidden" name="te_depth" value="'+te_depth+'">';
responseUI += '<textarea rows="3" cols="50" name="te_content" id="resp_content" class="rep-content"></textarea>';
responseUI += '<div id="resp_first"><span class="letter-count">300/300</span></div>';
responseUI += '<div id="resp_second" class="align-right">';
responseUI += ' <input type="submit" value="답글 작성">';
responseUI += ' <input type="button" value="취소" class="resp-reset">';
responseUI += '</div>';
responseUI += '</form>';
//답글 작성폼을 답글을 작성하고자하는 데이터가 있는 div에 노출
$(this).after(responseUI);
});
//답글에서 취소버튼 클릭시 답글 폼 초기화
$(document).on('click','.resp-reset',function(){
initResponseForm();
});
//답글 작성 폼 초기화
function initResponseForm(){
$('.response-btn,.response2-btn').show();
$('#resp_form').remove();
}
//답글 등록
$(document).on('submit','#resp_form',function(event){
let resp_form = $(this);
if($('#resp_content').val().trim()==''){
alert('내용을 입력하세요!');
$('#resp_content').val('').focus();
return false;
}
//폼에 입력한 데이터 반환
let form_data = $(this).serialize();
//댓글 번호
let re_num = $(this).find('#resp_num').val();
//서버와 통신
$.ajax({
url:'writeResponse',
type:'post',
data:form_data,
dataType:'json',
success:function(param){
if(param.result == 'logout'){
alert('로그인해야 답글을 작성할 수 있습니다.');
}else if(param.result == 'success'){
//답글 목록 호출
//getListResponse(re_num,resp_form.parents('.item'));
initResponseForm();
}else{
alert('답글 작성 오류 발생');
}
},
error:function(){
alert('네트워크 오류 발생');
}
});
//기본 이벤트 제거
event.preventDefault();
});
board.css 내용수정 및 추가
버튼들 추가
.modify-btn,.delete-btn,.resp-modify-btn,.resp-delete-btn,
.response-btn,
.response2-btn,
.paging-button input{
padding:4px, 20px;
border:1px solid #f0f2f1;
border-radius:20px;
color:#6e6e6e;
background-color:#f0f2f1;
font-weight:bold;
cursor:pointer;
}
.modify-btn:hover,.delete-btn:hover,.resp-modify-btn:hover,.resp-delete-btn:hover,
.response-btn:hover,
.response2-btn:hover,
.paging-button input:hover{
background-color:#e1e3e2;
color:#000000;
transition:0.2s ease-out;
height:30px;
}
.rescontent-btn{
width:300px;
margin:10px 10px 0 10px;
padding:4px 20px;
border-radius:20px;
color:#6e6e6e;
background-color:#f0f2f1;
font-weight:bold;
cursor:pointer;
}
.rescontent-btn:hover{
background-color:#e1e3e2;
color:#000000;
transition:0.2s ease-out; /* 부드럽게 움직이고 전환되게 하는 효과 */
height:30px;
}
div.respitem{
margin-top:20px;
padding-left:50px;
}


sqldeveloper에서 값이 들어가있는지 확인한다.
'쌍용교육(JAVA) > SpringBoot' 카테고리의 다른 글
쌍용교육 -JSP수업 101일차 ch15SpringPage(12) (0) | 2024.07.10 |
---|---|
쌍용교육 -JSP수업 100일차 ch15SpringPage(11) (0) | 2024.07.09 |
쌍용교육 -JSP수업 98일차 ch15SpringPage(9) (0) | 2024.07.05 |
쌍용교육 -JSP수업 97일차 ch15SpringPage(8) (0) | 2024.07.04 |
쌍용교육 -JSP수업 96일차 ch15SpringPage(7) (0) | 2024.07.03 |
sql => table.sql
--답글
create table spboard_response(
te_num number not null,
te_content varchar2(900) not null,
te_date date default sysdate not null,
te_mdate date,
te_parent_num number not null, --부모글의 번호가 들어감, 자식 글이 아니라 부모글인 경우 0
te_depth number not null, --자식의 깊이, 부모글의 자식글 A 1, 자식글A의 자식글B 2, 부모글일 경우 0
te_ip varchar2(40) not null,
re_num number not null,
mem_num number not null,
constraint spboard_treply_pk primary key (te_num),
constraint treply_reply_fk1 foreign key (re_num) references spboard_reply (re_num),
constraint treply_spmember_fk2 foreign key (mem_num) references spmember (mem_num)
);
create sequence response_seq;
BoardResponseVO 생성
package kr.spring.board.vo;
import kr.spring.util.DurationFromNow;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class BoardResponseVO {
private long te_num;
private String te_content;
private long te_parent_num; //부모번호
private int te_depth; //숫자
private String te_date;
private String te_mdate;
private String te_ip;
private long re_num;
private long mem_num;
private String id;
private String nick_name;
private String parent_id;
private String pnick_name;
public void setTe_date(String te_date) {
this.te_date = DurationFromNow.getTimeDiffLabel(te_date);
}
public void setTe_mdate(String te_mdate) {
DurationFromNow.getTimeDiffLabel(te_mdate);
}
}
BoardMapper.java
//답글(대댓글)
public List<BoardResponseVO> selectList(Long re_num);
public BoardResponseVO selectResponse(Long te_num);
public void insertResponse(BoardResponseVO boardResponse);
public void updateResponse(BoardResponseVO boardResponse);
public void deleteResponse(Long te_num);
//댓글 삭제시 자식글인 답글을 모두 삭제
public void deleteResponseByReNum(Long re_num);
//부모글 삭제시 댓글의 답글이 존재하면 댓글 번호를 구해서 답글 삭제
@Delete("DELETE FROM spboard_response WHERE re_num IN (SELECT re_num FROM spboard_reply WHERE board_num=#{board_num})")
public void deleteResponseByBoardNum(Long board_num);
BoardServiceImpl
//답글 삭제
boardMapper.deleteResponseByBoardNum(board_num);
//답글
boardMapper.deleteResponseByReNum(re_num);
133라인
<!-- 댓글 목록 -->
라인의 sql문에 LEFT OUTER JOIN 추가
LEFT OUTER JOIN (SELECT re_num,COUNT(*) resp_cnt
FROM spboard_response GROUP BY re_num)
USING(re_num)
<!-- 답글등록 -->
<insert id="insertResponse" parameterType="boardResponseVO">
INSERT INTO spboard_response(
te_num,
te_content,
te_parent_num,
te_depth,
te_ip,
re_num,
mem_num)
VALUES (
response_seq.nextval,
#{te_content},
#{te_parent_num},
#{te_depth},
#{te_ip},
#{re_num},
#{mem_num})
</insert>
BoardService 인터페이스
//답글(대댓글)
public List<BoardResponseVO> selectList(Long re_num);
public BoardResponseVO selectResponse(Long te_num);
public void insertResponse(BoardResponseVO boardResponse);
public void updateResponse(BoardResponseVO boardResponse);
public void deleteResponse(Long te_num);
BoardServiceImpl
@Override
public void insertResponse(BoardResponseVO boardResponse) {
boardMapper.insertResponse(boardResponse);
}
BoardAjaxController
/*===================
답글 등록
===================*/
@PostMapping("/board/writeResponse")
@ResponseBody
public Map<String,String> writeREsponse(BoardResponseVO boardResponseVO,
HttpSession session,
HttpServletRequest request){
log.debug("<<답글 등록>> :" + boardResponseVO);
Map<String,String> mapJson = new HashMap<String, String>();
MemberVO user = (MemberVO)session.getAttribute("user");
if(user==null) {
//로그인이 되지 않는 경우
mapJson.put("result","logout");
}else {
//회원번호 저장
boardResponseVO.setMem_num(user.getMem_num());
//ip 저장
boardResponseVO.setTe_ip(request.getRemoteAddr());
//답글 등록
boardService.insertResponse(boardResponseVO);
mapJson.put("result", "success");
}
return mapJson;
}
board.reply.js
83라인부터
//답글 시작
if(param.user_num){
output +=' <input type="button" data-num='+item.re_num+'" data-parent="0" data-depth="0" value="답글 작성" class="response-btn">';
}
if(item.resp_cnt > 0){
output +='<div><input type="button" data-status="0" data-num='+item.re_num+'" value="▲ 답글 '+item_resp_cnt+'" class="rescontent-btn"></div>';
}else{
output +='<div><input type="button" data-status="0" data-num='+item.re_num+'" value="▲ 답글 0" class="rescontent-btn" style="display:none;"></div>';
}
//답글 끝
output += '</div>';
output += '</div>';
383라인 시작
183라인에있는 //댓글 수정폼 UI 아래값들을 복붙 후 modifyUI를 responseUI로 변경 및 태그명 등을 변경
/*===================
답글 등록
===================*/
//답글 작성 버튼 클릭시 답글 작성 폼 노출
$(document).on('click','.response-btn,.response2-btn',function(){
//모든 폼 초기화
initResponseForm();
//클릭하면 모든 답글 작성 버튼을 노출시키고 클릭한 답글 작성 버튼만 숨기기
$(this).hide();
//댓글번호
let re_num = $(this).attr('data-num');
//부모 글번호
let te_parent_num = $(this).attr('data-parent');
//깊이
let te_depth = $(this).attr('data-depth');
console.log(te_parent_num + ',' + te_depth);
//답글 작성 폼 UI
let responseUI = '<form id="resp_form">';
responseUI += '<input type="hidden" name="re_num" id="re_num" value="'+re_num+'">';
responseUI += '<input type="hidden" name="te_parent_num" value="'+te_parent_num+'">';
responseUI += '<input type="hidden" name="te_depth" value="'+te_depth+'">';
responseUI += '<textarea rows="3" cols="50" name="te_content" id="resp_content" class="rep-content"></textarea>';
responseUI += '<div id="resp_first"><span class="letter-count">300/300</span></div>';
responseUI += '<div id="resp_second" class="align-right">';
responseUI += ' <input type="submit" value="답글 작성">';
responseUI += ' <input type="button" value="취소" class="resp-reset">';
responseUI += '</div>';
responseUI += '</form>';
//답글 작성폼을 답글을 작성하고자하는 데이터가 있는 div에 노출
$(this).after(responseUI);
});
//답글에서 취소버튼 클릭시 답글 폼 초기화
$(document).on('click','.resp-reset',function(){
initResponseForm();
});
//답글 작성 폼 초기화
function initResponseForm(){
$('.response-btn,.response2-btn').show();
$('#resp_form').remove();
}
//답글 등록
$(document).on('submit','#resp_form',function(event){
let resp_form = $(this);
if($('#resp_content').val().trim()==''){
alert('내용을 입력하세요!');
$('#resp_content').val('').focus();
return false;
}
//폼에 입력한 데이터 반환
let form_data = $(this).serialize();
//댓글 번호
let re_num = $(this).find('#resp_num').val();
//서버와 통신
$.ajax({
url:'writeResponse',
type:'post',
data:form_data,
dataType:'json',
success:function(param){
if(param.result == 'logout'){
alert('로그인해야 답글을 작성할 수 있습니다.');
}else if(param.result == 'success'){
//답글 목록 호출
//getListResponse(re_num,resp_form.parents('.item'));
initResponseForm();
}else{
alert('답글 작성 오류 발생');
}
},
error:function(){
alert('네트워크 오류 발생');
}
});
//기본 이벤트 제거
event.preventDefault();
});
board.css 내용수정 및 추가
버튼들 추가
.modify-btn,.delete-btn,.resp-modify-btn,.resp-delete-btn,
.response-btn,
.response2-btn,
.paging-button input{
padding:4px, 20px;
border:1px solid #f0f2f1;
border-radius:20px;
color:#6e6e6e;
background-color:#f0f2f1;
font-weight:bold;
cursor:pointer;
}
.modify-btn:hover,.delete-btn:hover,.resp-modify-btn:hover,.resp-delete-btn:hover,
.response-btn:hover,
.response2-btn:hover,
.paging-button input:hover{
background-color:#e1e3e2;
color:#000000;
transition:0.2s ease-out;
height:30px;
}
.rescontent-btn{
width:300px;
margin:10px 10px 0 10px;
padding:4px 20px;
border-radius:20px;
color:#6e6e6e;
background-color:#f0f2f1;
font-weight:bold;
cursor:pointer;
}
.rescontent-btn:hover{
background-color:#e1e3e2;
color:#000000;
transition:0.2s ease-out; /* 부드럽게 움직이고 전환되게 하는 효과 */
height:30px;
}
div.respitem{
margin-top:20px;
padding-left:50px;
}


sqldeveloper에서 값이 들어가있는지 확인한다.
'쌍용교육(JAVA) > SpringBoot' 카테고리의 다른 글
쌍용교육 -JSP수업 101일차 ch15SpringPage(12) (0) | 2024.07.10 |
---|---|
쌍용교육 -JSP수업 100일차 ch15SpringPage(11) (0) | 2024.07.09 |
쌍용교육 -JSP수업 98일차 ch15SpringPage(9) (0) | 2024.07.05 |
쌍용교육 -JSP수업 97일차 ch15SpringPage(8) (0) | 2024.07.04 |
쌍용교육 -JSP수업 96일차 ch15SpringPage(7) (0) | 2024.07.03 |