본문 바로가기
다양한 기술들/레거시 Web 실습

[14] 댓글기능 : Ajax, json, gson을 활용

by 예스p 2023. 1. 20.

댓글기능

댓글 조회와 댓글입력 기능을 구현해보자(수정 삭제 생략)
이때, 1초마다 댓글목록이 리로드되어 갱신된다.


 

 


댓글조회

일반 게시글 목록에서 게시글을 클릭하면 조회되는 상세페이지가 있다.

해당 상세페이지가 로딩되면 해당 게시글의 댓글을 가져오도록 로직을 짜보자.


STEP1) Reply.java

  • 생성위치 : com.br.board.model.vo
  • 댓글정보를 보관할 vo 클래스를 만든다.
  • 작성자 번호는 숫자와 문자 둘다 넣을수 있으므로 String 타입으로 지정.
  • 날짜는 왠만하면 그냥 Date타입 말고 String 타입으로 한다.

 

 

STEP2) board-mapper.xml (수정)

  • 게시글의 댓글을 조회하는데 필요한 sql문을 작성하면서 필요한 데이터를 확인해본다.
SELECT
       REPLY_NO
     , REPLY_CONTENT
     , USER_ID
     , TO_CHAR(CREATE_DATE, 'YY/MM/DD HH:MI') "CREATE_DATE"
 FROM REPLY R
 JOIN MEMBER ON (REPLY_WRITER=USER_NO)
WHERE R.STATUS='Y' 
  AND REF_BNO = 현재 내가 보고있던 게시글 번호
ORDER
   BY REPLY_NO DESC

 

 

STEP3) boardDetailView.jsp (수정)

  • 화면딴에서 댓글영역을 구현한다.
    >> 댓글작성은 로그인한 상태에서만 가능하게 설정한다.
  • 댓글목록을 조회하는 함수는  selectReplyList로 명명하고
    페이지가 다 로드되면 호출되도록 $(function(){...})안에서 호출한다.
<div id="reply-area">
    <table border="1">
        <thead>
            <tr>
                <th>댓글작성</th>
                <% if(loginUser != null) { %>
                <!-- 로그인이 되어있을 경우 -->
                <td><textarea cols="50" rows="3" style="resize:none;"></textarea></td>
                <td><button>댓글등록</button></td> 
                <% }else{ %>
                <!-- 로그인이 되어있지 않은 경우 -->
                <td><textarea cols="50" rows="3" style="resize:none;">로그인후 이용이 가능한 서비스입니다.</textarea></td>
                <td><button disabled>댓글등록</button></td>
                <% } %>
            </tr>
        </thead>
        <tbody>
            <!-- 댓글이 뿌려질 자리 -->
        </tbody>
    </table>
</div>

<script>
    // 페이지가 로드되면 selectReplyList 메소드 실행
    $(function(){
        selectReplyList();
    })
    // ajax로 해당 게시글에 달린 댓글 목록 조회해오는 메소드
    function selectReplyList(){
        $.ajax({
            url:"<%=contextPath%>/rlist.bo",
            //숫자는 따옴표로 묶지 않아도 된다.
            data:{no:<%=b.getBoardNo()%>},
            success:function(list){
                //console.log(list); 댓글 형태 확인
                let value = "";
                if(list.length == 0) {
                    //댓글이 없을 경우
                    value += "<tr>"
                          +      "<td colspan='3'>조회된 댓글이 없습니다.</td>"
                          + "</tr>"
                }else{
                    //댓글이 있을 경우
                    for(let i=0; i<list.length; i++){
                    value += "<tr>"       //필드명이 키값이다. 
                          +       "<td>" + list[i].replyWriter + "</td>"
                          +       "<td>" + list[i].replyContent + "</td>"
                          +       "<td>" + list[i].createDate + "</td>"
                          +  "</tr>";
                    } 
                }
                $('#reply-area tbody').html(value);
            },error:function(){
                console.log('댓글목록 조회용 ajax통신 실패');
            }
        })
    }    
</script>

 

 

STEP4) AjaxReplyListController.java

  • 생성위치 : com.br.board.controller
    서블릿/매핑값 rlist.bo
  • 상세페이지가 로딩되자마자 실행되는, 댓글목록을 조회해서 데이터를 넘겨주는 Ajax용 서블릿
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    int boardNo = Integer.parseInt(request.getParameter("no"));
    ArrayList<Reply> list = new BoardService().selectReplyList(boardNo);
    response.setContentType("application/json; charset=utf-8");
    // [{...}, {...}, ... ] 의 형태로 돌려줘야함.
    // toJson()메소드를 호출할때 객체와 요청했던곳과의 스트림을 제시
    new Gson().toJson(list, response.getWriter());
}

 

 

STEP5) BoardService.java (수정)

  • selectReplyList(boardNo) 메소드 작성
public ArrayList<Reply> selectReplyList(int boardNo) {
    Connection conn = getConnection();
    ArrayList<Reply> list = new BoardDao().selectReplyList(conn, boardNo);
    close(conn);
    return list;
}

 

 

STEP6) BoardDao.java (수정)

  • selectReplyList(conn, boardNo) 메소드 작성
public ArrayList<Reply> selectReplyList(Connection conn, int boardNo) {
    ArrayList<Reply> list = new ArrayList<>();
    PreparedStatement pstmt = null;
    ResultSet rset = null;
    String sql = prop.getProperty("selectReplyList");
    try {
        pstmt = conn.prepareStatement(sql);
        pstmt.setInt(1, boardNo);
        rset = pstmt.executeQuery();
        while(rset.next()) {
            Reply r = new Reply();
            r.setReplyNo(rset.getInt("reply_no"));
            .....
            list.add(r);
        }
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        close(rset);
        close(pstmt);
    }
    return list;
}

 

 


댓글입력

댓글을 입력했을때 db에 insert후
새롭게 댓글목록을 리로딩해서 내가 쓴 댓글이 보이도록 한다.


STEP1) board-mapper.xml (수정)

  • Reply테이블이 댓글을 insert하는 구문을 작성해보면서 필요한 데이터들을 확인해본다.
INSERT
  INTO REPLY
     (
       REPLY_NO
     , REPLY_CONTENT
     , REF_BNO
     , REPLY_WRITER
     )
VALUES 
     (
       SEQ_RNO.NEXTVAL
     , 사용자가 입력한 댓글 내용
     , 현재보굈던 게시글 번호
     , 작성자의 회원번호
     )

 

 

STEP2) boardDetailView.jsp (수정)

  • 댓글등록에 클릭시 insertReply 함수 발동되도록 한다
  • Ajax에서 data를 담는 객체에 userNo:<%=loginUser.getUserNo()%> 구문을 쓰면 비회원 상태 로딩시 null오류난다.
    회원이든 비회원이든 스크립트는 한번 쫙 읽히기 때문이다.
    >>서블릿에서 데이터를 담도록 한다.
댓글작성 : <textarea cols="50" rows="3" style="resize:none;"></textarea>
<button onclick="insertReply();">댓글등록</button>
.....
<script>
    function insertReply(){
        $.ajax({
            url:"<%=contextPath%>/rinsert.bo",
            data:{
                content:$('#reply-area textarea').val(),
                no:<%=b.getBoardNo()%>
                //여기서 UserId를 담는 구문을 쓰면, 오류가 난다
            },
            type"post",
            success:function(result){
            if(result>0) { //댓글 등록 성공
                //댓글란에 적힌것은 초기화 시킨다.
                $("#reply-area textarea").val("");
                //db로부터 댓글들을 재조회 한다.
                selectReplyList();    					
            }else {
                alert("댓글등록 실패");
            }
            }, error:function(){
                console.log("댓글작성용 ajax 통신 실패")
            }
        })
    }
</script>

 

 

STEP3) AjaxReplyInsertController.java

  • 생성위치 : com.br.board.controller
    서블릿/매핑값 rinsert.bo
  • 일반 서블릿과 달리 result에 담긴 성공여부를 판별하지 않고 바로 응답데이터를 넘기면 된다.
    >> 이때, result는 한글이 없으므로 setContentType 안해도 됨.
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String replyContent = request.getParameter("content");
    int boardNo = Integer.parseInt(request.getParameter("no"));
    int userNo = ((Member)request.getSession().getAttribute("loginUser")).getUserNo();
    Reply r = new Reply();
    r.setReplyContent(replyContent);
    r.setRefBoardNo(boardNo);
    r.setReplyWriter(String.valueOf(userNo));
    int result = new BoardService().insertReply(r);
		
    response.getWriter().print(result);
}

 

 

STEP4) BoardService.java (수정)

  • insertReply(r) 메소드 작성
public int insertReply(Reply r) {
    Connection conn = getConnection();
    int result = new BoardDao().insertReply(conn, r);
    if(result>0) {
        commit(conn);
    }else {
        rollback(conn);
    }
    close(conn);
    return result;
}

 

 

STEP5) BoardDao.java (수정)

  • insertReply(conn, r) 메소드 작성
public int insertReply(Connection conn, Reply r) {
    int result = 0;
    PreparedStatement pstmt = null;
    String sql = prop.getProperty("insertReply");
    try {
        pstmt = conn.prepareStatement(sql);
        pstmt.setString(1, r.getReplyContent());
        pstmt.setInt(2, r.getRefBoardNo());
        pstmt.setString(3, r.getReplyWriter());
        result = pstmt.executeUpdate();
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        close(pstmt);
    }
    return result;
}

 

 


주기적으로 댓글 리로드

1초마다 댓글목록을 새로 조회하도록 한다.

해당 원리는 채팅에서도 활용할 수 있다.


STEP1) AjaxReplyListController.java (수정)

  • 윈도우 객체의 함수 setInterval(함수, ms) 사용
    >>이때 함수에 ()를 붙이지 않는다!
  • 위치는 $(function(){...}) 안
  • 테스트를 하기 위해서는 세션을 분리시켜야하는데,
    탭을 여러개 연다고 해도 세션은 공유됨.
    >> 크롬 시크릿창 활용
<script>
     // 페이지가 로드되면 selectReplyList 메소드 실행
     $(function(){
          selectReplyList();
          setInterval(selectReplyList, 1000)
     })
</script>

 

 

 

 

 

 

댓글