본문 바로가기
블로그 이미지

방문해 주셔서 감사합니다! 항상 행복하세요!

  
   - 문의사항은 메일 또는 댓글로 언제든 연락주세요.
   - "해줘","답 내놔" 같은 질문은 답변드리지 않습니다.
   - 메일주소 : lts06069@naver.com


Javascript/[기초] Javascript

Javascript 댓글 기능 제작(자바스크립트 댓글, Javascript comment)

야근없는 행복한 삶을 위해 ~
by 마샤와 곰 2020. 9. 9.

게시판에서 자주 사용되는 기능 중 하나가 바로 "댓글" 입니다.

댓글을 게시글을 참조하여 데이터가 쌓이는 형식 입니다.

아래와 같이 데이터가 존재한다고 가정하겠습니다.

{
	idx : "_NN5i7ztvy8bl1L4wTQoKx9THDhagNV9AIeSQOmlm",
	title : "제목"	
	contents : "안녕하세요. 저는 게시글 입니다.",
	reg_id : "user",
	reg_date : "2020-09-09 08:00:10",
}

 

idx는 고유의 키 값을 의미합니다.

나머지 항목은 일반 정보들을 의미 합니다.

만약 이러한 데이터가 존재하는 데 댓글이 입력되려면 idx값을 key값으로 한 데이터가 생성이 되어야 합니다.

{
	comment_idx : "_NN5i7ztvy8bl1L4wTQoKx9THDhagNV9AIeSQOmlm",
	idx : "고유키",
	title : "제목"	
	contents : "댓글입니다.",
	reg_id : "user",
	reg_date : "2020-09-09 08:00:10",
}

 

comment_idx에 게시글의 고유 키 값을 넣어주었습니다.

이렇게 하면 단순한 게시판에 대한 댓글기능이 완성되었습니다.

여기에 댓글에 대한 댓글의 기능을 추가하려면 키 값이 한개 더 필요합니다.

{
	comment_idx : "_NN5i7ztvy8bl1L4wTQoKx9THDhagNV9AIeSQOmlm",
	idx : "고유키",
	mother_idx : null,    
	title : "제목"	
	contents : "댓글입니다.",
	reg_id : "user1",
	reg_date : "2020-09-09 09:30:00",
},
{
	comment_idx : "_NN5i7ztvy8bl1L4wTQoKx9THDhagNV9AIeSQOmlm",
	idx : "고유키2",
	mother_idx : "고유키",        
	title : "제목2"	
	contents : "위 댓글에 댓글입니다.",
	reg_id : "user2",
	reg_date : "2020-09-09 11:00:10",
}

 

mother_idx값은 댓글에 대한 참조 역할을 수행합니다.

첫번째로 등록한 데이터는 mother_idx가 없습니다.  데이터가 없다는 의미는 본문에 대한 댓글을 의미합니다.

두번째로 등록한 데이터는 mother_idx에 값이 존재 합니다.

mother_idx값은 참조해야되는 댓글을 의미합니다.

 

이제 데이터베이스에서 데이터를 가져왔다고 가정하여 봅니다.

데이터를 가져오려면 단순하게 게시글의 고유 키값을 대상으로 조회해서 전부 가져오면 됩니다.

아래 샘플 데이터를 데이터베이스에서 가져온 댓글 데이터라고 가정하겠습니다.

var comments = [
    {
        comment_idx : "_NN5i7ztvy8bl1L4wTQoKx9THDhagNV9AIeSQOmlm",
        idx : "admin1",
        mother_idx : null,
        reg_id : "admin",
        reg_date : "2020-09-09 10:44:00",
        comment_contents : "문의사항에 대한 답변입니다."
    },
    {
        comment_idx : "_NN5i7ztvy8bl1L4wTQoKx9THDhagNV9AIeSQOmlm",
        idx : "user1",
        mother_idx : "admin1",
        reg_id : "user",
        reg_date : "2020-09-09 10:50:31",
        comment_contents : "재문의 드립니다."
    },
    {
        comment_idx : "_NN5i7ztvy8bl1L4wTQoKx9THDhagNV9AIeSQOmlm",
        idx : "admin2",
        mother_idx : "user1",
        reg_id : "admin",
        reg_date : "2020-09-09 10:55:31",
        comment_contents : "재문의 답변입니다."
    },
    {
        comment_idx : "_NN5i7ztvy8bl1L4wTQoKx9THDhagNV9AIeSQOmlm",
        idx : "user2",
        mother_idx : "admin2",
        reg_id : "user",
        reg_date : "2020-09-09 11:05:02",
        comment_contents : "마지막 문의입니다."
    },
    {
        comment_idx : "_NN5i7ztvy8bl1L4wTQoKx9THDhagNV9AIeSQOmlm",
        idx : "other_user",
        mother_idx : null,
        reg_id : "other",
        reg_date : "2020-09-10 09:25:31",
        comment_contents : "다른사람 문의입니다.."
    },
    {
        comment_idx : "_NN5i7ztvy8bl1L4wTQoKx9THDhagNV9AIeSQOmlm",
        idx : "other_admin",
        mother_idx : "other_user",
        reg_id : "admin",
        reg_date : "2020-09-10 12:48:28",
        comment_contents : "다른사람 댓글 입니다."
    },
    {
        comment_idx : "_NN5i7ztvy8bl1L4wTQoKx9THDhagNV9AIeSQOmlm",
        idx : "just_one",
        mother_idx : null,
        reg_id : "justman",
        reg_date : "2020-09-10 13:48:28",
        comment_contents : "한개만 있는 댓글 입니다."
    }        
];

 

순수 배열형태의 모양의 데이터 입니다.

위 데이터에서 가장 먼저 해야될 작업은 게시글에 대한 댓글, 댓글에 대한 댓글이 아니라 게시글에 대한 댓글을 분리하는 것 입니다.

var conv_comment = [];
comments = comments.filter(function(data){
    if(data.mother_idx == null){  //해당 값이 없는 것이 게시글에 대한 댓글입니다.
        conv_comment.push({
            comment_idx : data.comment_idx,
            idx : data.idx,
            mother_idx : null,
            reg_id : data.reg_id,
            reg_date : data.reg_date,
            comment_contents : data.comment_contents
        });
        return false;
    }
    return true;
});

 

이렇게 하고나면 이제 게시글에 대한 댓글을 분리하였습니다.

남은 작업은 댓글에 대한 댓글을 담아주는 것 입니다.

conv_comment = conv_comment.map(function(data){
    var array = [];  //댓글에 대한 댓글을 담을 배열
    var working = true;
    var mother_idx = data.idx;
    while(working){
        var isok = false;
        for(var i=0;i<comments.length;i++){
            var mini = comments[i];
            if(mother_idx == mini.mother_idx){  //고유키값이 참조키와 같으면
                array.push(mini);  //데이터를 넣습니다.
                mother_idx = mini.idx;  //고유키를 바꾸고
                isok = true;
                break; //반복문을 중단하여 줍니다.
            }            
        }
        if(!isok){  //더이상 존재하지 않다면
            working = false;  //while을 중단합니다.
        }
    }
    data.array = array;
    return data;
});

 

게시글의 고유 값을 가지고 데이터베이스에서 가져온 데이터에서 댓글참조키과 같은 값이 있는지 확인 합니다.

위 반복문 처럼 고유키값을 바꾸어주면서 확인을 하여줍니다.

이렇게 데이터를 정리하면 아래 모습처럼 데이터가 정렬된 것을 볼 수 있습니다.

총 3개의 데이터, 댓글의 댓글 데이터는 array안에 들어가 있습니다.

 

이제 남은 것은 배열값을 토대로 반복을 동작시켜 테그를 그려내는 일만 남았습니다.

간단하게 Jquery를 통해서 대상을 그려보도록 하겠습니다.

* 앵귤러, 리엑트를 통해서 그리면 참 편할것 입니다.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>board</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <script src = "https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">    
</head>

<body style='height: 100%;width: 100%;' id='hello'>

    <div class='container'>
        <br>
        <input type="text" value="게시글" class='form-control'>
        <br>
        <div class='form-control' style="height: 100px;">
            <div>안녕하세요</div>    
            <div>반갑습니다.</div>    
            <div>저는 게시글 입니다.</div>    
        </div>
        <div id='comment_area'></div>
    </div>

</body>
</html>

<script>    
var comments = [
    {
        comment_idx : "_NN5i7ztvy8bl1L4wTQoKx9THDhagNV9AIeSQOmlm",
        idx : "admin1",
        mother_idx : null,
        reg_id : "admin",
        reg_date : "2020-09-09 10:44:00",
        comment_contents : "문의사항에 대한 답변입니다."
    },
    {
        comment_idx : "_NN5i7ztvy8bl1L4wTQoKx9THDhagNV9AIeSQOmlm",
        idx : "user1",
        mother_idx : "admin1",
        reg_id : "user",
        reg_date : "2020-09-09 10:50:31",
        comment_contents : "재문의 드립니다."
    },
    {
        comment_idx : "_NN5i7ztvy8bl1L4wTQoKx9THDhagNV9AIeSQOmlm",
        idx : "admin2",
        mother_idx : "user1",
        reg_id : "admin",
        reg_date : "2020-09-09 10:55:31",
        comment_contents : "재문의 답변입니다."
    },
    {
        comment_idx : "_NN5i7ztvy8bl1L4wTQoKx9THDhagNV9AIeSQOmlm",
        idx : "user2",
        mother_idx : "admin2",
        reg_id : "user",
        reg_date : "2020-09-09 11:05:02",
        comment_contents : "마지막 문의입니다."
    },  
    {
        comment_idx : "_NN5i7ztvy8bl1L4wTQoKx9THDhagNV9AIeSQOmlm",
        idx : "other_user",
        mother_idx : null,
        reg_id : "other",
        reg_date : "2020-09-10 09:25:31",
        comment_contents : "다른사람 문의입니다.."
    },
    {
        comment_idx : "_NN5i7ztvy8bl1L4wTQoKx9THDhagNV9AIeSQOmlm",
        idx : "other_admin",
        mother_idx : "other_user",
        reg_id : "admin",
        reg_date : "2020-09-10 12:48:28",
        comment_contents : "다른사람 댓글 입니다."
    },
    {
        comment_idx : "_NN5i7ztvy8bl1L4wTQoKx9THDhagNV9AIeSQOmlm",
        idx : "just_one",
        mother_idx : null,
        reg_id : "justman",
        reg_date : "2020-09-10 13:48:28",
        comment_contents : "한개만 있는 댓글 입니다."
    }        
];

var conv_comment = [];
var backup_comments = JSON.parse(JSON.stringify( comments )); 
comments = comments.filter(function(data){
    if(data.mother_idx == null){
        conv_comment.push({
            comment_idx : data.comment_idx,
            idx : data.idx,
            mother_idx : null,
            reg_id : data.reg_id,
            reg_date : data.reg_date,
            comment_contents : data.comment_contents
        });
        return false;
    }
    return true;
});

conv_comment = conv_comment.map(function(data){
    var array = [];
    var working = true;
    var mother_idx = data.idx;
    while(working){
        var isok = false;
        for(var i=0;i<comments.length;i++){
            var mini = comments[i];
            if(mother_idx == mini.mother_idx){
                array.push(mini);
                mother_idx = mini.idx;
                isok = true;
                break;
            }            
        }
        if(!isok){
            working = false;
        }
    }
    data.array = array;
    return data;
});

//댓글 입력창 틀을 만듭니다.
function elementForm(className, target, arg, uniqueId, option){
    target.append(
        $('<div>').attr('id',uniqueId != null ? uniqueId : '').addClass(className).append(
            $('<input type="text">').addClass('form-control mini').val(arg != null ? arg.idx : ''),
            $('<input type="text">').addClass('form-control mini').val(arg != null ? arg.reg_date : ''),
            $('<input type="text">').addClass('form-control').val(arg != null ? arg.comment_contents : ''),
            option
        )
    );    
}

//댓글 더하기 버튼을 만듭니다.
function addButton(uniqueId, className){
    $('#'+uniqueId).append(
        $('<div>').addClass(className).attr('show_type','none').text('#댓글 입력하기').click(function(){
            var type = $(this).attr('show_type');
            if(type == 'none'){
                $(this).attr('show_type','show');
                $(this).next().show();
                $(this).text('#취소');
            } else {
                $(this).attr('show_type','none');
                $(this).next().hide();
                $(this).text('#댓글 입력하기');
            }
        })
    );
}


conv_comment.forEach(function(arg, index){
    var uniqueId = index + "_comment_id";
    var parent_id_for_save = arg.idx;

    elementForm( 'mother_class', $('#comment_area') , arg , uniqueId );
    if(arg.array.length > 0){  //댓글이 존재하면
        arg.array.forEach(function(mini, idx){
            elementForm( 'child_class', $('#'+uniqueId) , mini , null);
            if(idx == arg.array.length-1){  //마지막이면
                addButton(uniqueId , 'child_class');
                parent_id_for_save = mini.idx;
            }
        });
    } else {
        addButton(uniqueId , 'child_class');          
    }
    elementForm( 'child_class default_hide', $('#'+uniqueId) , null , null, 
        $('<input type="button">').addClass('btn btn-success').val('등록').click(function(){
            console.log('부모 아이디 : ', parent_id_for_save);
        })        
     );
});

addButton('comment_area', 'mother_class');
elementForm( 'mother_class default_hide', $('#comment_area') , null , null, 
    $('<input type="button">').addClass('btn btn-success').val('등록').click(function(){
        console.log('부모 아이디', '부모아이디는 없는 상태로 저장되어야 합니다.');
    })        
);

</script>
    
<style>
    .mini{
        width : 30%;
        display: inline-block;
    }
    .mother_class{
        margin-top : 3%;
        padding-left: 2%;
        padding-right: 2%;
    }
    .child_class{
        margin-top : 1%;
        padding-left: 10%;
        padding-right: 10%;        
    }
    .default_hide{
        display: none;
    }
</style>

 

위 코드를 통해서 실제 구현한 모습입니다.

 

댓글 입력하기에서는 바로 위의 부모키 값을 댓글의 참조키 값으로 저장하면 될 것 입니다.

이상으로 자바스크립트에서 댓글과 관련된 기능을 제작하는 방법에 대해서 살펴 보았습니다.

 

반응형
* 위 에니메이션은 Html의 캔버스(canvas)기반으로 동작하는 기능 입니다. Html 캔버스 튜토리얼 도 한번 살펴보세요~ :)
* 직접 만든 Html 캔버스 애니메이션 도 한번 살펴보세요~ :)

댓글