[스프링] 추천기능 구현 (OKKY사이트 추천기능 참고) :: 간편 웹프로그래밍 (tistory.com)
저번 글에서 게시글 추천 기능을 구현했었다.
이번엔 댓글에 비동기 방식으로 추천기능을 구현해보려고 한다.
댓글에 좋아요/싫어요를 누르면 OKKY사이트 처럼 해당 회원의 포인트에 영향을 미치고,
좋아요와 싫어요를 따로 구분한다.
좋아요/싫어요가 반영되는 memberPoint 라는 컬럼 외에,
네이버 지식in의 내공시스템처럼, 따로 memberDEV라는 칼럼을 하나 더 만들어 줬다.
그냥 좋아요 싫어요는 너무 식상해서
출석/커뮤니티 글작성/댓글 등 , 커뮤니티 카테고리에서 받는 포인트는 memberPoint로 쌓이고,
QnA , Tech , IT NEWS 등의 기술기반 카테고리에서 받는 포인트는 DEV로 쌓이게해서,
memberPoint 를 내공처럼 질문 시에 사용할 수 있도록 만들고,
DEV는 작성자의 계급 (지식in의 태양신, 구름신 등등..)에 영향을 미치도록 하고
따로 랭킹페이지등도 만들어 볼 생각이다 ( 주에 가장 많은 점수를 얻는 사람 등등..)
일단 로직은 전 글에 작성한 글과 같다.
대신 bno ( 게시글 PR) 대신 rno ( 댓글PR) 을 사용한다.
추천기능을 위해
- 추천 중복 방지와, 구분을 위한 테이블 생성 ( MP_REPLY_LIKE)
- 댓글테이블에 LIKEHIT 컬럼 생성 ( MP_REPLY)
- 회원테이블에 MEMBER_POINT 컬럼 생성 ( MP_MEMBER)
이 필요하다.
create table MP_REPLY_LIKE(
LIKENO NUMBER NOT NULL PRIMARY KEY ,
BNO NUMBER,
MEMBER_ID VARCHAR2(50) NOT NULL,
RNO NUMBER,
DEVCHECK NUMBER DEFAULT 0 NOT NULL,
DEVDATE DATE DEFAULT SYSDATE NOT NULL,
FOREIGN KEY (MEMBER_ID) REFERENCES MP_MEMBER(MEMBER_ID) ON DELETE CASCADE,
FOREIGN KEY (BNO) REFERENCES MP_BOARD (BNO) ON DELETE CASCADE
);
alter table mp_REPLY add(LIKEHIT NUMBER default 0);
alter table mp_MEMBER add(MEMBER_POINT NUMBER default 0);
Mapper.xml
<!-- 댓글 추천수 -->
<update id="updateLike" >
update MP_REPLY set
LIKEHIT = LIKEHIT +1
where RNO = #{rno}
</update>
<!-- 댓글 추천수취소 -->
<update id="updateLikeCancel" >
update MP_REPLY set
LIKEHIT = LIKEHIT - 1
where RNO = #{rno}
</update>
<!-- 좋아요 -->
<!-- 댓글 추천 시 Like 테이블에 insert -->
<insert id="insertLike">
insert into MP_REPLY_LIKE(LIKENO , BNO , RNO ,MEMBER_ID)
values((SELECT NVL(MAX(likeno), 0) + 1 FROM MP_REPLY_LIKE) ,#{bno},#{rno} ,#{memberId})
</insert>
<!-- 댓글 추천취소 시 delete -->
<delete id="deleteLike">
delete from MP_REPLY_LIKE where MEMBER_ID = #{memberId} and RNO = #{rno}
</delete>
<!-- 댓글 추천 중복방지 select문 -->
<select id="likeCheck" resultType="int">
select count(*) from MP_REPLY_LIKE where MEMBER_ID = #{memberId} and RNO = #{rno}
</select>
<!-- 댓글 추천 시 회원포인트 증가 -->
<update id="memberPointPlus">
update MP_MEMBER set MEMBER_POINT = MEMBER_POINT + 1 where MEMBER_ID = #{writerId}
</update>
<!-- 댓글 추천취소 시 회원포인트 감소 -->
<update id="memberPointDown">
update MP_MEMBER set MEMBER_POINT = MEMBER_POINT - 1 where MEMBER_ID = #{writerId}
</update>
Controller
@RequestMapping(value = "/commentLike")
public int updateLike(int bno,int rno, String memberId,String writerId,Model model)throws Exception{
if(replyService.hateCheck(rno, memberId) == 1) {
int likeCheck = 2;
return likeCheck;
}else {
int likeCheck = replyService.likeCheck(rno, memberId);
if(likeCheck == 0) {
//좋아요 처음누름
replyService.insertLike(bno, rno , memberId );//like테이블 삽입
replyService.updateLike(rno); //게시판테이블 +1
replyService.memberPointPlus(writerId); //회원포인트 +1
}else if(likeCheck == 1) {
replyService.updateLikeCancel(rno); //게시판테이블 =1
replyService.deleteLike(rno, memberId); //like테이블 삭제
replyService.memberPointDown(writerId);
}
return likeCheck;
}
}
RESTController를 사용했다. (= @ResponseBody)
매개변수로
- bno - 게시글PR
- rno - 댓글PR
- memberId - 추천을 누르는 회원PR
- writerId - 댓글작성자 회원PR
을 가져온다
hateCheck로 반대를 눌렀다면 추천을 누를 수 없게 int 값 2를 리턴한다.
likeCheck로 MP_REPLY_LIKE 테이블에 값이 있는지 Count해서
0 ( 없음) 이 나오면
insert , update + 1 , member update+1
을 하고
1(있음) 이 나오면
delete, update - 1 , member update -1
을 한다.
1이면 테이블의 값을 delete하기 때문에, 0으로 돌아가서 중복을 방지한다.
(댓글 뷰 페이지).jsp
//댓글 목록
function commentList(){
$.ajax({
url : '/reply/readReply',
type : 'get',
data : {'bno':bno },
success : function(data){
var a ='';
if(data<1){
a +='<h2>등록된 댓글이 없습니다</h2> ';}
$.each(data, function(key, value){
a += '<div class="commentArea" >';
a += '<div class="commentInfo'+value.rno+'">'+'댓글번호 : '+value.rno+' / 작성자 : '+value.writer;
a += '<div class="commentDate'+value.rno+'"> <p> 작성일 : '+value.regdate+'</p>' ;
a += '<tr><td class="commentContent'+value.rno+'"> <p> 내용 : '+value.content +' ---- likehit:'+value.likehit+'---hatehit:'+value.hatehit+'---memberPoint:'+value.memberVO.memberPoint+'---DEV:'+value.memberVO.memberDevPoint+' </p> </td><tr> ';
if(writer == value.id){
a += '<button type="button" class="btn btn-success" onclick="commentUpdate('+value.rno+',\''+value.content+'\');"> 수정 </button>';
a += '<button type="button" class="btn btn-dark" onclick="commentDelete('+value.rno+');"> 삭제 </button> ';
이부분 a +='<button type="button" class="btn btn-primary" onclick="commentLike('+value.bno+','+value.rno+' , '+value.id+');"> 추천</button>';
a +='<button type="button" class="btn btn-primary" onclick="commentHate('+value.bno+','+value.rno+' , '+value.id+');"> 반대</button>';
if(bgno == 20){
a +='<button type="button" class="btn btn-primary" onclick="commentDev('+value.bno+','+value.rno+' , '+value.id+');"> Dev</button>';
}
}
a += ' </div></div> </div> <hr/>';
});
$(".commentList").html(a);
}
});
}
//좋아요
function commentLike(bno,rno,id){
$.ajax({
type : "POST",
url : "/reply/commentLike",
dataType : "json",
data : {'bno' : bno ,'rno' : rno, 'writerId' : id , 'memberId' : writer},
error : function(){
alert("통신 에러");
},
success : function(likeCheck) {
if(likeCheck == 0){
alert("추천완료.");
commentList();
}
else if (likeCheck == 1){
alert("추천취소");
commentList();
}else if(likeCheck == 2){
alert("이미 반대하셨습니다");
}
}
});
}
댓글목록을 출력하는 ajax에 추천 버튼을 추가해주고, (중간에 표시)
추천 ajax를 작성해준다.
컨트롤러에 필요한 매개변수를 data로 보내준다.
값이 0,1,2 인지에 따라 alert 창을 호출해주고, 목록을 호출해서 댓글창이 갱신되게 해준다.
간단하게 alert창으로 구현했지만 여러가지 이벤트를 넣어서 꾸밀 수 있겠다.
이제 추천/추천취소를 구현했으니 뷰에 출력해줘야 한다.
댓글 목록은 List로 출력하는데,
하나의 List에 REPLY 테이블의 값과 MEMBER 테이블의 값이 필요하기 때문에, 조인을 이용해준다.
<select id="readReply" resultMap="ReplyVO">
SELECT
a.BNO,a.RNO, a.CONTENT, a.WRITER, a.REGDATE, a.ID , a.LIKEHIT,a.HATEHIT,a.DEVHIT,b.MEMBER_POINT,b.MEMBER_DEVPOINT
FROM MP_REPLY a left outer Join MP_MEMBER b
on a.ID = b.MEMBER_ID
WHERE BNO = #{bno}
order by RNO DESC
</select>
<resultMap type="kr.co.vo.MemberVO" id="MemberVO">
<result column="MEMBER_ID" property="memberId" />
<result column="MEMBER_POINT" property="memberPoint"/>
<result column="MEMBER_DEVPOINT" property="memberDevPoint"/>
</resultMap>
<resultMap type="kr.co.vo.ReplyVO" id="ReplyVO">
<result column="BNO" property="bno"/>
<result column="RNO" property="rno"/>
<result column="WRITER" property="writer"/>
<result column="REGDATE" property="regdate"/>
<result column="CONTENT" property="content"/>
<result column="ID" property="id"/>
<result column="LIKEHIT" property="likehit"/>
<result column="HATEHIT" property="hatehit"/>
<result column="DEVHIT" property="devhit"/>
<collection property="memberVO" resultMap="MemberVO"></collection>
</resultMap>
결과
- 추천버튼을 눌렀을때
- 게시글 추천과 작성자 추천이 1씩 늘었다.
- 추천 후 반대를 눌렀을때
- 다시 추천을 눌렀을때
- 추천이 -1씩 줄고 반대가 눌림
- 반대와 작성자추천이 적용되고 DEV를 두번누름
- 모두 잘 적용된다.
service와 dao는 이전글에서 많이 설명했기 때문에 생략하고,
반대는 위의 로직을 반대로 만들면 되고,
DEV는 추천시스템을 구현 한 후, 특정 카테고리에만 적용되게 뷰에서 처리해줬다.
이제 프로젝트를 제작한지 한달이 좀 넘어가는데,
처음엔 ajax가 많이 어려웠지만 이제 꽤나 친숙해졌다.
join쿼리에 익숙해지면서 좀 더 복잡한 쿼리에도 자신이 생겼고
실력이 많이 늘고 있는 걸 깨닫는 요즘이다.
재미가 붙어서 더 열심히 하게 된다.
'SPRING > IceWater Community' 카테고리의 다른 글
[스프링]게시판 스크랩기능 구현 (3) | 2021.10.11 |
---|---|
[스프링] 질문 채택 기능 구현 (Like 지식 IN) (0) | 2021.10.08 |
[스프링] 게시판 이전글 다음글 읽은 글 표시 (1) | 2021.10.01 |
[스프링]게시판 읽은 글 표시 (3) | 2021.10.01 |
[스프링] 공지사항 구현 (0) | 2021.09.30 |