이번글은
1. 동기식 댓글작성을 사용할때 댓글수 구현
2. 비동기식 댓글작성을 사용할때 댓글수 구현
3. 그리고 전체댓글개수를 검색해서(select count) 댓글수를 변경하는 방법과
댓글 작성시 +1 , 삭제시 -1 로 댓글수를 변경하는 방법
을 설명합니다.
서비스중인 게시판들을 보면, 게시판 제목 옆에 댓글이 몇개 달렸는지 나와있는 부분이 있다.
그 부분을 구현해보려고 한다.
바로 이 전 글인 조회수 증가와 비슷한 부분이 있다
--------
동기식 ( 서브쿼리 방식)
ALTER TABLE MP_BOARD ADD(REPLYHIT NUMBER DEFAULT 0);
COMMIT;
일단 게시글 테이블에 댓글수 컬럼을 추가해준다.
public class BoardVO {
private int bno;
private String title;
private String content;
private String writer;
private Date regdate;
private int hit;
private int replyhit;
그리고 VO에 방금 추가한 replyhit 컬럼값을 받을 수 있는 getter&setter를 생성해준다.
<select id="listPage" resultType="kr.co.vo.BoardVO" parameterType="kr.co.vo.SearchCriteria">
SELECT BNO,
TITLE,
CONTENT,
WRITER,
REGDATE,
HIT,
REPLYHIT
FROM (
SELECT BNO,
TITLE,
CONTENT,
WRITER,
REGDATE,
HIT ,
REPLYHIT,
ROW_NUMBER() OVER(ORDER BY BNO DESC) AS RNUM
FROM MP_BOARD
WHERE 1=1
<include refid="search"></include>
) MP
WHERE RNUM BETWEEN #{rowStart} AND #{rowEnd}
ORDER BY BNO DESC
</select>
Mapper.xml의 게시글 목록검색 쿼리에 댓글개수 컬럼(REPLYHIT)를 넣어준다.
<!-- 댓글 수 -->
<update id="replyCount" parameterType="int">
UPDATE MP_BOARD SET REPLYHIT = (select count(*) from MP_REPLY where BNO = #{bno} ) WHERE BNO=#{bno}
</update>
그 후에, update쿼리를 추가한다.
댓글테이블에서 게시글PR로 개수(count)를 검색한 후,
댓글개수를 게시글테이블의 PR(bno)이 맞는 게시글에 REPLYHIT(댓글개수) 컬럼에 업데이트 시켜준다.
DAO
public void replyCount(int bno)throws Exception;
DAOImpl
@Override
public void replyCount(int bno)throws Exception{
sqlsession.update("replyMapper.replyCount", bno);
}
DAO와 DAOImpl에 댓글수를 갱신시키는 쿼리문(replyCount)을 넣어주고 replyCount가 bno값 사용할 수 있게 매개변수로 넣는다.
ServiceImpl
@Transactional(isolation = Isolation.READ_COMMITTED)
@Override
public BoardVO read(int bno)throws Exception{
dao.boardHit(bno);
dao.replyCount(bno);
return dao.read(bno);
}
그 후, ServiceImpl의
read ( 게시글 목록에서 게시글을 눌러 내용조회, 내용보기 , 상세페이지를 보는 것을 구현한 메서드) 에
replyCount를 넣어준다. bno값을 파라미터로 받아서 매개변수로 주고 , 쿼리문을 돌려서 댓글개수를 가져온다.
<div class="table-responsive">
<form role="form" method="get">
<table class="table table-bordered" width="100%" cellspacing="0">
<thead>
<tr>
<th>번호</th>
<th>제목</th>
<th>작성자</th>
<th>등록일</th>
<th>조회수</th>
</tr>
</thead>
<c:forEach items="${list}" var="list">
<tr>
<td><c:out value="${list.bno}" /></td>
<td><a href="/board/readView?bno=${list.bno}&
page=${scri.page}&
perPageNum=${scri.perPageNum}&
searchType=${scri.searchType}&
keyword=${scri.keyword}">
<!-- 이부분
<c:out value="${list.title}" /></a> <strong> (${list.replyhit}) </strong> </td>
-->
<td><c:out value="${list.writer}" /></td>
<td><fmt:formatDate value="${list.regdate}"
pattern="yyyy-MM-dd hh:mm" /></td>
<td><c:out value="${list.hit} " /></td>
</tr>
</c:forEach>
</tbody>
</table>
게시글 목록구현 뷰의 제목인 {list.title} 옆이나 원하는 위치에 {list.replyhit}를 넣어준다.
처음에 VO에 replyhit값을 넣었고, 컨트롤러에서 list 라는 key로 으로 service의 list값(value) 을 가져올 수 있는
model 함수
model.addAttribute("list", service.list(scri)); 로
VO값을 뷰에서 받을 수 있다.
하지만 문제가 생겼다.
댓글 작성을 ajax로 처리했는데, 비동기 방식으로 댓글을 작성해서 새로고침이 되지 않아
수동으로 새로고침을 하지 않으면 목록화면의 댓글수 조회에 데이터가 넘어가지 않는것이다.
그 이유는 아까 service부분에서 read (게시글 내용보기)에 댓글수조회쿼리를 넣었기 때문에,
게시글내용페이지를 봐야 동시에 댓글수 데이터가 넘어가기 때문이다.
그래서 목록의 댓글수를 갱신하려면 댓글을쓰고 f5를 눌러 새로고침을 해주지 않으면
목록에선 다시 게시글을 읽기 전엔 댓글수가 늘지 않는다;;
새로고침만 하면 되겠지하고 목록으로 가는 버튼에 reload() 함수를 넣어서 목록으로 가면
자동으로 새로고침이 되게 설정해봤지만 ( 물론 성능에는 아주 안좋다 ㅎ..)
이 방법은 목록으로 갈때마다 새로고침이 되기 때문에 조회수가 증가해
한번 게시글을 들어가서 읽고 댓글을 쓰고 나오면 조회수가 2씩 증가하고,
목록 버튼을 눌러서 나오는게 아닌 브라우저를 꺼버린다거나 주소창에 직접 주소입력으로 목록화면으로 가버리면
영영 댓글수가 늘지 않는다......()...
이 위의 방법은 ajax가 아닌 동기방식의 댓글작성시엔 자동으로 새로고침이 되므로 상관없다.
그래서 다른 방법을 찾았다. 그 차이점을 구현 후 설명하겠다.
비동기식 ( -1 , +1 방식)
일단 위에서 설명한 댓글수 컬럼추가,목록 쿼리에 댓글수 컬럼추가, vo추가까지는 동일하다
<!-- 댓글 수 -->
<update id="replyCount" parameterType="int">
UPDATE MP_BOARD SET REPLYHIT = REPLYHIT + #{amount} WHERE BNO=#{bno}
</update>
댓글수를 산출하는 update쿼리가
기존 게시글PR을 기준으로 댓글테이블의 댓글수(count)를 산출 ->
댓글수 + #{amount} 로 바뀌었다. amount는 댓글작성시 +1 , 삭제시 -1을 해주는 값이다.
BoardDAO
public void replyCount(int bno,int amount)throws Exception;
BoardDAOImpl
@Override
public void replyCount(int bno,int amount)throws Exception{
Map<String,Object> paramMap = new HashMap<>();
paramMap.put("bno", bno);
paramMap.put("amount", amount);
sqlsession.update("(NAMESPACE설정값).replyCount", paramMap);
}
BoardDAO의 댓글수 업데이트 부분인 replyCount에 필요한 bno와 amount파라미터값을 설정해준다.
Map인터페이스를 구현한 HashMap의 key,value형태로 모델에 데이터를 전송하려고 한다.
.put으로 "bno"라는 이름의 key에 매개변수로 선언한 bno파라미터의 값을 넣는다.
amount도 마찬가지인데, amount의 값은 뒤의 서비스부분에서 설명하겠다.
put으로 맵에 데이터를 넣고, 그 값이 담긴 paramMap을 쿼리의 매개변수로 넣어준다.
Serivce
public void writeReply(ReplyVO vo) throws Exception
ServiceImpl
@Transactional
@Override
public void writeReply(ReplyVO vo) throws Exception {
dao.writeReply(vo);
daoo.replyCount(vo.getBno(), 1);
}
위는 댓글작성(insert) 서비스 부분이다.
서비스 부분에선 따로 메서드를 작성하지 않고 , dao에서 구현한 댓글수 증감쿼리문을 작성서비스에 넣어줄 것이다.
이렇게하면 댓글이 작성될때, 댓글작성쿼리인 writeReply 와 댓글수증감 쿼리인 replyCount가 동시에 실행되어서,
ajax사용중에서도 문제없이 댓글수가 변영이 된다. 전체적인 로직의 차이는 가장 뒤에 설명하겠다.
*******************************
여기서 게시글 DAO와 댓글DAO를 따로 설정해놓아서 dao(댓글) , 와 daoo(게시글)를 사용하는데
위에 서비스 의존성 주입부분에서 DAO를 주입 시켜주고 사용하면 된다.
@Service
public class ReplyServiceImpl implements ReplyService {
@Inject
public ReplyDAO dao;
@Inject
public BoardDAO daoo;
하나의 dao에서 구현한 경우에는 해당 dao만 주입시키고 사용하면 된다.
************************************
위에선 글 작성시 해당 게시글(PR로 구분)의 댓글수 컬럼에 update쿼리로 +1을 갱신해줬고
반대로 글 삭제시 해당 게시글 댓글수 컬럼에 -1을 해줘야 한다.
serviceImpl
@Transactional
@Override
public void deleteReply(ReplyVO vo) throws Exception{
int bno = dao.getBno(vo.getRno());
dao.deleteReply(vo);
daoo.replyCount(bno, -1);
}
<select id="getBno" resultType="int">
SELECT
bno
FROM
MP_REPLY
WHERE
rno = #{rno}
</select>
ReplyDAO
public int getBno(int rno)throws Exception
ReplyDAOImpl
public int getBno(int rno)throws Exception{
return sql.selectOne("replyMapper.getBno", rno);
}
일단 순서대로 가장 위의 쿼리부터 설명하겠다.
제일 먼저 PR의 값을 rno를 기준으로 int값인 "bno"로 가져온다.
그 후 삭제쿼리를 실행하고 댓글수 업데이트쿼리에 -1을 설정해준다.
아래는 "bno"를 얻기위한 쿼리이다.
왜 글작성과 다르게 글 삭제는 bno를 따로 구하는가?
이건 뷰단에서 오는 파라미터값과 관련이 있다.
ReplyVO는 rno(댓글PR)는 물론 bno(게시글PR)도 받을 수 있다.
댓글작성은 bno, rno,게시글내용,작성자 등등을 모두 필요로 한다.
그래서 뷰단(댓글작성창)에서 bno를 따로 입력하지 않지만 input type:hidden으로 값을 가져온다.(rno는 시퀸스로 생성)
하지만 글 삭제 쿼리는 rno만을 필요로 한다.
그래서 뷰단(댓글삭제버튼)에서 rno만을 파라미터로 보낸다.
댓글작성쿼리와 매개변수로 같은 ReplyVO를 가지고 있지만 VO에 bno값이 없어서,
따로 bno를 구하는 쿼리를 사용해 rno을 기준으로 bno를 구한다.
물론 뷰단에서 글 삭제시에도 rno뿐만아니라 bno까지 파라미터값으로 보낼 수 있게 하면
daoo.replyCount(vo.getBno(), -1);
로 코드를 작성할 수도 있을 것이다.
하지만 다르게 값을 받고 사용해보고 싶었고 이미 한번 설정한 뷰단 건드리기가 귀찮아서 ;;; 그냥 하는 부분도 있다.
이렇게 하면 위에서 언급했던 문제를 해결하고 ajax 비동기식댓글 작성과 삭제시에도 조회수와 따로 댓글수가 반영된다.
두개 방법의 차이는 댓글수 갱신을 어디서 하는가이다.
위의 방법은 댓글수 갱신을 read(게시글 내용조회 및 상세페이지로 이동)부분에 넣는다.
댓글작성은 read페이지에서 하기 때문에 read페이지의 게시글PR을 이용해서
내용을 볼때마다 댓글수갱신이 되게 로직을 만들었다.
비동기방식의 댓글작성을 사용하지 않으면 댓글작성시 새로고침이 될 것이고,
그럼 read가 다시한번 로딩될테니 그때 댓글수가 갱신되어 목록부분의 댓글수에 변화가 생기는 것이다.
작성,삭제를 구분 할 필요가 없다.
아래의 방법은 댓글수 갱신을 writeReply(댓글작성)에서 한다.
댓글작성시 댓글수가 갱신되기 때문에, 따로 새로고침을 하지 않아도 주소를 입력해 목록페이지로 돌아가면
댓글수가 갱신이 되어있다. 하지만 write와 delete의 로직을 나누고 조금 더 복잡해진다.
아래의방법에서 갑자기 서브쿼리가 -1,+1 쿼리로 바뀌었는데 ,
당연히 아래의 방법도 서브쿼리를 사용 할 수 있다.
위의 방법에서
read가 아닌 writeReply과 deleteReply(+bno값구하기)에 쿼리를 넣어주면 동일하게 동작한다.
하지만 서브쿼리문을 사용하면 bno값을 기준으로 해당게시글 댓글테이블의 모든 댓글을 select count해서 update하는 것 이다.
단순히 댓글수에 amount(+1,-1)을 update하는 것과 당연히 성능 차이가 날 것으로 생각해 이런 방식으로 구현해보았다.
물론 위쪽의 방법에서 뷰단에서 댓글삭제시 bno값이 넘어오게하고 , 글작성과 삭제 둘다 rno,bno값이 있는 상태에서,
read에서 amount를 적용하는 방식으로도 구현 할 수도 있을 것이다.
정리하자면
동기식 댓글작성시엔 간단하게 댓글작성페이지 (read)에 댓글수로직을 추가해,반영이 될 수 있게 하면 됨
비동기식 댓글작성시엔 write와 delete를 따로 나누어주고 댓글작성(insert)부분에 댓글수로직을 추가하면 된다.
댓글수는
해당 게시물 댓글테이블의 모든 댓글을 count해서 개수를 갱신하는 방식과,
해당 게시물 댓글테이블의 댓글수 0에서 작성시 +1 ,삭제시 -1이 갱신되게 하는 방식이 있다.
여러가지 방식을 합치고 섞어서 사용하면서,
뷰단의 파라미터값과 VO의 관계 , 모델에 파라미터값을 주는 방식의 다양성 , DAO와 Service, Controller의 역할 차이를
좀 더 자세하게 알게 되었다.
이 글을 보는 사람은 많지 않겠지만 같은 문제에 직면했다면 아직 위의 역할차이를 잘 이해하지 못한 단계일테니,
위에서 설명한 여러가지 방법들을 직접 수정해가며 그 차이를 확인해보길 바란다.
'SPRING > IceWater Community' 카테고리의 다른 글
[스프링]게시판 첨부파일 다운로드 (3) (1) | 2021.08.30 |
---|---|
[스프링] 게시글 첨부파일 조회 구현 설명(select) (2) (1) | 2021.08.30 |
[스프링]게시판 첨부파일 업로드 구현 설명(insert) (1) (1) | 2021.08.30 |
[스프링] 게시글 조회수와 트랜잭션 구현 (0) | 2021.08.25 |
스프링 페이징 구현 - 게시글 수정,삭제,내용보기 후 검색어유지 (1) | 2021.08.23 |