2번째 필수강의 pt.4 10, 11강 요약
댓글 기능을 추가하기 위해 다음과 같은 순서로 진행한다.
- DB테이블 생성
- Mapper.xml 작성
- DAO 작성 & 테스트
- Service 작성 & 테스트
- 컨트롤러 작성 & 테스트
- 뷰(UI) 작성 & 테스트
1~5번은 백엔드에서, 6번은 프론트에서 담당한다.
DAO에서는 순수하게 데이터에 접근하는 기능을 작성하고, Setvice에서는 비즈니스 로직과 트랜잭션을 처리하며, 컨트롤러에서는 요청과 응답을 처리하고, 데이터 유효성을 검증하며 실행 흐름을 제어한다.
CREATE TABLE `springbasic`.`comment` (
`cno` INT NOT NULL AUTO_INCREMENT,
`bno` INT NOT NULL,
`pcno` INT NULL,
`comment` VARCHAR(3000) NULL,
`commenter` VARCHAR(30) NULL,
`reg_date` DATETIME NULL DEFAULT now(),
`up_date` DATETIME NULL DEFAULT now(),
PRIMARY KEY (`cno`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
COLLATE = utf8_bin;
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fastcampus.ch4.dao.CommentMapper">
<delete id="deleteAll" parameterType="int">
DELETE FROM comment
WHERE bno = #{bno}
</delete>
<select id="count" parameterType="int" resultType="int">
SELECT count(*) FROM comment
WHERE bno = #{bno}
</select>
<delete id="delete" parameterType="map">
DELETE FROM comment WHERE cno = #{cno} AND commenter = #{commenter}
</delete>
<insert id="insert" parameterType="CommentDto">
INSERT INTO comment
(bno, pcno, comment, commenter, reg_date, up_date)
VALUES
(#{bno}, #{pcno}, #{comment}, #{commenter}, now(), now())
</insert>
<select id="selectAll" parameterType="int" resultType="CommentDto">
SELECT cno, bno, pcno, comment, commenter, reg_date, up_date
FROM comment
WHERE bno = #{bno}
ORDER BY reg_date ASC, cno ASC
</select>
<select id="select" parameterType="int" resultType="CommentDto">
SELECT cno, bno, pcno, comment, commenter, reg_date, up_date
FROM comment
WHERE cno = #{cno}
</select>
<update id="update" parameterType="CommentDto">
UPDATE comment
SET comment = #{comment}
, up_date = now()
WHERE cno = #{cno} and commenter = #{commenter}
</update>
</mapper>
Mapper 문서를 참고하여 작성한다. 작성 후 인터페이스를 추출하였다.
@Repository
public class CommentDaoImpl implements CommentDao {
@Autowired
SqlSession session;
String namespace = "com.fastcampus.ch4.dao.CommentMapper.";
@Override
public int deleteAll(Integer bno) throws Exception{
return session.delete(namespace+"deleteAll", bno);
}
@Override
public int count(Integer bno) throws Exception{
return session.selectOne(namespace+"count", bno);
}
@Override
public int delete(Integer cno, String commenter) throws Exception{
Map map = new HashMap();
map.put("cno", cno);
map.put("commenter", commenter);
return session.delete(namespace+"delete", map);
}
@Override
public int insert(CommentDto dto) throws Exception{
return session.insert(namespace + "insert", dto);
}
@Override
public List<CommentDto> selectAll(Integer bno) throws Exception{
return session.selectList(namespace + "selectAll", bno);
}
@Override
public CommentDto select(Integer cno) throws Exception{
return session.selectOne(namespace+"select", cno);
}
@Override
public int update(Integer cno, String commenter) throws Exception{
Map map = new HashMap();
map.put("cno", cno);
map.put("commenter", commenter);
return session.update(namespace + "update", map);
}
테이블을 생성했던 것과 동일하게 작성해준다. 생성자 메서드는 default 값이 있는 cno, reg_date, up_date를 제외하도록 한다.
댓글이 삭제되면, 게시글에서도 댓글의 개수가 수정되어야 하기 때문에 BoardDao와 CommentDao가 둘 다 필요하다.
다만 service에 하나씩 @Autowired로 주입하기보다는(=인스턴스 변수 주입) 생성자 메서드로 주입받는 것이 낫다. 이 때는 애너테이션을 붙이지 않아도 주입할 수 있다.
public CommentServiceImpl(CommentDao commentDao, BoardDao boardDao) {
this.commentDao = commentDao;
this.boardDao = boardDao;
}
BoardMapper에 아래와 같은 sql을 추가하고, 이 기능을 DAO와 Service에도 넣어준다.
이 기능은 CommentServiceImpl에서 사용하며, BoardService에서 사용할 필요는 없다.
<update id="updateCommentCount" parameterType="map">
UPDATE springbasic.board
SET comment_count = comment_count + #{count}
WHERE bno = #{bno}
</update>
덧글을 삭제하는 기능은 먼저 덧글 수에서 -1 시킨 후, 댓글을 지워야 한다. 이 때 쿼리가 두 번 실행되므로 둘 중에 하나라도 실패해서는 안되기 때문에 @Transactional 애너테이션을 사용하며, 예외가 발생하면 롤백이 실행되도록 작성하였다.
@Override
@Transactional(rollbackFor = Exception.class)
public int remove(Integer cno, Integer bno, String commenter) throws Exception {
int rowCnt = boardDao.updateCommentCount(bno, -1);
System.out.println("updateCommentCnt - rowCnt = " + rowCnt);
// throw new Exception("test");
rowCnt = commentDao.delete(cno, commenter);
System.out.println("rowCnt = " + rowCnt);
return rowCnt;
}