댓글 테이블,시퀀스 만들기
Quantum DB-댓글을 담을 테이블과 시퀀스 생성
-- 댓글 정보를 저장할 테이블
CREATE TABLE board_cafe_comment(
num NUMBER PRIMARY KEY, -- 댓글의 글번호
writer VARCHAR2(100), -- 댓글 작성자
content VARCHAR2(500), -- 댓글 내용
target_id VARCHAR2(100), -- 댓글의대상이되는아이디(글작성자)
ref_group NUMBER, -- 댓글 그룹번호
comment_group NUMBER, -- 원글에 달린 댓글 내에서의 그룹번호
deleted CHAR(3) DEFAULT 'no', -- 댓글이 삭제 되었는지 여부
regdate DATE -- 댓글 등록일
);
CREATE board_cafe_seq;
댓글 저장하기
detail.jsp -원글의 댓글 작성기능폼 및 css작성
div 컨테이너안에 댓글을 작성할 수 있는폼을작성한다.
ref_group, target_id, content가 파라미터로 넘어가게된다.
ref_group(댓글 그룹번호)는 원글의 글번호,
target_id(댓글 대상자)는 원글의 작성자,
content는 댓글내용을 담는다. commet_group번호는 따로 전송하지 않는다. = null
만약 로그인이 되어있지 않다면 '로그인이 필요합니다'라고 출력되도록 한다.
<div class="comments">
<!-- 원글에 댓글을 작성할 수 있는 폼 -->
<div class="comment_form">
<form action="comment_insert.do" method="post">
<!-- 댓글의 그룹번호는 원글의 글번호가 된다. -->
<input type="hidden" name="ref_group" value="${dto.num }"/>
<!-- 댓글의 대상자는 원글의 작성자가 된다. -->
<input type="hidden" name="target_id" value="${dto.writer }"/>
<textarea rows="content"><c:if test="${empty id }">로그인이 필요합니다.</c:if></textarea>
<button type="submit">등록</button>
</form>
</div>
</div>
<style>
/* 글 내용의 경계선 표시 */
.content{
border: 1px dotted #cecece;
}
/* 글 내용안에 있는 이미지의 크기 제한 */
.content img{
max-width: 100%;
}
/* 댓글에 관련된 css */
.comments ul{
padding: 0;
margin: 0;
list-style-type: none;
}
.comments ul li{
border-top: 1px solid #888; /* li 의 윗쪽 경계선 */
}
.comments dt{
margin-top: 5px;
}
.comments dd{
margin-left: 26px;
}
.comments form textarea, .comments form button{
float: left;
}
.comments li{
clear: left;
}
.comments form textarea{
width: 85%;
height: 100px;
}
.comments form button{
width: 15%;
height: 100px;
}
/* 댓글에 댓글을 다는 폼과 수정폼을 일단 숨긴다. */
.comment form{
display: none;
}
.comment{
position: relative;
}
.comment .reply_icon{
width: 8px;
height: 8px;
position: absolute;
top: 10px;
left: 30px;
}
.comments .user-img{
width: 20px;
height: 20px;
border-radius: 50%;
}
</style>
detail.jsp -댓글(a)의 댓글 작성기능폼
댓글의 댓글창에 만약 로그인이 안되어있다면 '로그인이 필요합니다'가 출력하도록 한다.
ref_group에는 원글의 번호가
target_id에는 댓글(a)의 작성자가
comment_group에는 댓글(a)의 comment_group이 들어가게 한다.
<form class="comment-insert-form" action="comment_insert.do" method="post">
<!-- 덧글 그룹 -->
<input type="hidden" name="ref_group" value="${dto.num }" />
<!-- 덧글 대상 -->
<input type="hidden" name="target_id" value="${tmp.writer }" />
<input type="hidden" name="comment_group" value="${tmp.comment_group }" />
<textarea name="content"><c:if test="${empty id }">로그인이 필요합니다.</c:if></textarea>
<button type="submit">등록</button>
</form>
Controller-댓글 저장요청처리하기
service에서 클라이언트의 요청을 인자로담은 saveComment를 실행하기로 한다. =>아래의 servie-댓글저장하기 참고
detail.jsp의 comment_form에서 전송된 파라미터 ref_group(글번호이자 댓글의 그룹번호)를 detail에 get방식 파라미터로 넣어서 리다이렉트 시킨다.
//댓글 저장 요청 처리
@RequestMapping(value="/cafe/comment_insert",
method=RequestMethod.POST)
public ModelAndView authCommentInsert(HttpServletRequest request,
@RequestParam int ref_group)
{
service.saveComment(request);
return new ModelAndView("redirect:/cafe/detail.do?num="+ref_group);
}
Service-댓글 저장하기
요청으로 부터 ref_group(원글번호), target_id(원글작성자), content(댓글내용)가 파라미터를 얻어낸다.
폼에서 전송한 것이 댓글인 경우와 대댓글인 경우가 있다.
작성된 것이 댓글인 경우 : commnet_group(그룹번호)은 null값이다.
댓글의 num(글번호)를 댓글의 commnet_group(그룹번호)로 집어넣는다.
대댓글인경우 : 원댓글의 comment_group(그룹번호)가 댓글의 comment_group(그룹번호)가 된다.
//댓글 저장하는 메소드
@Override
public void saveComment(HttpServletRequest request) {
//댓글 작성자
String writer=(String)request.getSession()
.getAttribute("id");
//댓글의 그룹번호
int ref_group=
Integer.parseInt(request.getParameter("ref_group"));
//댓글의 대상자 아이디
String target_id=request.getParameter("target_id");
//댓글의 내용
String content=request.getParameter("content");
//댓글 내에서의 그룹번호 (null 이면 원글의 댓글이다)
String comment_group=
request.getParameter("comment_group");
//저장할 댓글의 primary key 값이 필요하다
int seq = cafeCommentDao.getSequence();
//댓글 정보를 Dto 에 담기
CafeCommentDto dto=new CafeCommentDto();
dto.setNum(seq);
dto.setWriter(writer);
dto.setTarget_id(target_id);
dto.setContent(content);
dto.setRef_group(ref_group);
if(comment_group==null) {//원글의 댓글인 경우
//댓글의 글번호가 댓글의 그룹 번호가 된다.
dto.setComment_group(seq);
}else {//댓글의 댓글인 경우
//comment_group 번호가 댓글의 그룹번호가 된다.
dto.setComment_group
(Integer.parseInt(comment_group));
}
//댓글 정보를 DB 에 저장한다.
cafeCommentDao.insert(dto);
}
참고
1.원글의 글번호가 댓글의 ref_group번호가 된다.
2.원글의 댓글은 댓글의 번호와 comment_group번호가 같다.
3.댓글의 댓글은 댓글의 번호와 commnet_group번호가 다르고 comment_group번호는 최초로 시작된
댓글의 글번호로 부여된다.
댓글 읽어오기
Service -글의 detail을 얻어올 때 댓글전체도 얻어오기
detail.jsp의 요청에 응답할 service메소드 getDetail에 댓글 목록도 얻어와서 request에 담아준다.
@Override
public void getDetail(HttpServletRequest request) {
//파라미터로 전달되는 글번호
int num=Integer.parseInt(request.getParameter("num"));
//검색과 관련된 파라미터를 읽어와 본다.
String keyword=request.getParameter("keyword");
String condition=request.getParameter("condition");
//CafeDto 객체 생성 (select 할때 필요한 정보를 담기 위해)
CafeDto dto=new CafeDto();
if(keyword != null) {//검색 키워드가 전달된 경우
if(condition.equals("titlecontent")) {//제목+내용 검색
dto.setTitle(keyword);
dto.setContent(keyword);
}else if(condition.equals("title")) {//제목 검색
dto.setTitle(keyword);
}else if(condition.equals("writer")) {//작성자 검색
dto.setWriter(keyword);
}
//request 에 검색 조건과 키워드 담기
request.setAttribute("condition", condition);
/*
* 검색 키워드에는 한글이 포함될 가능성이 있기 때문에
* 링크에 그대로 출력가능하도록 하기 위해 미리 인코딩을 해서
* request 에 담아준다.
*/
String encodedKeyword=null;
try {
encodedKeyword=URLEncoder.encode(keyword, "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
//인코딩된 키워드와 인코딩 안된 키워드를 모두 담는다.
request.setAttribute("encodedKeyword", encodedKeyword);
request.setAttribute("keyword", keyword);
}
//CafeDto 에 글번호도 담기
dto.setNum(num);
//조회수 1 증가 시키기
cafeDao.addViewCount(num);
//글정보를 얻어와서
CafeDto dto2=cafeDao.getData(dto);
//request 에 글정보를 담고
request.setAttribute("dto", dto2);
//댓글 목록을 얻어와서 request 에 담아준다.
List<CafeCommentDto> commentList=cafeCommentDao.getList(num);
request.setAttribute("commentList", commentList);
}
Mapper -댓글전체 얻어오기
users테이블의 id와 cafecomment테이블의 writer가 겹치므로 이를 활용하여 users테이블의 profile을 얻어낸다.
comment_group번호에 대해서 먼저 오름차순한 이후에 num에 대해서 오름차순 해서 출력시 순서를 정리해준다.
<select id="getList" resultType="cafeCommentDto" parameterType="int">
SELECT num,writer,content,target_id,ref_group,
comment_group,deleted, board_cafe_comment.regdate,profile
FROM board_cafe_comment
INNER JOIN users
ON board_cafe_comment.writer=users.id
WHERE ref_group=#{ref_group}
ORDER BY comment_group ASC, num ASC
</select>
detail.jsp- 댓글들(commnetList)를 반복문을 돌면서 읽어온다.
댓글의 deleted가 'yes'일 때는 '삭제된 댓글입니다'라고 출력되게 조건문을 사용한다.
참고 : dl dt dd : 설명 리스트 목록
dl로 시작 , dto로 타이틀목록을 만들고 dd로 그에대한 설명을 해줌
<dl>
<dt></dt>
<dd></dd>
</dl>
<ul>
<c:forEach items="${commentList }" var="tmp">
<c:choose>
<c:when test="${tmp.deleted ne 'yes' }">
<li class="comment" id="comment${tmp.num }" <c:if test="${tmp.num ne tmp.comment_group }">style="padding-left:50px;"</c:if> >
<c:if test="${tmp.num ne tmp.comment_group }">
<img class="reply_icon" src="${pageContext.request.contextPath}/resources/images/re.gif"/>
</c:if>
<dl>
<dt>
<c:choose>
<c:when test="${empty tmp.profile }">
<img class="user-img" src="${pageContext.request.contextPath}/resources/images/default_user.jpg"/>
</c:when>
<c:otherwise>
<img class="user-img" src="${pageContext.request.contextPath}${tmp.profile}"/>
</c:otherwise>
</c:choose>
<span>${tmp.writer }</span>
<c:if test="${tmp.num ne tmp.comment_group }">
to <strong>${tmp.target_id }</strong>
</c:if>
<span>${tmp.regdate }</span>
<a href="javascript:" class="reply_link">답글</a> |
<c:choose>
<%-- 로그인된 아이디와 댓글의 작성자가 같으면 --%>
<c:when test="${id eq tmp.writer }">
<a href="javascript:" class="comment-update-link">수정</a>
<a href="javascript:deleteComment(${tmp.num })">삭제</a>
</c:when>
<c:otherwise>
<a href="javascript:">신고</a>
</c:otherwise>
</c:choose>
</dt>
<dd>
<pre>${tmp.content }</pre>
</dd>
</dl>
<form class="comment-insert-form" action="comment_insert.do" method="post">
<!-- 덧글 그룹 -->
<input type="hidden" name="ref_group" value="${dto.num }" />
<!-- 덧글 대상 -->
<input type="hidden" name="target_id" value="${tmp.writer }" />
<input type="hidden" name="comment_group" value="${tmp.comment_group }" />
<textarea name="content"><c:if test="${empty id }">로그인이 필요합니다.</c:if></textarea>
<button type="submit">등록</button>
</form>
<!-- 로그인한 아이디와 댓글의 작성자와 같으면 수정폼 출력 -->
<c:if test="${id eq tmp.writer }">
<form class="comment-update-form" action="comment_update.do">
<input type="hidden" name="num" value="${tmp.num }" />
<textarea name="content">${tmp.content }</textarea>
<button type="submit">수정</button>
</form>
</c:if>
</li>
</c:when>
<c:otherwise>
<li <c:if test="${tmp.num ne tmp.comment_group }">style="padding-left:50px;"</c:if> >삭제된 댓글 입니다.</li>
</c:otherwise>
</c:choose>
</c:forEach>
</ul>
댓글 삭제하기
Mapper-댓글 삭제하기
삭제시 댓글이 테이블에서 사라지는 것이아니라 '댓글이 삭제되었습니다'라고 출력할 것이기때문에
delete가아닌 update문을 수행한다. 삭제를 원하는 테이블의 deleted가 yes가 되도록 바꾼다.
<delete id="delete" parameterType="int">
UPDATE board_cafe_comment
SET deleted='yes'
WHERE num=#{num}
</delete>
datail.jsp-댓글 삭제를 눌렀을 때 호출되는 함수
//댓글 삭제를 눌렀을때 호출되는 함수
function deleteComment(num){
var isDelete=confirm("확인을 누르면 댓글이 삭제 됩니다.");
if(isDelete){
//페이지 전환 없이 ajax요청을 통해서 삭제하기
$.ajax({
url:"comment_delete.do",// <-상대경로로 절대경로로->"/cafe/comment_delete.do"요청
method:"post",
data:{"num":num}, // num이라는 파라미터명으로 삭제할 댓글의 번호 전송
success:function(responseData){
if(responseData.isSuccess){
var sel="#comment"+num;
$(sel).text("삭제된 댓글 입니다.");
}
}
});
}
}
cafeController - 댓글삭제 요청처리
@responsebody + return type : map 이면 ajax형태로 처리된다.
ajax실행 시 "isSuccess"로 true를 리턴하게 한다.
//댓글 삭제 요청 처리\
@ResponseBody
@RequestMapping(value= "/cafe/comment_delete",
method=RequestMethod.POST)
public Map<String, Object>
authCommentDelete(HttpServletRequest request,
@RequestParam int num){
service.deleteComment(num);
Map<String,Object> map=new HashMap<>();
map.put("isSuccess",true);
return map; //{"isSuccess":true}형식의 JSON문자열이 응답된다.
}
}
LoginAspect
로그인하지않았다면 ajax를 아예 실행하지 않고 "isSuccess"로 false를 리턴하게한다.
@Around("execution(java.util.Map auth*(..))")
public Object loginCheckAjax(ProceedingJoinPoint joinPoint) throws Throwable {
//aop 가 적용된 메소드에 전달된 값을 Object[] 로 얻어오기
Object[] args=joinPoint.getArgs();
//로그인 여부
boolean isLogin=false;
HttpServletRequest request=null;
for(Object tmp:args) {
//인자로 전달된 값중에 HttpServletRequest type 을 찾아서
if(tmp instanceof HttpServletRequest) {
//원래 type 으로 casting
request=(HttpServletRequest)tmp;
//HttpSession 객체 얻어내기
HttpSession session=request.getSession();
//세션에 "id" 라는 키값으로 저장된게 있는지 확인(로그인 여부)
if(session.getAttribute("id") != null) {
isLogin=true;
}
}
}
//로그인 했는지 여부
if(isLogin){
// aop 가 적용된 메소드를 실행하고
Object obj=joinPoint.proceed();
// 리턴되는 값을 리턴해 주기
return obj;
}
//로그인을 하지 않았으면
Map<String, Object> map=new HashMap<>();
map.put("isSuccess", false);
return map;
}
Mapper commnet-시퀀스 미리 생성하기
저장시점이 아닌 미리 글번호를 얻어와서 테이블에 삽입한다.
댓글의 글번호가 대댓글의 그룹번호로 쓰일 것이기 때문에 대댓글을 쓸 때는
<select id="getSequence" resultType="int">
SELECT board_cafe_comment_seq.NEXTVAL
FROM DUAL
</select>
댓글 수정하기
detail.jsp-수정 링크 클릭 시 수정폼이 보여지게하는 이벤트 등록
//댓글 수정 링크를 눌렀을때 호출되는 함수 등록
$(".comment-update-link").click(function(){
$(this)
.parent().parent().parent()
.find(".comment-update-form")
.slideToggle(200);
});
detail.jsp-수정폼 생성
<!-- 로그인한 아이디와 댓글의 작성자와 같으면 수정폼 출력 -->
<c:if test="${id eq tmp.writer }">
<form class="comment-update-form" action="comment_update.do" method="post">
<input type="hidden" name="num" value="${tmp.num }" />
<textarea name="content">${tmp.content }</textarea>
<button type="submit">수정</button>
</form>
</c:if>
datail.jsp-수정폼 제출 시 일어나는 이벤트 등록
폼을 제출은 하지 않을 것이기 때문에 return false;해준다.
//댓글 수정 폼에 submit 이벤트가 일어났을때 호출되는 함수 등록
$(".comment-update-form").on("submit", function(){
// "private/comment_update.do"
var url=$(this).attr("action");
//폼에 작성된 내용을 query 문자열로 읽어온다.
// num=댓글번호&content=댓글내용
var data=$(this).serialize();
//이벤트가 일어난 폼을 선택해서 변수에 담아 놓는다.
var $this=$(this);
$.ajax({
url:url,
method:"post",
data:data,
success:function(responseData){
// responseData : {isSuccess:true}
if(responseData.isSuccess){
//폼을 안보이게 한다
$this.slideUp(200);
//폼에 입력한 내용 읽어오기
var content=$this.find("textarea").val();
//pre 요소에 수정 반영하기
$this.parent().find("pre").text(content);
}
}
});
//폼 제출 막기
return false;
});
Controller-댓글 수정요청 처리
ajax로 처리하기 위해 @Responsebody , return map 해준다.
//댓글 수정 요청 처리(ajax)
@ResponseBody
@RequestMapping("/cafe/comment_update")
public Map<String,Object>
authCommentUpdate(HttpServletRequest request,
@ModelAttribute CafeCommentDto dto){
service.updateComment(dto);
Map<String,Object> map=new HashMap<>();
map.put("isSuccess", true );
return map;
}
Mapper -댓글수정하기
<update id="update" parameterType="cafeCommnetDto">
UPDATE board_cafe_comment
SET content =#{content}
WHERE num=#{num}
</update>
datail.jsp-댓글 textarea에 click이벤트가 일어났을 때 로그인 여부에 따라 반응하기
//폼에 focus 이벤트가 일어 났을때 실행할 함수 등록
$(".comments form textarea").on("click", function(){
//로그인 여부
var isLogin=${not empty id};
if(isLogin==false){
var isMove=confirm("로그인 페이지로 이동하시겠습니까?");
if(isMove){
location.href="${pageContext.request.contextPath}/users/loginform.do?url=${pageContext.request.contextPath}/cafe/detail.do?num=${dto.num}";
}
}
});
'프로그래밍 기초 > SPRING' 카테고리의 다른 글
[Spring]custom exception만들고 사용하기 /404,500에러 처리페이지만들기 (0) | 2020.02.11 |
---|---|
[Spring]트랜잭션과 예외 처리 (0) | 2020.02.10 |
[Spring]게시물 이전글,다음글 보기 기능 구현 (0) | 2020.02.07 |
[spring]Anotation모음 (0) | 2020.02.04 |
[Spring]Mapper에서 별칭(Aliase)지정해주기 (0) | 2020.02.03 |