본문 바로가기

프로젝트 이야기/CRUD 미니 게시판

220327 (6) : 댓글 기능 구현

COMMENTS TABLE 생성

댓글 기능을 구현하기위해 댓글을 저장하는 DB 테이블을 생성해주었다.

CREATE TABLE COMMENTS (
	CNO INT(11) NOT NULL AUTO_INCREMENT,
    PNO INT(11) NOT NULL,
    CONTENT TEXT NOT NULL,
    WRITER VARCHAR(30) NOT NULL,
    CRE_DATE DATETIME NOT NULL,
    PRIMARY KEY (CNO, PNO)
) COMMENT "댓글";

CNO : 댓글의 고유번호

PNO : 댓글이 작성된 피드의 번호

CONTENT : 피드 내용

WRITER : 댓글쓴이

CRE_DATE : 댓글 생성 날짜

 

Comment VO 생성

 

CommentDao 생성

DB와 직접 연결되서 Comment 데이터를 관리할 Dao를 생성해준다.

public class MySqlCommentDao {

    public DataSource ds;

    public void setDataSource(DataSource ds) { this.ds = ds; }

    public List<Comment> selectList(int fno) throws Exception {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;

        try {
            conn = ds.getConnection();
            stmt = conn.createStatement();
            rs = stmt.executeQuery("SELECT CNO, CONTENT, WRITER, CRE_DATE FROM COMMENTS WHERE PNO="+fno);

            List<Comment> comments = new ArrayList<>();

            while (rs.next()) {
                comments.add(new Comment()
                        .setNo(rs.getInt("CNO"))
                        .setContent(rs.getString("CONTENT"))
                        .setWriter(rs.getString("WRITER"))
                        .setCreatedDate(rs.getDate("CRE_DATE"))
                );
            }

            return comments;
        } catch (Exception e) { throw e; }
        finally {
            closeAll(conn, stmt, null, rs);
        }
    }

    public int insert(Comment comment, int no) throws Exception {
        Connection conn = null;
        PreparedStatement stmt = null;

        try {
             conn = ds.getConnection();
             stmt = conn.prepareStatement("INSERT INTO COMMENTS (PNO, CONTENT, WRITER, CRE_DATE) VALUES (?, ?, ?, NOW())");
             stmt.setInt(1, no);
             stmt.setString(2, comment.getContent());
             stmt.setString(3, comment.getWriter());

             return stmt.executeUpdate();

        } catch (Exception e) { throw e; }
        finally {
            closeAll(conn, null, stmt, null);
        }
    }

    public int delete(int cno) throws Exception {
        Connection conn = null;
        PreparedStatement stmt = null;

        try {
            conn = ds.getConnection();
            stmt = conn.prepareStatement("DELETE FROM COMMENTS WHERE CNO=?");
            stmt.setInt(1, cno);

            return stmt.executeUpdate();
        } catch (Exception e) { throw e; }
        finally {
            closeAll(conn, null, stmt, null);
        }
    }

    public int countComments(int fno) throws Exception {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;

        try {
            conn = ds.getConnection();
            stmt = conn.createStatement();
            rs = stmt.executeQuery("SELECT COUNT(*) FROM COMMENTS WHERE PNO="+fno);

            if (rs.next()) {
                return rs.getInt("COUNT(*)");
            } else {
                return -1;
            }


        } catch (Exception e) { throw e; }
        finally {
            closeAll(conn, stmt, null, rs);
        }
    }

    private void closeAll(Connection conn, Statement stmt, PreparedStatement pre_stmt, ResultSet rs) {
        try { if (rs != null) rs.close(); } catch (Exception e) {}
        try { if (stmt != null) stmt.close(); } catch (Exception e) {}
        try { if (pre_stmt != null) stmt.close(); } catch (Exception e) {}
        try { if (conn != null) conn.close(); } catch (Exception e) {}
    }
}
  • selectList(int fno) : List<Comment> 피드 고유번호(fno)를 받아 해당 피드에 달린 댓글을 List 타입으로 리턴한다. 
  • insert(Comment comment) : int comment 값 객체를 COMMENTS 디비에 삽입하고, 고유 번호(CNO)를 리턴한다.
  • delete(int cno) : int 댓글 고유 번호(cno)에 해당 하는 댓글을 삭제한다.
  • countComments(int fno) : int 피드 고유번호(fno)를 받아 해당 피드에 달린 댓글의 개수를 int 타입으로 리턴한다.

 

FeedContentController에 댓글 기능 추가

 

  • getDataBinder()
public class FeedContentController implements Controller, DataBinding {

    @Override
    public Object[] getDataBinders() {
        return new Object[] {
                "no", Integer.class,
                "password", String.class,
                "comment", String.class
        };
    }
}

기존의 no(피드 번호) / password(피드 수정 비밀번호) 외에 댓글 생성시 필요한 데이터인 comment 까지 getDataBinder를 통해 전달 받는다.

 

  • 댓글 생성
public class FeedContentController implements Controller, DataBinding {

    @Override
    public String execute(Map<String, Object> model) throws Exception {
    
        // 2. create comments
        if (model.get("comment") != null) {
            String content = (String) model.get("comment");

            Comment comment = new Comment()
                    .setContent(content)
                    .setWriter(loginUser);

            commentDao.insert(comment, no);
        }
    }
}

다음은 FeedContentController에서 댓글을 생성하는 파트이다. 클라이언트로 부터 새로운 댓글이 전송되면, 댓글 내용과 현재 로그인 유저의 정보를 통해 Comment 객체를 만든후 dao 메서드를 통해 DB에 삽입한다.

 

  • FeedContentController 전문
public class FeedContentController implements Controller, DataBinding {

    MySqlFeedDao feedDao;
    MySqlCommentDao commentDao;

    public FeedContentController setFeedDao(MySqlFeedDao feedDao) {
        this.feedDao = feedDao;
        return this;
    }

    public FeedContentController setCommentDao(MySqlCommentDao commentDao) {
        this.commentDao = commentDao;
        return this;
    }

    @Override
    public Object[] getDataBinders() {
        return new Object[] {
                "no", Integer.class,
                "password", String.class,
                "comment", String.class
        };
    }

    @Override
    public String execute(Map<String, Object> model) throws Exception {

        Integer no = (Integer) model.get("no");
        HttpSession session = (HttpSession) model.get("session");
        Feed feed = feedDao.selectOne(no);

        String loginUser = (String) session.getAttribute("loginUser");

        // 1. check authority to modify when click modify button
        if (model.get("password") != null) {
            String password = (String) model.get("password");

            if (feed.getWriter().startsWith("익명")) {
                if (password.equals(feed.getWriter().substring(2))) {
                    return "redirect:edit.do?no="+no;
                }
            } else {
                if (feed.getWriter().equals(session.getAttribute("loginUser"))) {
                    return "redirect:edit.do?no="+no;
                }
            }

            model.put("alert", "수정 권한이 없습니다");
        }

        // 2. create comments
        if (model.get("comment") != null) {
            String content = (String) model.get("comment");

            Comment comment = new Comment()
                    .setContent(content)
                    .setWriter(loginUser);

            commentDao.insert(comment, no);
        }

        // 3. enter content page

        // check authority to edit
        if (!feed.getWriter().startsWith("익명")) {
            model.put("authority", "readonly");
        } else {
            model.put("authority", "");
        }

        // update views
        feedDao.updateViews(no);
        Integer counts = Integer.valueOf(commentDao.countComments(no));

        // parse feed writer name
        if (feed.getWriter().startsWith("익명")) {
            feed.setWriter("익명");
        }

        // feed content
        model.put("comments", commentDao.selectList(no));
        model.put("counts", counts.toString());
        model.put("feed", feed);
        return "/feed/FeedContent.jsp";
    }
}

commentDao.seletList 메서드를 통해 해당 피드에 작성된 댓글 리스트를 뷰에 전달한다.

commentDao.countsComments 메서드를 통해 해당 피드에 작성된 댓글의 개수를 뷰에 전달한다.