개발/[Spring] 블로그 만들기

[코드로 배우는 스프링 웹 프로젝트] 20강. 댓글과 댓글 수에 대한 처리 (트랜잭션 적용)

ee2ee2 2022. 5. 25. 15:58
728x90
반응형

해당 프로젝트는 코드로 배우는 스프링 웹 프로젝트(개정판)을 기반으로 진행됩니다.


게시글에 달린 댓글의 개수를 나타내기 위해 댓글 추가시, replyCnt(댓글 개수) 컬럼을 update하는 예제를 진행해본다.

먼저, tbl_board 테이블에 replyCnt 컬럼을 추가한다. (기본 값 0)

alter table tbl_board add (replyCnt number default 0);

기존에 댓글이 존재했다면, replyCnt에 반영해 두어야 하므로 아래의 쿼리를 실행한다.

update tbl_board set replyCnt = (select count(rno) 
                                 from tbl_reply 
                                 where tbl_reply.bno = tbl_board.bno);

 

적용 결과


20.1 프로젝트 수정

위와 같이 테이블을 수정하였으므로, BoardVO 클래스와 MyBatis의 SQL, BoardService 등을 수정할 필요가 있다.

 

20.1.1 BoardVO, BoardMApper 수정

BoardVO 클래스

package org.zerock.domain;

import java.util.Date;

import lombok.Data;

@Data
public class BoardVO {
	private Long bno;
	private String title;
	private String content;
	private String writer;
	private Date cdate;
	private Date udate;
	
	private int replyCnt;
}

BoardMapper 인터페이스

BoardMapper 인터페이스에는 새롭게 replyCnt를 업데이트하는 메소드를 추가해야 한다.

	//게시글 수정 : 특정 게시글 수정하기
	//수정하고자하는 게시글이 있다면 1, 없으면 0 반환
	public int modify(BoardVO board);
	
	//게시글의 전체 개수 구하기
	public int getTotalCount(Criteria cri);
	
	//게시글에 대한 댓글 수 업데이트
	public void updateReplyCnt(@Param("bno") Long bno, @Param("amount") int amount);

새롭게 추가된 updateReplyCnt()는 해당 게시물의 번호인 bno와 증가나 감소는 의미하는 amount 변수에 파라미터를 받을 수 있도록 처리한다. 댓글이 등록되면 1증가, 삭제되면 1감소한다.

MyBatis의 SQL을 처리할 때, 2개 이상의 데이터를 전달하려면 @Param이라는 어모테이션을 이용해 처리해야 한다.

 

BoardMapper.xml의 수정

<!-- 게시글의 페이지 번호와 읽어올 개수에 따라 게시글을 조회하는 함수 -->
<!-- CDATA 섹션은 XML에서 사용할 수 없는 부등호를 사용하기 위함. XML의 경우 '<,>'는 태그로 인식하는데, 이를 막기 위함이다. -->
<select id ="getListWithPaging" resultType="org.zerock.domain.BoardVO">
	<![CDATA[
		select *
		from
			(select /*+ INDEX_DESC(tbl_board pk_board) */
			 		rownum rn, bno, title, content, writer, cdate, udate, replyCnt
			 from tbl_board
			 where 
	]]>
	
	<include refid="criteria"></include>
	 
	 <![CDATA[
	 	rownum <= #{pageNum} * #{amount}
			)
		where rn > (#{pageNum} - 1) * #{amount}
	 ]]>
</select>

...생략

<update id="updateReplyCnt">
	update tbl_board 
	set replyCnt = replyCnt + #{amount}
	where bno = #{bno}
</update>

20.1.2 ReplyServiceImpl의 트랜잭션 처리

ReplyServiceImpl 클래스는 기존에는 ReplyMapper만을 이용했지만, 반정규화 처리가 되면서 BoardMapper를 같이 이용해야하는 상황이다. (댓글의 개수 처리) ReplyServiceImpl에서 댓글의 추가/삭제가되는 상황이면 두 Mapper 모두 사용하며 트랜잭션으로 처리되어야 한다.

 

ReplyServiceImpl 클래스

import lombok.Setter;
import lombok.extern.log4j.Log4j;

@Service
@Log4j
//@AllArgsConstructor
public class ReplyServiceImpl implements ReplyService{

	/* 
	 * ReplyServiceImpl은 ReplyMapper에 의존적인 관계이기 때문에 @Setter를 이용해서 처리하거나, 
	 * 스프링 4.3의 생성자와 자동주입을 이용해서 @AllArgsConstructor으로 대체 가능하다. 
	 * 
	 * 추가적으로 boardMappeer를 이용하면서 자동주입 대신 @Setter를 통한 주입이 이뤄져야 함.
	 */
	
	@Setter(onMethod_ = @Autowired) //-> @AllArgsConstructor으로 대체 가능
	private ReplyMapper mapper;
	
	@Setter(onMethod_ = @Autowired)
	private BoardMapper boardMapper;
	
	//댓글 등록의 경우에는 파라미터로 전달받은 ReplyVO 내에 게시글 번호가 존재하므로 이를 이용해서 댓글을 추가함.
	@Transactional
	@Override
	public int register(ReplyVO vo) {
		log.info("register....... " + vo);
		
		boardMapper.updateReplyCnt(vo.getBno(), 1);
		return mapper.insert(vo);
	}	
    
    ..생략
    
	//댓글의 삭제는 파라미터가 댓글의 번호인 rno만을 받기 때문에 해당 댓글의 게시물을 알아내는 과정이 필요함.
	@Transactional
	@Override
	public int remove(Long rno) {
		log.info("remove....... " + rno);
		
		ReplyVO vo = mapper.read(rno);
		
		boardMapper.updateReplyCnt(vo.getBno(), -1);
		
		return mapper.delete(rno);
	}
    
    ..생략

20.1.3 화면 수정

게시물의 목록 화면에서는 댓글의 숫자가 출력될 수 있도록 댓글의 숫자를 반영한다.

list.jsp의 일부

<c:forEach items="${list}" var="board">
    <tr>
        <td><c:out value="${board.bno}"/> </td>
        <td>
            <a class='move' href='<c:out value="${board.bno}"/>'><c:out value="${board.title}"/>
                <b>[ <c:out value="${board.replyCnt}"/> ]</b>
            </a>
        </td>
        <td><c:out value="${board.writer}"/> </td>
        <td><fmt:formatDate pattern="yyyy-MM-dd" value="${board.cdate}"/> </td>
        <td><fmt:formatDate pattern="yyyy-MM-dd" value="${board.udate}"/> </td>
    </tr>
</c:forEach>

실행 결과


다음 장에서는 파일 업로드 처리 기능을 추가해 볼 것이다!