[Spring] 28.SpringProject-댓글처리(1)




댓글처리 - REST 방식



게시물에 댓글을 다는 것은 REST 방식으로 처리할 것인데요~!

댓글 처리를 위한 준비를 하나씩 해보도록 하겠습니다!

먼저 REST 방식을 이용하기 때문에 해당 컨트롤러에 맞는 URI를 결정하겠습니다!

REST 방식은 URI 가 하나의 자원을 의미한다는 것을 잊지 마시길 바랍니다!


image




마음같아서는 컨트롤러부터 만들고 싶지만 영속계층과 비즈니스 계층부터 만들어보겠습니다~!


댓글을 위한 테이블 설정




먼저, 하나의 게시글은 여러 개의 댓글을 가질 수 있는데, 그 댓글 자원을 보관하기 위해 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)
);




image




댓글의 수정과 삭제 시 별도의 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이 사용되었다는 점입니다!


자! 일단 댓글 처리를 위해서 비즈니스 계층과 영속 계층 설계는 끝이 났는데요~!

원래 단위 테스트를 해줘야하지만, 일단 패스하겠습니다!

다음 장에서 컨트롤러까지 구현한 다음에 제대로 동작하는지 확인해보겠습니다!