이번엔 질문 채택기능을 구현했다.
우선 , 지금 하고 있는 커뮤니티 프로젝트는
일반 카테고리 글작성, 추천 등으로 받을 수 있는 회원 포인트와,
Q&A 답변 , Tech카테고리 게시글 작성 , Q&A와 Tech카테고리에서만 받을 수 있는 DEV라는 포인트가 있다.
하트가 추천 포인트, 번개가 DEV 포인트 이다.
각 회원별로 포인트를 가지며 , 해당 구현은 전 글에 작성하였다.
작성자 옆에 보이는 것이 이번에 구현한 채택기능인데,
Q&A 카테고리의 채택전(dev포인트) , 채택 후 (v체크) , 다른카테고리라 채택기능이 없는 게시글 목록이다.
글 작성자는 Q&A글을 작성할때 , 자신의 추천포인트를 소비해서 지식in처럼 내공을 걸 수 있다.
Q&A카테고리의 댓글엔 글 작성자만 채택버튼이 보이며, 채택버튼을 누르면 해당 댓글작성자에게 내공이 전달되고,
댓글창에 채택완료 아이콘이 보인다.
결과는 포스팅 제일 아랫부분에 게시.
게시글 테이블에 HelpPoint , QuestionCheck , QuestionId 컬럼을 추가한다.
내공 , 채택 여부 , 채택받은 댓글작성자 를 저장 할 컬럼이다.
alter table mp_board add(QUESTIONCHECK NUMBER DEFAULT 0);
alter table mp_board add(HELPPOINT NUMBER DEFAULT 0);
alter table mp_board add(QUESTIONID VARCHAR2(50));
컬럼을 추가했으니 VO , resultMap 등 자신의 상황에 맞게 컬럼을 추가해주고 ,
게시글 목록이나 상세보기의 select문에도 추가해준다.
writeView.jsp
<c:if test="${bgno == 19 or bgno == 20 or bgno == 21 or bgno == 22 or bgno == 23}">
<tr>
<th class="active">내공</th>
<td>
<select name="helppoint" class="chk" id="helppoint" title="Q&A게시판을 선택하세요">
<option value="0" >0</option>
<c:if test="${point >= 10 }"><option value="10" >10</option></c:if>
<c:if test="${point >= 20 }"><option value="20" >20</option></c:if>
<c:if test="${point >= 30 }"><option value="30" >30</option></c:if>
<c:if test="${point >= 40 }"><option value="40" >40</option></c:if>
<c:if test="${point >= 50 }"><option value="50" >50</option></c:if>
<c:if test="${point >= 80 }"><option value="80" >80</option></c:if>
<c:if test="${point >= 100 }"><option value="100" >100</option></c:if>
</select>
글작성 페이지에서 , select 옵션으로 내공을 input 시켜줬다.
bgno는 카테고리를 나눈 번호이다. ( 여러테이블이 아닌 하나의 테이블로 카테고리를 나눔)
위와같은 if test문을 사용해서 , Q&A카테고리에서 글 작성버튼을 눌러야 (bgno가 19~23 사이)
내공 select option이 보이도록 만들었다.
다른 카테고리에선 내공을 걸 수 있게 만들면 안되기 때문에 , 카테고리별로 나눠줘야한다.(설명 생략)
${point}는 회원테이블의 포인트를 select문으로 검색해서 model로 가져온 것 이다.
세션의 회원포인트값을 사용하면, 로그인을 계속 하고 있는 상태에서 다수의 Q&A 내공글을 작성하면
세션값의 회원포인트는 재로그인을 하기 전까지 초기화 되지 않기 때문에 ,
따로 select해서 값을 가져왔다. 세션을 다시 부여하는 방법도 있겠지만 , 간단하게 처리해봤다.
Mapper.xml 의 게시글작성 쿼리
<!-- 게시판 글 작성 -->
<insert id="insert" parameterType="kr.co.vo.BoardVO"
useGeneratedKeys="true" keyProperty="bno">
<selectKey keyProperty="bno" resultType="int" order="BEFORE">
SELECT
MP_BOARD_SEQ.NEXTVAL FROM DUAL
</selectKey>
INSERT INTO MP_BOARD( BNO
, TITLE
, CONTENT
, WRITER
, BGNO
, ID
<if test="helppoint != null">
, HELPPOINT
</if>
)
VALUES( #{bno}
,#{title}
, #{content}
, #{writer}
, #{bgnoinsert}
, #{id}
<if test="helppoint != null">
, #{helppoint} + 10
</if>
)
게시글 작성 쿼리에 helppoint가 null이 아니면 값을 추가하도록 해준다.
기본값을 0으로 잡아놓았는데 , Q&A 글 작성시에 기본내공으로 10점이 부여되도록 +10을 해줬다.
파라미터로 boardVO를 받는데 , 처음 시작에 VO에 컬럼을 추가시켜줬으므로 따로 파라미터를 추가해줄 필요는 없다.
만약 , 글 작성시에 내공을 걸었다면 회원포인트가 내려가야 한다.
<!-- Q&A 내공걸면 회원포인트 다운-->
<update id="questionPointDown">
update MP_MEMBER set MEMBER_POINT = MEMBER_POINT - #{helppoint} where member_ID = #{memberId}
</update>
helppoint 의 파라미터를 받아오고,
글 작성 시에는 로그인이 되어야 할 수 있게 해놓았기 때문에,
세션값에서 회원PR인 ID를 가져왔다.
Map으로 helppoint와 memberId를 넣어서 , 내공을 넣은만큼 감소시키고 , 그만큼 insert 시킨다.
글작성 컨트롤러 중 일부
service.write(boardVO,mpRequest);
if(boardVO.getHelppoint() != 0) {
service.questionPointDown(boardVO.getHelppoint(),boardVO.getId());
}
글작성로직 후 , helppoint의 값이 0이 아닐때 , 회원의 포인트 감소 로직을 실행시킨다.
내공의 기본상태인 0일때에나 값이 아예 안들어가는 다른 카테고리에서는 회원포인트 감소를 할 필요가 없기 때문에,
0으로 구분해준다.
이제 정상적으로 DB에 내공을 넣고, 그만큼의 회원포인트를 감소시켰다.
이제 채택버튼을 눌렀을때의 로직을 작성해보자.
우선, 채택버튼은 댓글에 표시되는데 , 댓글은 ajax로 구현했으므로 채택버튼과 로직 또한 ajax로 구현했다.
readView 중 , 댓글부분 전체
//댓글 목록
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+' <br/> <span style="font-size: 25px">'+value.writer+'</span>';
a += '<span style="font-size: 10px"><i class="fas fa-heart ml-1" style="color:red"></i></span>'+value.memberVO.memberPoint+''
a += '<span style="font-size: 12px"><i class="fa fa-bolt ml-1" style="color:blue" aria-hidden="true"></i></span>'+value.memberVO.memberDevPoint;
a += '<div class="commentDate'+value.rno+' mb-4 "> '+value.regdate+'</div>' ;
a += '<tr><td class="commentContent'+value.rno+'"> '+value.content +'</td><tr> <br/>';
if(writer == value.id){
a += '<button type="button" class="btn btn-success mr-2" onclick="commentUpdate('+value.rno+','+value.content+');"> 수정 </button>';
}
a += '<button type="button" class="btn btn-dark mr-2" onclick="commentDelete('+value.rno+');"> 삭제 </button> ';
if(${login != null}){
a +='<button type="button" class="btn btn-warning mr-2" onclick="commentLike('+value.bno+','+value.rno+' , '+value.id+');">'+value.likehit+' <i class="fas fa-thumbs-up"></i></button>';
a +='<button type="button" class="btn btn-danger mr-2" onclick="commentHate('+value.bno+','+value.rno+' , '+value.id+');">'+value.hatehit+' <i class="fas fa-thumbs-down"></i></button>';
if( bgno == 19 || bgno == 20 || bgno == 21 || bgno == 22 || bgno == 23 || bgno == 24|| bgno == 25|| bgno == 26|| bgno == 27|| bgno == 28 || bgno == 32){
a +='<button type="button" class="btn btn-primary mr-2" onclick="commentDev('+value.bno+','+value.rno+' , '+value.id+');">'+value.devhit+' <i class="fab fa-dev"></i></button>';
if(${read.questioncheck == 0 && read.id == login.memberId}) {
a +='<button type="button" class="btn btn-success float-right" onclick="questionCheck('+value.bno+','+helppoint+','+value.id+','+value.rno+')"> <i class="far fa-check-circle fa-4x"></i> </button> ';
}
}
}
if(${read.questioncheck == 1}){
if(value.rno == questionid ){
a +='<i class="fas fa-check-circle float-right fa-4x" style ="color:red"></i>';
}
}
a += ' </div> </div> <br/><hr/>';
});
$(".commentList").html(a);
}
});
}
//채택
function questionCheck(bno,helppoint,id,rno){
$.ajax({
type : "POST",
url : "/board/questionCheck",
dataType : "json",
data : {'bno' : bno ,'helppoint' : helppoint, 'Id' : id,'rno':rno},
error : function(){
alert("통신 에러");
},
success : function(questionCheck) {
if(questionCheck == 1){
alert("채택완료");
location.reload();
}
else if (likeCheck == 2){
alert("채택문제발생");
commentList();
}
}
});
}
요약
if(${read.questioncheck == 0 && read.id == login.memberId}) {
if(value.id != writer){
a +='<button type="button" class="btn btn-success float-right" onclick="questionCheck('+value.bno+','+helppoint+','+value.id+','+value.rno+')"> <i class="far fa-check-circle fa-4x"></i> </button> ';
}
}
}
}
if(${read.questioncheck == 1}){
if(value.rno == questionid ){
a +='<i class="fas fa-check-circle float-right fa-4x" style ="color:red"></i>';
}
}
questioncheck는 기본값이 0이고 , 채택버튼 클릭시 1로 update되는 컬럼이다.
0이고 , 게시글 작성자의 ID와 현재 로그인한 회원의 ID가 맞을 경우 채택버튼이 보인다.
단 , 댓글 작성자ID와 로그인한 회원의 ID가 같을 경우 채택버튼이 보이지 않는다 ( 본인댓글일 경우)
++ writer = ${login.memberId}
채택버튼을 눌러서, 채택로직을 완료하면
1이고, 채택된 댓글 번호를 저장하는 컬럼인 questionid 와 댓글번호의 값이 맞다면 채택완료 버튼을 출력한다.
ajax는 채택버튼을 눌렀을때 , 채택로직에 필요한 파라미터인
bno ( 게시글 PR)
helppoint (게시글에 저장 된 내공 )
Id ( 댓글 작성자의 PR)
rno ( 댓글 PR)
을 컨트롤러로 보낸다.
//채택
@ResponseBody
@RequestMapping(value = "/board/questionCheck",method=RequestMethod.POST)
public int questionCheck(int bno,int helppoint,String Id,int rno) throws Exception{
int questionCheck ;
try {
service.questionPointUp(helppoint, Id);
service.questionCheck(bno,rno);
questionCheck = 1;
}catch(Exception e) {
questionCheck = 2;
return questionCheck;
}
return questionCheck;
}
컨트롤러는 간단하다.
ajax 비동기 통신을 위한 @ResponseBody 어노테이션을 붙여주고,
ajax로 보낸 파라미터를 @RequestParam으로 받는다 ( 이름이 같을 경우 생략가능)
내공만큼 채택된 댓글 작성자의 DEV를 올려주는 로직과
채택의 여부를 알려주는 questionCheck 로직에 파라미터를 준다.
그 후 1인지 2인지를 리턴해서 성공,실패 여부를 본다.
<!-- 채택된 회원에게 포인트 전달 -->
<update id="questionPointUp">
update MP_MEMBER set MEMBER_DEVPOINT = MEMBER_DEVPOINT + #{helppoint} where MEMBER_ID = #{memberId}
</update>
<!-- questionCheck를 1로 바꿔서 채택완료를 뜨게함 -->
<update id="questionCheck">
update MP_BOARD set QUESTIONCHECK = 1 , QUESTIONID = #{rno} where bno =#{bno}
</update>
간단하다. 댓글작성자의 ID값을 이용해서 DEVPOINT 컬럼에 내공만큼 + 한다.
Check의 값도 기본인 0에서 1로 바꿔주고 , 댓글번호(rno)를 QuestionID 컬럼에 넣어준다.
만약 , 댓글번호가 아닌 댓글 작성자를 기준으로 채택댓글을 구분하면 ,
댓글채택을 받은 작성자가 여러 개의 댓글을 달았을 경우 모두 채택완료 버튼이 보이게 된다.
이제 목록에 Q&A 게시글이라면 내공과 채택여부를 표시해주려고 한다.
list.jsp
<c:if test="${list.questioncheck == 1}"><i class="fas fa-check-circle float-right" style ="color:red"></i>
</c:if>
<c:if test="${list.questioncheck == 0}">
<c:if test="${list.bgno == 19 or list.bgno == 20 or list.bgno == 21 or list.bgno == 22 or list.bgno == 23}">
<i class="far fa-check-circle float-right" style ="color:dark"> ${list.helppoint} </i>
</c:if>
</c:if>
questioncheck의 값이 1인지 0인지에 따라 구분해주면 된다. 간단하다.
결과
- '1234'회원으로 로그인 , 73의 추천포인트 확인 후 글작성
- 추천포인트가 80이하면 50까지 표시되도록 설정해놨음. 50으로 설정 후 글작성
- 50포인트가 감소되서 23이 나와야하지만 글작성시 3포인트가 오르기 때문에 26, 정상작동 내공도 기본10+50
- 해당 게시글에서 '1234' , '11111' 회원으로 댓글을 작성, 글작성자는 채택버튼이 뜨지 않음
- 채택완료. 버튼이 바뀌었고, 채택받은 작성자의 DEV가 60증가됐다.
- 목록에서도 추천완료버튼이 나옴
나름 복잡한 로직이였다.
아무런 조언도 없이 , 오로지 혼자 설계도를 메모장에 작성해서 만든 로직이다.
생각해보니 익숙한 다른 로직들 말고 혼자 만들어 본 것은 이번이 처음인 것 같다.
아직 구멍이 많지만 그래도 정상적인 기능을 만들어서 뿌듯하다.
물론 중간중간 오류도 여러번 나왔지만 로그를 찍어가며 파라미터 이동 흐름을 보며 하다보니
몇시간 안되서 구현한 것 같다. 블로그 글 작성이 제일 힘들다..ㅜㅜ
'SPRING > IceWater Community' 카테고리의 다른 글
[스프링] 마이페이지 구현 - 회원 활동로그와 작성한 게시글,댓글,스크랩 목록 (0) | 2021.10.13 |
---|---|
[스프링]게시판 스크랩기능 구현 (3) | 2021.10.11 |
[스프링] 댓글 좋아요/싫어요 구현 - (Ajax) (3) | 2021.10.06 |
[스프링] 게시판 이전글 다음글 읽은 글 표시 (1) | 2021.10.01 |
[스프링]게시판 읽은 글 표시 (3) | 2021.10.01 |