728x90

게시판.vol2.egg
8.00MB
게시판.vol1.egg
10.00MB

1. DB 테이블 생성

2. DTO 작성

3. Mapper XML 작성 , mybatis-configs.xml 작성

4. DAO 작성 , test

5. Service 작성 , test 

@Transactional(rollbackFor = Exception.class)

6. Controller 작성 , test

7. View 작성


1. DB 테이블 생성

CREATE TABLE IF NOT EXISTS `board` (
  `bno` int NOT NULL AUTO_INCREMENT,
  `title` varchar(100) NOT NULL,
  `content` text NOT NULL,
  `writer` varchar(30) NOT NULL,
  `view_cnt` int DEFAULT '0',
  `comment_cnt` int DEFAULT '0',
  `reg_date` datetime DEFAULT CURRENT_TIMESTAMP,
  `up_date` datetime DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`bno`)
) ENGINE=InnoDB AUTO_INCREMENT=387 DEFAULT CHARSET=utf8mb3;


CREATE TABLE IF NOT EXISTS `comment` (
  `cno` int NOT NULL AUTO_INCREMENT,
  `bno` int NOT NULL,
  `pcno` int DEFAULT NULL,
  `comment` varchar(3000) DEFAULT NULL,
  `commenter` varchar(30) DEFAULT NULL,
  `reg_date` datetime DEFAULT CURRENT_TIMESTAMP,
  `up_date` datetime DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`cno`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb3;


CREATE TABLE IF NOT EXISTS `user_info` (
  `id` varchar(30) NOT NULL,
  `pwd` varchar(30) DEFAULT NULL,
  `name` varchar(30) DEFAULT NULL,
  `email` varchar(30) DEFAULT NULL,
  `birth` date DEFAULT NULL,
  `sns` varchar(30) DEFAULT NULL,
  `reg_date` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;

 

2. CommentDTO 작성

package com.test.ch4.domain;

import java.util.*;

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;
    }

    @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);
    }

    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;
    }

    public Integer getCno() {
        return cno;
    }

    public void setCno(Integer cno) {
        this.cno = cno;
    }

    @Override
    public String toString() {
        return "CommentDto{" +
                "cno=" + cno +
                ", bno=" + bno +
                ", pcno=" + pcno +
                ", comment='" + comment + '\'' +
                ", commenter='" + commenter + '\'' +
                ", reg_date=" + reg_date +
                ", up_date=" + up_date +
                '}';
    }
}

 

3. CommentMapper XML 작성 , mybatis-configs.xml 작성

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
    <typeAliases>
        <typeAlias alias="BoardDto" type="com.test.ch4.domain.BoardDto"/>
        <typeAlias alias="CommentDto" type="com.test.ch4.domain.CommentDto"/>
        <typeAlias alias="SearchCondition" type="com.test.ch4.domain.SearchCondition"/>
    </typeAliases>
</configuration>
<?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.test.ch4.dao.BoardMapper">
    <select id="count" resultType="int">
        SELECT count(*) FROM board
    </select>

    <delete id="deleteAll">
        DELETE FROM board
    </delete>

    <delete id="delete" parameterType="map">
        DELETE FROM board WHERE bno = #{bno} and writer = #{writer}
    </delete>

    <insert id="insert" parameterType="BoardDto">
        INSERT INTO board
            (title, content, writer)
        VALUES
            (#{title}, #{content}, #{writer})
    </insert>

    <select id="selectAll" resultType="BoardDto">
        SELECT *
        FROM board
        ORDER BY reg_date DESC, bno DESC
    </select>

    <select id="select" parameterType="int" resultType="BoardDto">
        SELECT bno, title, content, writer
             , view_cnt, comment_cnt, reg_date
        FROM board
        WHERE bno = #{bno}
    </select>

    <select id="selectPage" parameterType="map" resultType="BoardDto">
        SELECT bno, title, content, writer, view_cnt, comment_cnt, reg_date
        FROM board
        ORDER BY reg_date DESC, bno DESC
            LIMIT #{from}, #{size}
    </select>

    <update id="update" parameterType="BoardDto">
        UPDATE board
        SET   title = #{title}
          , content = #{content}
          , up_date = now()
        WHERE bno = #{bno}
    </update>

    <update id="increaseViewCnt" parameterType="int">
        UPDATE board
        SET   view_cnt = view_cnt + 1
        WHERE bno = #{bno}
    </update>

    <sql id="searchCondition">
        <choose>
            <when test='option=="T"'>
                AND title LIKE concat('%', #{keyword}, '%')
            </when>
            <when test='option=="W"'>
                AND writer LIKE concat('%', #{keyword}, '%')
            </when>
            <otherwise>
                AND (title   LIKE concat('%', #{keyword}, '%')
                OR   content LIKE concat('%', #{keyword}, '%'))
            </otherwise>
        </choose>
    </sql>

    <select id="searchSelectPage" parameterType="SearchCondition" resultType="BoardDto">
        SELECT bno, title, content, writer, view_cnt, comment_cnt, reg_date
        FROM  board
        WHERE bno > 0
        <include refid="searchCondition"/>
        ORDER BY reg_date DESC, bno DESC
        LIMIT #{offset}, #{pageSize}
    </select>

    <select id="searchResultCnt" parameterType="SearchCondition" resultType="int">
        SELECT count(*)
        FROM  board
        WHERE bno > 0
        <include refid="searchCondition"/>
    </select>

    <update id="updateCommentCnt" parameterType="map">
        update board
        set comment_cnt = comment_cnt + #{cnt}
        where bno = #{bno}
    </update>


</mapper>
<?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.test.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, ifnull(pcno,cno) as pcno, comment, commenter, reg_date, up_date
        FROM comment
        WHERE bno = #{bno}
        ORDER BY pcno 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>

 

4. CommentDao 작성 , test

package com.test.ch4.dao;

import com.test.ch4.domain.CommentDto;

import java.util.List;

public interface CommentDao {
    int count(Integer bno) throws Exception;
    int deleteAll(Integer bno);
    int delete(Integer cno, String commenter) throws Exception;
    int insert(CommentDto dto) throws Exception;
    List<CommentDto> selectAll(Integer bno) throws Exception;
    CommentDto select(Integer cno) throws Exception;
    int update(CommentDto dto) throws Exception;
}
package com.test.ch4.dao;

import com.test.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.test.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)
}

 

test

package com.test.ch4.dao;

import com.test.ch4.domain.CommentDto;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;

import static org.junit.Assert.assertTrue;

@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);
        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()));
    }
}

 

5. Service 작성 , test 

package com.test.ch4.service;

import com.test.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;
}
package com.test.ch4.service;

import com.test.ch4.dao.*;
import com.test.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
    public CommentServiceImpl(CommentDao commentDao, BoardDao boardDao) {
        this.commentDao = commentDao;
        this.boardDao = boardDao;
    }

    @Override
    public int getCount(Integer bno) throws Exception {
        return commentDao.count(bno);
    }

    @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);
    }
}

 

6. Controller 작성 , test 

  ★ 서버가 잘못처리해서 500 에러가 나와야 하는데

      에러가 나든 성공하든 상태코드 200이 나옴.... > ResponseEntity 이용하기.

상황에 맞는 상태코드 표현 ( 전/후)

엔티티 : 응답이나 요청할때 전송할 대상을 말함

new ResponseEntity< List<CommentDto> >(list , HttpStatus.OK);

 

그럼 이제 controller 코드를 보자

package com.test.ch4.controller;

import com.test.ch4.domain.CommentDto;
import com.test.ch4.service.CommentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpSession;
import java.util.List;

//@Controller
//@ResponseBody
@RestController
public class CommentController {

    @Autowired
    CommentService service;

    //댓글을 수정하는 메서드
    @PatchMapping("/comments/{cno}")   // /ch4/comments/70 POST
    public ResponseEntity<String> modify(@PathVariable Integer cno, @RequestBody CommentDto dto , HttpSession session ) {
        String commenter = (String)session.getAttribute("id");
        dto.setCommenter(commenter);
        dto.setCno(cno);
        System.out.println("dto = " + dto);

        try {
            if(service.modify(dto) != 1)
                throw new Exception("Write failed");

            return new ResponseEntity<String>("MOD_OK" , HttpStatus.OK);
        } catch (Exception e) {
            e.printStackTrace();
            return new ResponseEntity<String>("MOD_ERR", HttpStatus.BAD_REQUEST);
        }
    }


    //댓글을 저장하는 메서드
    @PostMapping("/comments")   // /ch4/comments?bno=11 POST
    public ResponseEntity<String> write(@RequestBody CommentDto dto, Integer bno , HttpSession session ) {
        String commenter = (String)session.getAttribute("id");
        dto.setCommenter(commenter);
        dto.setBno(bno);
        System.out.println("dto = " + dto);

        try {
            if(service.write(dto) != 1)
                throw new Exception("Write failed");

            return new ResponseEntity<String>("WRT_OK" , HttpStatus.OK);
        } catch (Exception e) {
            e.printStackTrace();
            return new ResponseEntity<String>("WRT_ERR", HttpStatus.BAD_REQUEST);
        }
    }

    //지정된 댓글을 삭제하는 메서드
    @DeleteMapping("/comments/{cno}") // comments/11?bno=1010 (삭제할댓글번호) , uri 방식{cno}의 경우 @PathVariable
    public ResponseEntity<String> remove(@PathVariable Integer cno , Integer bno , HttpSession session ){
        String commenter = (String)session.getAttribute("id");
        try {
            int rowCount = service.remove(cno , bno, commenter);
            if(rowCount !=1)
                throw new Exception("Delete Failed");

            return new ResponseEntity<String>( "DEL_OK", HttpStatus.OK);
        } catch (Exception e) {
            e.printStackTrace();
            return new ResponseEntity<String>("DEL_ERR", HttpStatus.BAD_REQUEST);
        }
    }

    //지정된 게시물의 모든 댓글을 가져오는 메서드
    @RequestMapping(value = "/comments") //comments?bno=11
    public ResponseEntity<List<CommentDto>> list(Integer bno){

        List<CommentDto> list = null;
        try {
            list = service.getList(bno);
            return new ResponseEntity<List<CommentDto>>(list , HttpStatus.OK); //200
        } catch (Exception e) {
            e.printStackTrace();
            return new ResponseEntity<List<CommentDto>>(HttpStatus.BAD_REQUEST); //400
        }
    }
}

 

클래스 안의 메서드 모두 @ResponseBody 가 붙어야 한다면

클래스에 @ResponseBody 붙이고 메서드 에는 붙이지 않아도 된다.

@ResponseBody + @Controller 합친것 @RestController

//@Controller
//@ResponseBody
@RestController
public class CommentController {

 

7. View 작성

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <script src="https://code.jquery.com/jquery-1.11.3.js"></script>
</head>
<body>

<h2>commentTest</h2>
comment : <input type="text" name="comment"></body>
<button id="sendBtn" type="button">등록</button>
<button id="modBtn" type="button">수정</button>
<div id="commentList"></div>

<div id='replyForm' style='display: none'>
    <input type='text' name='replyComment'/>
    <button id='wrtRepBtn' type='button' >답글등록</button>
</div>

<script>

    let bno = 386; /** 이 부분은 게시판 상세보기와 연동하여 개발한다.. */
    $(document).ready(function(){

        showList(bno);

        /** 댓글 등록 */
        $("#sendBtn").click(function(){
            let comment = $("input[name=comment]").val();
            if(comment.trim() == ""){
                alert("댓글을 입력하세요.");
                $("input[name=comment]").focus();
                return;
            }

            $.ajax({
                type:'POST',
                url: '/ch4/comments?bno=' + bno,
                headers : { "content-type": "application/json"},
                dataType : 'text',
                data : JSON.stringify( {bno : bno , comment : comment} ),
                success : function(result){
                    alert(result);
                    showList(bno);
                },
                error   : function(){ alert("error") }
            });
        });

        /** 대댓글 등록 */
        $("#wrtRepBtn").click(function(){
            let pcno = $("#replyForm").parent().attr("data-pcno");
            let comment = $("input[name=replyComment]").val();
            if(comment.trim() == ""){
                alert("댓글을 입력하세요.");
                $("input[name=replyComment]").focus();
                return;
            }

            $.ajax({
                type:'POST',
                url: '/ch4/comments?bno=' + bno,
                headers : { "content-type": "application/json"},
                dataType : 'text',
                data : JSON.stringify( {bno : bno, pcno : pcno, comment : comment} ),
                success : function(result){
                    alert(result);
                    showList(bno);
                },
                error   : function(){ alert("error") }
            });
        });

        /** 댓글 수정 */
        $("#modBtn").click(function(){

            let cno = $(this).attr("data-cno");
            let comment = $("input[name=comment]").val();
            if(comment.trim() == ""){
                alert("댓글을 입력하세요.");
                $("input[name=comment]").focus();
                return;
            }

            if(cno == undefined){
                alert("수정할 댓글을 선택하세요.");
                return;
            }

            $.ajax({
                type:'PATCH',
                url: '/ch4/comments/' + cno,
                headers : { "content-type": "application/json"},
                dataType : 'text',
                data : JSON.stringify( {comment : comment} ),
                success : function(result){
                    alert(result);
                    showList(bno);
                },
                error   : function(){ alert("error") }
            });
        });
    });

    /** 댓글 삭제 */
    $("#commentList").on("click", ".delBtn", function(){

        let cno = $(this).parent().attr("data-cno");
        let bno = $(this).parent().attr("data-bno");

        $.ajax({
            type:'DELETE',
            url: '/ch4/comments/' + cno + '?bno=' + bno,
            success : function(result){
                alert(result);
                showList(bno);
            },
            error   : function(){ alert("error") }
        });
    });

    /** 댓글에서 수정 click */
    $("#commentList").on("click", ".modBtn", function(){
        let cno = $(this).parent().attr("data-cno");
        let comment = $("span.comment", $(this).parent()).text();

        // 1. comment의 내용을 input에 뿌려주기
        $("input[name=comment]").val(comment);

        // 2. cno 전달하기
        $("#modBtn").attr("data-cno", cno);
    });

    /** 댓글에서 답글 click */
    $("#commentList").on("click", ".replyBtn", function(){
        // 1.답글 입력 폼의 위치를 이동시킨다.
        $(this).parent().append( $("#replyForm") );

        // 1.답글을 입력할 폼을 보여준다.
        $("#replyForm").css("display","block");
    });

    /** 댓글 리스트 불러오기 */
    let showList = function(bno){
        $.ajax({
            type:'GET',
            url: '/ch4/comments?bno='+ bno,
            success : function(result){
                $("#commentList").html(toHtml(result));
            },
            error   : function(){ alert("error") }
        });
    }

    /** 댓글 html */
    let toHtml = function (comments){
        replyFormClear();

        let tmp = "<ul>";
        comments.forEach(function(comment){
            tmp +='<li data-cno=' + comment.cno
            tmp +=' data-pcno=' + comment.pcno
            tmp +=' data-bno=' + comment.bno + '>'
            if(comment.cno != comment.pcno){ tmp += " ㄴ "}
            tmp +=' commenter = <span class="commenter">' + comment.commenter + '</span>'
            tmp +=' comment = <span class="comment">' + comment.comment +'</span>'
            tmp +=' <button class="delBtn">삭제</button>'
            tmp +=' <button class="modBtn">수정</button>'
            tmp +=' <button class="replyBtn">답글</button>'
            tmp +='</li>'
        })
        return tmp + "</ul>"
    }

    /** form 원래대로 복원 */
    let replyFormClear = function(){
        $("input[name=replyComment]").val('');
        $("#replyForm").css("display","none");
        $("#replyForm").appendTo("body");
    }

</script>
</body>
</html>

7. View 디자인 예시..

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
    <script src="https://code.jquery.com/jquery-1.11.3.js"></script>
    <title>Document</title>
    <style>
        * {
            border : 0;
            padding : 0;
        }

        ul {
            border:  1px solid rgb(235,236,239);
            border-bottom : 0;
        }

        li {
            background-color: #f9f9fa;
            list-style-type: none;
            border-bottom : 1px solid rgb(235,236,239);
            padding : 18px 18px 0 18px;
        }

        #commentList {
            width : 50%;
            margin : auto;
        }

        .comment-content {
            overflow-wrap: break-word;
        }

        .comment-bottom {
            font-size:9pt;
            color : rgb(97,97,97);
            padding: 8px 0 8px 0;
        }

        .comment-bottom > a {
            color : rgb(97,97,97);
            text-decoration: none;
            margin : 0 6px 0 0;
        }

        .comment-area {
            padding : 0 0 0 46px;
        }

        .commenter {
            font-size:12pt;
            font-weight:bold;
        }

        .commenter-writebox {
            padding : 15px 20px 20px 20px;
        }

        .comment-img {
            font-size:36px;
            position: absolute;
        }

        .comment-item {
            position:relative;
        }

        .up_date {
            margin : 0 8px 0 0;
        }

        #comment-writebox {
            background-color: white;
            border : 1px solid #e5e5e5;
            border-radius: 5px;
        }

        textarea {
            display: block;
            width: 100%;
            min-height: 17px;
            padding: 0 20px;
            border: 0;
            outline: 0;
            font-size: 13px;
            resize: none;
            box-sizing: border-box;
            background: transparent;
            overflow-wrap: break-word;
            overflow-x: hidden;
            overflow-y: auto;
        }

        #comment-writebox-bottom {
            padding : 3px 10px 10px 10px;
            min-height : 35px;
        }

        .btn {
            font-size:10pt;
            color : black;
            background-color: #eff0f2;
            text-decoration: none;
            padding : 9px 10px 9px 10px;
            border-radius: 5px;
            float : right;
        }

        #btn-write-comment, #btn-write-reply {
            color : #009f47;
            background-color: #e0f8eb;
        }

        #btn-cancel-reply {
            background-color: #eff0f2;
            margin-right : 10px;
        }

        #btn-write-modify {
            color : #009f47;
            background-color: #e0f8eb;
        }

        #btn-cancel-modify {
            margin-right : 10px;
        }

        #reply-writebox {
            display : none;
            background-color: white;
            border : 1px solid #e5e5e5;
            border-radius: 5px;
            margin : 10px;
        }

        #reply-writebox-bottom {
            padding : 3px 10px 10px 10px;
            min-height : 35px;
        }

        #modify-writebox {
            background-color: white;
            border : 1px solid #e5e5e5;
            border-radius: 5px;
            margin : 10px;
        }

        #modify-writebox-bottom {
            padding : 3px 10px 10px 10px;
            min-height : 35px;
        }

        /* The Modal (background) */
        .modal {
            display: none; /* Hidden by default */
            position: fixed; /* Stay in place */
            z-index: 1; /* Sit on top */
            padding-top: 100px; /* Location of the box */
            left: 0;
            top: 0;
            width: 100%; /* Full width */
            height: 100%; /* Full height */
            overflow: auto; /* Enable scroll if needed */
            background-color: rgb(0,0,0); /* Fallback color */
            background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
        }

        /* Modal Content */
        .modal-content {
            background-color: #fefefe;
            margin: auto;
            padding: 20px;
            border: 1px solid #888;
            width: 50%;
        }

        /* The Close Button */
        .close {
            color: #aaaaaa;
            float: right;
            font-size: 28px;
            font-weight: bold;
        }

        .close:hover,
        .close:focus {
            color: #000;
            text-decoration: none;
            cursor: pointer;
        }



        .paging {
            color: black;
            width: 100%;
            text-align: center;
        }

        .page {
            color: black;
            text-decoration: none;
            padding: 6px;
            margin-right: 10px;
        }

        .paging-active {
            background-color: rgb(216, 216, 216);
            border-radius: 5px;
            color: rgb(24, 24, 24);
        }

        .paging-container {
            width:100%;
            height: 70px;
            margin-top: 50px;
            margin : auto;
        }
    </style>
</head>
<body>
<div id="commentList">
    <ul>
        <li class="comment-item" data-cno="1" data-bno="1070">
                <span class="comment-img">
                    <i class="fa fa-user-circle" aria-hidden="true"></i>
                </span>
            <div class="comment-area">
                <div class="commenter">asdf</div>
                <div class="comment-content">asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdfasdf
                </div>
                <div class="comment-bottom">
                    <span class="up_date">2022.01.01 23:59:59</span>
                    <a href="#" class="btn-write"  data-cno="1" data-bno="1070" data-pcno="">답글쓰기</a>
                    <a href="#" class="btn-modify" data-cno="1" data-bno="1070" data-pcno="">수정</a>
                    <a href="#" class="btn-delete" data-cno="1" data-bno="1070" data-pcno="">삭제</a>
                </div>
            </div>
        </li>
        <li class="comment-item" data-cno="2" data-bno="1070">
                <span class="comment-img">
                    <i class="fa fa-user-circle" aria-hidden="true"></i>
                </span>
            <div class="comment-area">
                <div class="commenter">qwer</div>
                <div class="comment-content">qwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwerqwer
                </div>
                <div class="comment-bottom">
                    <span class="up_date">2022.01.01 23:59:59</span>
                    <a href="#" class="btn-write"  data-cno="2" data-bno="1070" data-pcno="">답글쓰기</a>
                    <a href="#" class="btn-modify" data-cno="2" data-bno="1070" data-pcno="">수정</a>
                    <a href="#" class="btn-delete" data-cno="2" data-bno="1070" data-pcno="">삭제</a>
                </div>
            </div>
        </li>
    </ul>
    <div class="paging-container">
        <div class="paging">
            <a class="page" href="#">&lt;</a>
            <a class="page" href="#">1</a>
            <a class="page" href="#">2</a>
            <a class="page" href="#">3</a>
            <a class="page" href="#">4</a>
            <a class="page paging-active" href="#">5</a>
            <a class="page" href="#">6</a>
            <a class="page" href="#">7</a>
            <a class="page" href="#">8</a>
            <a class="page" href="#">9</a>
            <a class="page" href="#">10</a>
            <a class="page" href="#">&gt;</a>
        </div>
    </div>
    <div id="comment-writebox">
        <div class="commenter commenter-writebox">${id}</div>
        <div class="comment-writebox-content">
            <textarea name="" id="" cols="30" rows="3" placeholder="댓글을 남겨보세요"></textarea>
        </div>
        <div id="comment-writebox-bottom">
            <div class="register-box">
                <a href="#" class="btn" id="btn-write-comment">등록</a>
            </div>
        </div>
    </div>
</div>
<div id="reply-writebox">
    <div class="commenter commenter-writebox">${id}</div>
    <div class="reply-writebox-content">
        <textarea name="" id="" cols="30" rows="3" placeholder="댓글을 남겨보세요"></textarea>
    </div>
    <div id="reply-writebox-bottom">
        <div class="register-box">
            <a href="#" class="btn" id="btn-write-reply">등록</a>
            <a href="#" class="btn" id="btn-cancel-reply">취소</a>
        </div>
    </div>
</div>
<div id="modalWin" class="modal">
    <!-- Modal content -->
    <div class="modal-content">
        <span class="close">&times;</span>
        <p>
        <h2> | 댓글 수정</h2>
        <div id="modify-writebox">
            <div class="commenter commenter-writebox"></div>
            <div class="modify-writebox-content">
                <textarea name="" id="" cols="30" rows="5" placeholder="댓글을 남겨보세요"></textarea>
            </div>
            <div id="modify-writebox-bottom">
                <div class="register-box">
                    <a href="#" class="btn" id="btn-write-modify">등록</a>
                </div>
            </div>
        </div>
        </p>
    </div>
</div>
<script>
    let id = 'asdf';

    let addZero = function(value=1){
        return value > 9 ? value : "0"+value;
    }

    let dateToString = function(ms=0) {
        let date = new Date(ms);

        let yyyy = date.getFullYear();
        let mm = addZero(date.getMonth() + 1);
        let dd = addZero(date.getDate());

        let HH = addZero(date.getHours());
        let MM = addZero(date.getMinutes());
        let ss = addZero(date.getSeconds());

        return yyyy+"."+mm+"."+dd+ " " + HH + ":" + MM + ":" + ss;
    }

    $(document).ready(function(){
        $("a.btn-write").click(function(e){
            let target = e.target;
            let cno = target.getAttribute("data-cno")
            let bno = target.getAttribute("data-bno")

            let repForm = $("#reply-writebox");
            repForm.appendTo($("li[data-cno="+cno+"]"));
            repForm.css("display", "block");
            repForm.attr("data-pcno", pcno);
            repForm.attr("data-bno",  bno);
        });

        $("#btn-cancel-reply").click(function(e){
            $("#reply-writebox").css("display", "none");
        });

        $("a.btn-modify").click(function(e){
            let target = e.target;
            let cno = target.getAttribute("data-cno");
            let bno = target.getAttribute("data-bno");
            let pcno = target.getAttribute("data-pcno");
            let li = $("li[data-cno="+cno+"]");
            let commenter = $(".commenter", li).first().text();
            let comment = $(".comment-content", li).first().text();

            $("#modalWin .commenter").text(commenter);
            $("#modalWin textarea").text(comment);
            $("#btn-write-modify").attr("data-cno", cno);
            $("#btn-write-modify").attr("data-pcno", pcno);
            $("#btn-write-modify").attr("data-bno", bno);

            // 팝업창을 열고 내용을 보여준다.
            $("#modalWin").css("display","block");
        });

        $("a.btn-delete").click(function(e){
            alert("delete");
        });

        $("#btn-write-modify").click(function(){
            // 1. 변경된 내용을 서버로 전송
            // 2. 모달 창을 닫는다.
            $(".close").trigger("click");
        });

        $(".close").click(function(){
            $("#modalWin").css("display","none");
        });
    });
</script>
</body>
</html>

 

728x90

+ Recent posts