1. 댓글 기능 구현 순서
1) DB 테이블 생성
2) Mapper XML 작성
3) DAO 작성 & 테스트
4) Service 작성 & 테스트
5) 컨트롤러 작성 & 테스트
--> 백앤드
6) 뷰(UI) 작성 & 테스트
--> 프론트앤드
1) DB 테이블 생성


데이터 하나 삽입 후 테이블 조회
2) Mapper XML 작성 (commentMapper.xml)
<?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>
작성한 다음 쿼리창에 넣어서 돌려봐야 한다.
3) DAO 작성 & 테스트
- mapper.xml에 있는 sql들을 하나씩 불러오는 함수들 생성
이름을 CommentDao에서 인터페이스 추출 시 CommentDaoImpl로 해서 추출.
[CommentDaoImpl]
package com.fastcampus.ch4.dao;
import com.fastcampus.ch4.domain.CommentDto;
import org.apache.ibatis.session.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.stereotype.*;
import java.util.*;
@Repository
public class CommentDaoImpl implements CommentDao {
@Autowired
private SqlSession session;
private static String namespace = "com.fastcampus.ch4.dao.CommentMapper.";
@Override
public int count(Integer bno) throws Exception {
return session.selectOne(namespace+"count", bno);
} // T selectOne(String statement)
@Override
public int deleteAll(Integer bno) {
return session.delete(namespace+"deleteAll", bno);
} // int delete(String statement)
@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);
} // int delete(String statement, Object parameter)
@Override
public int insert(CommentDto dto) throws Exception {
return session.insert(namespace+"insert", dto);
} // int insert(String statement, Object parameter)
@Override
public List<CommentDto> selectAll(Integer bno) throws Exception {
return session.selectList(namespace+"selectAll", bno);
} // List<E> selectList(String statement)
@Override
public CommentDto select(Integer cno) throws Exception {
return session.selectOne(namespace + "select", cno);
} // T selectOne(String statement, Object parameter)
@Override
public int update(CommentDto dto) throws Exception {
return session.update(namespace+"update", dto);
} // int update(String statement, Object parameter)
}
[CommentDao]
package com.fastcampus.ch4.dao;
import com.fastcampus.ch4.domain.CommentDto;
import java.util.List;
public interface CommentDao {
int count(Integer bno) throws Exception // T selectOne(String statement)
;
int deleteAll(Integer bno) // int delete(String statement)
;
int delete(Integer cno, String commenter) throws Exception // int delete(String statement, Object parameter)
;
int insert(CommentDto dto) throws Exception // int insert(String statement, Object parameter)
;
List<CommentDto> selectAll(Integer bno) throws Exception // List<E> selectList(String statement)
;
CommentDto select(Integer cno) throws Exception // T selectOne(String statement, Object parameter)
;
int update(CommentDto dto) throws Exception // int update(String statement, Object parameter)
;
}
- CommentDto 생성
DB의 열 보면서 작성
bno, pcno, comment, commenter 생성자 생성.
전부 다 getter, setter, toString 생성
equals에 Date 변수 빼고 생성, 이후 next
package com.fastcampus.ch4.domain;
import java.util.Date;
import java.util.Objects;
public class CommentDto {
private Integer cno;
private Integer bno;
private Integer pcno;
private String comment;
private String commenter;
private Date reg_date;
private Date up_date;
public CommentDto() {}
public CommentDto(Integer bno, Integer pcno, String comment, String commenter) {
this.bno = bno;
this.pcno = pcno;
this.comment = comment;
this.commenter = commenter;
}
public Integer getCno() {
return cno;
}
public void setCno(Integer cno) {
this.cno = cno;
}
public Integer getBno() {
return bno;
}
public void setBno(Integer bno) {
this.bno = bno;
}
public Integer getPcno() {
return pcno;
}
public void setPcno(Integer pcno) {
this.pcno = pcno;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public String getCommenter() {
return commenter;
}
public void setCommenter(String commenter) {
this.commenter = commenter;
}
public Date getReg_date() {
return reg_date;
}
public void setReg_date(Date reg_date) {
this.reg_date = reg_date;
}
public Date getUp_date() {
return up_date;
}
public void setUp_date(Date up_date) {
this.up_date = up_date;
}
@Override
public String toString() {
return "CommentDto{" +
"cno=" + cno +
", bno=" + bno +
", pcno=" + pcno +
", comment='" + comment + '\'' +
", commenter='" + commenter + '\'' +
", reg_date=" + reg_date +
", up_date=" + up_date +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CommentDto that = (CommentDto) o;
return Objects.equals(cno, that.cno) && Objects.equals(bno, that.bno) && Objects.equals(pcno, that.pcno) && Objects.equals(comment, that.comment) && Objects.equals(commenter, that.commenter);
}
@Override
public int hashCode() {
return Objects.hash(cno, bno, pcno, comment, commenter);
}
}
- 테스트 생성


package com.fastcampus.ch4.dao;
import com.fastcampus.ch4.domain.*;
import org.junit.*;
import org.junit.runner.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.test.context.*;
import org.springframework.test.context.junit4.*;
import java.util.*;
import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"file:src/main/webapp/WEB-INF/spring/root-context.xml"})
public class CommentDaoImplTest {
@Autowired
CommentDao commentDao;
@Test
public void count() throws Exception {
commentDao.deleteAll(1); // 게시물 1번 댓글 다 지움
assertTrue(commentDao.count(1)==0);
}
@Test
public void delete() throws Exception {
commentDao.deleteAll(1);
CommentDto commentDto = new CommentDto(1, 0, "comment", "asdf");
assertTrue(commentDao.insert(commentDto)==1);
assertTrue(commentDao.count(1)==1);
}
@Test
public void insert() throws Exception {
commentDao.deleteAll(1);
CommentDto commentDto = new CommentDto(1, 0, "comment", "asdf");
assertTrue(commentDao.insert(commentDto)==1);
assertTrue(commentDao.count(1)==1);
commentDto = new CommentDto(1, 0, "comment", "asdf");
assertTrue(commentDao.insert(commentDto)==1);
assertTrue(commentDao.count(1)==2);
}
@Test
public void selectAll() throws Exception {
commentDao.deleteAll(1);
CommentDto commentDto = new CommentDto(1, 0, "comment", "asdf");
assertTrue(commentDao.insert(commentDto)==1);
assertTrue(commentDao.count(1)==1);
List<CommentDto> list = commentDao.selectAll(1);
assertTrue(list.size()==1);
commentDto = new CommentDto(1, 0, "comment", "asdf");
assertTrue(commentDao.insert(commentDto)==1);
assertTrue(commentDao.count(1)==2);
list = commentDao.selectAll(1);
assertTrue(list.size()==2);
}
@Test
public void select() throws Exception {
commentDao.deleteAll(1);
CommentDto commentDto = new CommentDto(1, 0, "comment", "asdf");
assertTrue(commentDao.insert(commentDto)==1);
assertTrue(commentDao.count(1)==1);
List<CommentDto> list = commentDao.selectAll(1);
String comment = list.get(0).getComment();
String commenter = list.get(0).getCommenter();
assertTrue(comment.equals(commentDto.getComment()));
assertTrue(commenter.equals(commentDto.getCommenter()));
}
@Test
public void update() throws Exception {
commentDao.deleteAll(1);
CommentDto commentDto = new CommentDto(1, 0, "comment", "asdf");
assertTrue(commentDao.insert(commentDto)==1);
assertTrue(commentDao.count(1)==1);
List<CommentDto> list = commentDao.selectAll(1);
commentDto.setCno(list.get(0).getCno());
commentDto.setComment("comment2");
assertTrue(commentDao.update(commentDto)==1);
list = commentDao.selectAll(1);
String comment = list.get(0).getComment();
String commenter = list.get(0).getCommenter();
assertTrue(comment.equals(commentDto.getComment()));
assertTrue(commenter.equals(commentDto.getCommenter()));
}
}

테스트 코드를 돌리면 테스트가 통과함을 확인할 수 있다.
4) Service 작성 & 테스트
- CommentService 클래스 생성
댓글이 추가되거나 삭제되면, BoardDao도 영향이 간다.
DB에서 board를 보면, comment_cnt가 있는데 comment가 추가되면 +1, 지워지면 -1이 되어야한다.
그래서 CommentService는 BoardDao와 CommentDao 두 개를 다 주입받아야한다.
필드 주입을 할 때 실수로 @Autowired을 빼먹어도 문제를 발견하지 못한다.
생성자 주입으로 해놓으면, 컴파일 할 때 이 문제를 알 수 있다.
그래서 필드 주입보다는 생성자 주입을 더 권장한다.

인터페이스 추출 수행.
- boardMapper.xml에서 sql문 생성


[CommentServiceImpl]
package com.fastcampus.ch4.service;
import com.fastcampus.ch4.dao.*;
import com.fastcampus.ch4.domain.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.stereotype.*;
import org.springframework.transaction.annotation.*;
import java.util.*;
@Service
public class CommentServiceImpl implements CommentService {
// 인스턴스 변수에 주입
// @Autowired
BoardDao boardDao;
// @Autowired
CommentDao commentDao;
// 생성자 주입. @Autowired 생략 가능. 생성자 하나일 때 가능
// 위보단 생성자 주입 사용해서 실수를 줄임.
@Autowired
public CommentServiceImpl(CommentDao commentDao, BoardDao boardDao) {
this.commentDao = commentDao;
this.boardDao = boardDao;
}
@Override
public int getCount(Integer bno) throws Exception {
return commentDao.count(bno);
}
// 댓글 수 감소와 지우기 둘 다 성공해야함. 예외 발생하면 rollback
@Override
@Transactional(rollbackFor = Exception.class)
public int remove(Integer cno, Integer bno, String commenter) throws Exception {
int rowCnt = boardDao.updateCommentCnt(bno, -1);
System.out.println("updateCommentCnt - rowCnt = " + rowCnt);
// throw new Exception("test");
rowCnt = commentDao.delete(cno, commenter);
System.out.println("rowCnt = " + rowCnt);
return rowCnt;
}
@Override
@Transactional(rollbackFor = Exception.class)
public int write(CommentDto commentDto) throws Exception {
boardDao.updateCommentCnt(commentDto.getBno(), 1);
// throw new Exception("test");
return commentDao.insert(commentDto);
}
@Override
public List<CommentDto> getList(Integer bno) throws Exception {
// throw new Exception("test");
return commentDao.selectAll(bno);
}
@Override
public CommentDto read(Integer cno) throws Exception {
return commentDao.select(cno);
}
@Override
public int modify(CommentDto commentDto) throws Exception {
return commentDao.update(commentDto);
}
}
[CommentService]
package com.fastcampus.ch4.service;
import com.fastcampus.ch4.domain.CommentDto;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
public interface CommentService {
int getCount(Integer bno) throws Exception;
@Transactional(rollbackFor = Exception.class)
int remove(Integer cno, Integer bno, String commenter) throws Exception;
@Transactional(rollbackFor = Exception.class)
int write(CommentDto commentDto) throws Exception;
List<CommentDto> getList(Integer bno) throws Exception;
CommentDto read(Integer cno) throws Exception;
int modify(CommentDto commentDto) throws Exception;
}
[CommentSeriviceImplTest]
package com.fastcampus.ch4.service;
import com.fastcampus.ch4.dao.*;
import com.fastcampus.ch4.domain.*;
import org.junit.*;
import org.junit.runner.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.test.context.*;
import org.springframework.test.context.junit4.*;
import java.util.*;
import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"file:src/main/webapp/WEB-INF/spring/root-context.xml"})
public class CommentServiceImplTest {
@Autowired
CommentService commentService;
@Autowired
CommentDao commentDao;
@Autowired
BoardDao boardDao;
@Test
public void remove() throws Exception {
boardDao.deleteAll();
BoardDto boardDto = new BoardDto("hello", "hello", "asdf");
assertTrue(boardDao.insert(boardDto) == 1);
Integer bno = boardDao.selectAll().get(0).getBno();
System.out.println("bno = " + bno);
commentDao.deleteAll(bno);
CommentDto commentDto = new CommentDto(bno,0,"hi","qwer");
assertTrue(boardDao.select(bno).getComment_cnt() == 0);
assertTrue(commentService.write(commentDto)==1);
assertTrue(boardDao.select(bno).getComment_cnt() == 1);
Integer cno = commentDao.selectAll(bno).get(0).getCno();
// 일부러 예외를 발생시키고 Tx가 취소되는지 확인해야.
int rowCnt = commentService.remove(cno, bno, commentDto.getCommenter());
assertTrue(rowCnt==1);
assertTrue(boardDao.select(bno).getComment_cnt() == 0);
}
@Test
public void write() throws Exception {
boardDao.deleteAll();
BoardDto boardDto = new BoardDto("hello", "hello", "asdf");
assertTrue(boardDao.insert(boardDto) == 1);
Integer bno = boardDao.selectAll().get(0).getBno();
System.out.println("bno = " + bno);
commentDao.deleteAll(bno);
CommentDto commentDto = new CommentDto(bno,0,"hi","qwer");
assertTrue(boardDao.select(bno).getComment_cnt() == 0);
assertTrue(commentService.write(commentDto)==1);
Integer cno = commentDao.selectAll(bno).get(0).getCno();
assertTrue(boardDao.select(bno).getComment_cnt() == 1);
}
}
'Spring' 카테고리의 다른 글
| ch4 12. 댓글 기능 구현(3) - UI 작성 (0) | 2023.03.22 |
|---|---|
| ch4 11. 댓글 기능 구현(2) - Controller 작성 (0) | 2023.03.22 |
| ch4 09. REST API Ajax (0) | 2023.03.22 |
| ch4 08. 게시판 검색 기능 추가하기(2) (0) | 2023.03.22 |
| ch4 07. 게시판 검색 기능 추가하기(1) (0) | 2023.03.21 |