[Spring] 28.SpringProject-댓글처리(1)
댓글처리 - REST 방식
게시물에 댓글을 다는 것은 REST 방식으로 처리할 것인데요~!
댓글 처리를 위한 준비를 하나씩 해보도록 하겠습니다!
먼저 REST 방식을 이용하기 때문에 해당 컨트롤러에 맞는 URI를 결정하겠습니다!
REST 방식은 URI 가 하나의 자원을 의미한다는 것을 잊지 마시길 바랍니다!
마음같아서는 컨트롤러부터 만들고 싶지만 영속계층과 비즈니스 계층부터 만들어보겠습니다~!
댓글을 위한 테이블 설정
먼저, 하나의 게시글은 여러 개의 댓글을 가질 수 있는데, 그 댓글 자원을 보관하기 위해 mysql에 reply 테이블을 생성하겠습니다!
CREATE TABLE reply (
rno int(11) NOT NULL AUTO_INCREMENT,
bno int(11) NOT NULL DEFAULT '0',
replytext varchar(1000) NOT NULL,
replyer varchar(50) NOT NULL,
regdate timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
updatedate timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (rno),
CONSTRAINT fk_board FOREIGN KEY (bno) REFERENCES board (bno)
);
댓글의 수정과 삭제 시 별도의 Primary key를 가지는 것이 편리하기 때문에 rno라는 칼럼을 생성하고, 해당 게시물의 번호를 의미하는 bno라는 컬럼을 가지도록 했습니다!
또 댓글과 원래의 게시물이 1:N 관계를 이루기 때문에 외래키를 설정해줬습니다!
댓글을 위한 도메인 객체 설계
다음과 같이 테이블과 유사한 도메인 객체(VO)도 만들어줍니다!
//ReplyVO.java
package com.gguri.swp.domain;
import java.util.Date;
public class ReplyVO {
private Integer rno;
private Integer bno;
private String replytext;
private String replyer;
private Date regdate;
private Date updatedate;
public Integer getRno() {
return rno;
}
public void setRno(Integer rno) {
this.rno = rno;
}
public Integer getBno() {
return bno;
}
public void setBno(Integer bno) {
this.bno = bno;
}
public String getReplytext() {
return replytext;
}
public void setReplytext(String replytext) {
this.replytext = replytext;
}
public String getReplyer() {
return replyer;
}
public void setReplyer(String replyer) {
this.replyer = replyer;
}
public Date getRegdate() {
return regdate;
}
public void setRegdate(Date regdate) {
this.regdate = regdate;
}
public Date getUpdatedate() {
return updatedate;
}
public void setUpdatedate(Date updatedate) {
this.updatedate = updatedate;
}
@Override
public String toString() {
return "ReplyVO [rno=" + rno + ", bno=" + bno + ", replytext=" + replytext + ", replyer=" + replyer
+ ", regdate=" + regdate + ", updatedate=" + updatedate + "]";
}
}
Mapper 작성
replyMapper.xml을 생성하여 댓글의 목록을 조회하거나, 댓글을 입력, 수정, 삭제하는 쿼리를 작성해줍니다!
<!-- boardMapper.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<!-- DTD 선언 -->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org/DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="replyMapper">
<!-- 댓글 삽입 -->
<insert id="create">
insert into reply (bno, replyer, replytext)
values( #{bno}, #{replyer}, #{replytext})
</insert>
<!-- 댓글 수정 -->
<update id="update">
update reply
set replytext = #{replytext}
where rno = #{rno}
</update>
<!-- 댓글 삭제 -->
<delete id="delete">
delete from reply where rno = #{rno}
</delete>
<!-- 페이지에 해당하는 댓글 목록 조회 -->
<select id="listPage" resultType="ReplyVO">
select *
from reply
where bno = #{bno}
order by rno desc
limit #{cri.pageStart}, #{cri.perPageNum}
</select>
<select id="getTotalCount" resultType="int">
select count(bno)
from reply
where bno = #{bno}
</select>
</mapper>
댓글에도 페이징 기능을 추가할 것이기 때문에 저번에 만든 Criteria 클래스와 PageMaker 클래스를 이용할 것입니다!
PageMaker 클래스를 이용하려면 댓글 전체의 개수를 알아야하므로 전체 개수를 구하는 쿼리도 추가했습니다~!
Service 객체 설계
그 다음 인터페이스 ReplyService를 작성해줍니다!
// ReplyService.java
package com.gguri.swp.service;
import java.util.List;
import com.gguri.swp.domain.Criteria;
import com.gguri.swp.domain.ReplyVO;
public interface ReplyService {
void register(ReplyVO reply) throws Exception;
void modify(ReplyVO reply) throws Exception;
void remove(Integer rno) throws Exception;
List<ReplyVO> listReplyPage(Integer bno, Criteria cri) throws Exception;
int getTotalCount(Integer bno) throws Exception;
}
그리고 구현체 ReplyServiceImpl도 생성!
//ReplyServiceImpl.java
package com.gguri.swp.service;
...
@Service
public class ReplyServiceImpl implements ReplyService{
@Inject
ReplyDAO replyDAO;
@Override
public void register(ReplyVO reply) throws Exception {
replyDAO.create(reply);
}
@Override
public void modify(ReplyVO reply) throws Exception {
replyDAO.update(reply);
}
@Override
public void remove(Integer rno) throws Exception {
replyDAO.delete(rno);
}
@Override
public List<ReplyVO> listReplyPage(Integer bno, Criteria cri) throws Exception {
return replyDAO.listPage(bno, cri);
}
@Override
public int getTotalCount(Integer bno) throws Exception {
return replyDAO.getToalCount(bno);
}
}
아직 ReplyDAO는 만들지 않았습니다!
만들러 가보죠!
DAO 객체 설계
ReplyDAO를 만들어줍니다!
//ReplyDAO.java
package com.gguri.swp.persistence;
import java.util.List;
import com.gguri.swp.domain.Criteria;
import com.gguri.swp.domain.ReplyVO;
public interface ReplyDAO {
void create(ReplyVO reply) throws Exception;
void update(ReplyVO reply) throws Exception;
void delete(Integer rno) throws Exception;
List<ReplyVO> listPage(Integer bno, Criteria cri) throws Exception;
int getToalCount(Integer bno);
}
구현체 ReplyDAOImpl 도 생성!
//ReplyDAOImpl.java
package com.gguri.swp.persistence;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import org.apache.ibatis.session.SqlSession;
import org.springframework.stereotype.Repository;
import com.gguri.swp.domain.Criteria;
import com.gguri.swp.domain.ReplyVO;
@Repository
public class ReplyDAOImpl implements ReplyDAO{
@Inject
SqlSession session;
private static final String NS = "replyMapper";
private static final String CREATE = NS + ".create";
private static final String UPDATE = NS + ".update";
private static final String DELETE = NS + ".delete";
private static final String LISTPAGE = NS + ".listPage";
private static final String GETTOTALCOUNT = NS + ".getTotalCount";
@Override
public void create(ReplyVO reply) throws Exception {
session.insert(CREATE, reply);
}
@Override
public void update(ReplyVO reply) throws Exception {
session.update(UPDATE, reply);
}
@Override
public void delete(Integer rno) throws Exception {
session.delete(DELETE, rno);
}
@Override
public List<ReplyVO> listPage(Integer bno, Criteria cri) throws Exception {
Map<String, Object>paramMap = new HashMap<>();
paramMap.put("bno", bno);
paramMap.put("cri", cri);
return session.selectList(LISTPAGE, paramMap);
}
@Override
public int getToalCount(Integer bno) {
return session.selectOne(GETTOTALCOUNT, bno);
}
}
여기서 주목해야할 점은 listPage 메소드에서 session.selectList의 매개변수로 bno와 cri를 전달해야하는데, selectList는 쿼리문과 매개변수 1개를 전달받을 수 있으므로 HashMap이 사용되었다는 점입니다!
자! 일단 댓글 처리를 위해서 비즈니스 계층과 영속 계층 설계는 끝이 났는데요~!
원래 단위 테스트를 해줘야하지만, 일단 패스하겠습니다!
다음 장에서 컨트롤러까지 구현한 다음에 제대로 동작하는지 확인해보겠습니다!