간펴니
간편 자바프로그래밍
간펴니
전체 방문자
오늘
어제
  • 전체보기 (185)
    • 알고리즘 (2)
    • JAVA (69)
      • 이펙티브 자바 (47)
      • JAVA 병렬프로그래밍 (5)
      • 자바 (17)
    • SPRING (60)
      • Spring (12)
      • IceWater Community (37)
      • Homme Shop (10)
      • 토비의 스프링 (1)
    • SPRING BOOT (4)
      • WhiteRecord (7)
    • 오류 (9)
    • DB (10)
      • ORACLE (5)
      • MYSQL (1)
      • MYBATIS (4)
      • JPA (0)
      • 대용량 데이터 베이스 (0)
      • SQL (0)
    • FRONT (8)
      • JSP (2)
      • JavaScript (2)
      • Jquery (3)
      • Thymeleaf (1)
    • AWS (6)
    • JNI (10)
    • 회고 (0)
    • MQ (0)
    • Radis (0)
    • Git (0)
    • Docker (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

  • 블로그 컨셉 변경

인기 글

태그

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
간펴니

간편 자바프로그래밍

[스프링] 질문 채택 기능 구현 (Like 지식 IN)
SPRING/IceWater Community

[스프링] 질문 채택 기능 구현 (Like 지식 IN)

2021. 10. 8. 10:19
728x90

이번엔 질문 채택기능을 구현했다.

 

우선 , 지금 하고 있는 커뮤니티 프로젝트는

 

일반 카테고리 글작성, 추천 등으로 받을 수 있는 회원 포인트와,

 

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>';
	 }
          }

 

01
슬라이드쇼 2장

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인지에 따라 구분해주면 된다. 간단하다.

 


결과

012345
슬라이드쇼 6장

  1. '1234'회원으로 로그인 , 73의 추천포인트 확인 후 글작성
  2. 추천포인트가 80이하면 50까지 표시되도록 설정해놨음. 50으로 설정 후 글작성
  3. 50포인트가 감소되서 23이 나와야하지만 글작성시 3포인트가 오르기 때문에 26, 정상작동 내공도 기본10+50
  4. 해당 게시글에서 '1234' , '11111' 회원으로 댓글을 작성, 글작성자는 채택버튼이 뜨지 않음
  5. 채택완료. 버튼이 바뀌었고, 채택받은 작성자의 DEV가 60증가됐다.
  6. 목록에서도 추천완료버튼이 나옴

 


나름 복잡한 로직이였다. 

 

아무런 조언도 없이 , 오로지 혼자 설계도를 메모장에 작성해서 만든 로직이다.

 

생각해보니 익숙한 다른 로직들 말고 혼자 만들어 본 것은 이번이 처음인 것 같다.

 

아직 구멍이 많지만 그래도 정상적인 기능을 만들어서 뿌듯하다.

 

물론 중간중간 오류도 여러번 나왔지만 로그를 찍어가며 파라미터 이동 흐름을 보며 하다보니

 

몇시간 안되서 구현한 것 같다. 블로그 글 작성이 제일 힘들다..ㅜㅜ

 

 

 

728x90
저작자표시 (새창열림)

'SPRING > IceWater Community' 카테고리의 다른 글

[스프링] 마이페이지 구현 - 회원 활동로그와 작성한 게시글,댓글,스크랩 목록  (0) 2021.10.13
[스프링]게시판 스크랩기능 구현  (3) 2021.10.11
[스프링] 댓글 좋아요/싫어요 구현 - (Ajax)  (4) 2021.10.06
[스프링] 게시판 이전글 다음글 읽은 글 표시  (1) 2021.10.01
[스프링]게시판 읽은 글 표시  (3) 2021.10.01
    'SPRING/IceWater Community' 카테고리의 다른 글
    • [스프링] 마이페이지 구현 - 회원 활동로그와 작성한 게시글,댓글,스크랩 목록
    • [스프링]게시판 스크랩기능 구현
    • [스프링] 댓글 좋아요/싫어요 구현 - (Ajax)
    • [스프링] 게시판 이전글 다음글 읽은 글 표시
    간펴니
    간펴니
    개발공부 기록하는 곳

    티스토리툴바