울러리

스킨 댓글창 코어파일 수정없이 댓글 삭제 표시 및 기타 기능 추가

· 2026-04-14 (화) 16:20:00 · 2564 · 3
스크린샷 2026-04-14 161801.png

코어파일 수정이나 db불러오는 거 없이,,

순전히 댓글의 들여쓰기 상태로 파악하도록 했습니다.

대댓글에는 원댓글 작성자명이 '@원댓글작성자명' 이렇게 표시됩니다.

게시판 본문글을 작성한 사람의 댓글은 배경색이 나오게 했습니다.

삭제된 댓글에 달린 대댓글이 없는 경우는 삭제 표시없이 그냥 없어집니다. 

(이 부분이 제 용도에는 맞는데, 다른 분은 어떨지 모르겠네요.)

테스트를 많이 거친게 아니라 오류가 있을수 있으니 원본은 꼭 백업해 두시고,

오류가 있으면 말씀해주시고, 감당이 안되는 오류가 있으면 이 글을 삭제 하겠습니다.

그리고 제 홈페이지 용도에 맞춘거라 디자인은 다소 이상할 거 같습니다.

view_comment.skin.php 파일 내용 ---------------------------

<?php
if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가
?>

<style>
/* ── 댓글 토글 버튼 ── */
.cmt_btn {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    background: none;
    border: none;
    padding: 4px 0;
    font-size: 0.82em;
    cursor: pointer;
    color: inherit;
}
.cmt_btn .cmt_more::before {
    content: '▾';
    display: inline-block;
    transition: transform 0.2s;
}
.cmt_btn.cmt_btn_op .cmt_more::before {
    transform: rotate(180deg);
}

/* ── 댓글 목록 섹션 ── */
#bo_vc {
    border-top: 1px solid rgba(128,128,128,0.25);
    opacity: 1;
    padding-top: 10px;
    margin-top: 6px;
}
#bo_vc > h2 { display: none; }

/* ── 댓글 없을 때 ── */
#bo_vc_empty {
    padding: 14px 0;
    text-align: center;
    font-size: 0.82em;
    opacity: 0.4;
}

/* ── 개별 댓글 article (공통 경계선) ── */
#bo_vc article {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    padding: 12px 10px;
    border-bottom: 1px solid rgba(128,128,128,0.18);
    position: relative;
    width: 100%;
    box-sizing: border-box;
}
#bo_vc article:last-of-type { border-bottom: none; }

/* [수정] 원글 작성자 강조 스타일 - 흰색 배경에서도 잘 보이도록 보완 */
#bo_vc article.is_author {
    background-color: rgba(0, 0, 0, 0.03); /* 아주 연한 회색 배경 */
    border: 1px solid rgba(0, 0, 0, 0.05); /* 은은한 테두리 추가 */
    border-radius: 8px;
    margin-top: 4px;
    margin-bottom: 4px;
}

/* ── 삭제된 댓글 가상 표시 스타일 ── */
#bo_vc article.is_deleted_placeholder {
    padding-top: 10px;
    padding-bottom: 10px;
    background: none;
}
.deleted_text {
    display: flex;
    align-items: center;
    gap: 6px;
    color: #e74c3c;
    font-size: 0.82em;
    opacity: 0.8;
}

/* 프로필 이미지 */
#bo_vc .pf_img {
    flex-shrink: 0;
    width: 26px;
    height: 26px;
}
#bo_vc .pf_img img {
    width: 26px;
    height: 26px;
    border-radius: 50%;
    object-fit: cover;
    display: block;
}

/* cm_wrap */
#bo_vc .cm_wrap {
    flex: 1;
    min-width: 0;
}

/* ── 댓글 헤더 ── */
#bo_vc header {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 4px;
    margin-bottom: 3px;
    background: none !important;
    border: none !important;
    padding: 0 !important;
    position: static !important;
}
#bo_vc header > h2 { display: none; }

#bo_vc .bo_vc_hdinfo {
    font-size: 0.75em;
    opacity: 0.42;
    margin-left: auto;
}

/* ── 댓글 본문 ── */
#bo_vc .cmt_contents {
    font-size: 0.88em;
    line-height: 1.6;
    word-break: break-all;
}

.reply_mention {
    color: #3498db;
    font-weight: 600;
    margin-right: 4px;
    font-size: 0.95em;
}

#bo_vc .cmt_contents p { margin: 0; }
#bo_vc .cmt_contents img[alt="비밀글"] {
    width: 11px;
    vertical-align: middle;
    opacity: 0.35;
    margin-right: 2px;
}

/* ── 답변/수정/삭제 옵션 영역 ── */
#bo_vc .bo_vl_opt {
    width: 100%;
    padding-left: 34px; 
    box-sizing: border-box;
}
#bo_vc .bo_vl_opt .btn_cm_opt { display: none !important; }
#bo_vc .bo_vl_opt .bo_vc_act {
    display: flex !important;
    flex-direction: row;
    gap: 10px;
    list-style: none;
    margin: 0;
    padding: 0;
}
#bo_vc .bo_vl_opt .bo_vc_act li { margin: 0; padding: 0; }
#bo_vc .bo_vl_opt .bo_vc_act li a {
    font-size: 0.75em;
    opacity: 0.35;
    text-decoration: none;
    color: inherit;
}
#bo_vc .bo_vl_opt .bo_vc_act li a:hover {
    opacity: 0.8;
    text-decoration: underline;
}

/* ── 댓글 쓰기 aside ── */
aside#bo_vc_w {
    margin-top: 10px;
    border-top: 1px solid rgba(128,128,128,0.25);
    padding-top: 10px;
}
aside#bo_vc_w > h2 { display: none; }

#fviewcomment textarea#wr_content {
    display: block !important;
    width: 100% !important;
    min-height: 68px;
    background: transparent;
    color: inherit;
    border: 1px solid rgba(128,128,128,0.35);
    border-radius: 5px;
    padding: 7px 9px;
    font-size: 0.84em;
    font-family: inherit;
    line-height: 1.55;
    resize: vertical;
    box-sizing: border-box;
    outline: none;
}
#fviewcomment textarea#wr_content:focus {
    border-color: rgba(128,128,128,0.7);
}

#char_cnt {
    float: right;
    font-size: 0.74em;
    font-weight: normal;
    opacity: 0.4;
    margin-bottom: 2px;
}

.bo_vc_w_wr {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 5px;
    margin-top: 5px;
}
.bo_vc_w_info {
    display: flex;
    align-items: center;
    gap: 5px;
    flex-wrap: wrap;
}
.bo_vc_w_info .frm_input {
    background: transparent;
    color: inherit;
    border: 1px solid rgba(128,128,128,0.35);
    border-radius: 4px;
    padding: 4px 7px;
    font-size: 0.8em;
    outline: none;
    width: 88px;
    font-family: inherit;
}
.bo_vc_w_info .frm_input:focus {
    border-color: rgba(128,128,128,0.7);
}

.btn_confirm {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-left: auto;
}
.secret_cm label {
    font-size: 0.76em;
    opacity: 0.5;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 3px;
    color: inherit;
}
.secret_cm input[type="checkbox"] { width: 12px; height: 12px; }
.secret_cm label span { display: none; }

#btn_submit {
    border: 1px solid rgba(128,128,128,0.5);
    border-radius: 4px;
    padding: 4px 13px;
    font-size: 0.8em;
    cursor: pointer;
    background: transparent;
    color: inherit;
    font-family: inherit;
    transition: opacity 0.15s;
}
#btn_submit:hover { opacity: 0.55; }
#btn_submit:disabled { opacity: 0.25; cursor: default; }

.sound_only {
    position: absolute;
    width: 1px; height: 1px;
    overflow: hidden;
    clip: rect(0,0,0,0);
}
</style>

<script>
var char_min = parseInt(<?php echo $comment_min ?>); 
var char_max = parseInt(<?php echo $comment_max ?>); 
</script>

<button type="button" class="cmt_btn"><span class="total"><b>댓글</b> <?php echo $view['wr_comment']; ?></span><span class="cmt_more"></span></button>
<section id="bo_vc">
    <h2>댓글목록</h2>
    <?php
    $cmt_amt = count($list);
    $printed_replies = array(); 

    for ($i=0; $i<$cmt_amt; $i++) {
        $comment_id = $list[$i]['wr_id'];
        $reply_str = $list[$i]['wr_comment_reply'];
        $group_id = $list[$i]['wr_comment']; // 같은 댓글 묶음 번호

        // ── 원댓글 및 단계별 삭제 대응 로직 ──
        $root_exists = false;
        for ($j=0; $j<$cmt_amt; $j++) {
            if ($list[$j]['wr_comment'] == $group_id && !$list[$j]['wr_comment_reply']) {
                $root_exists = true;
                break;
            }
        }

        if (!$root_exists && !in_array($group_id.'_root', $printed_replies)) {
            ?>
            <article class="is_deleted_placeholder" style="padding-left:10px;">
                <div class="deleted_text"><i class="fa fa-times-circle"></i> 삭제된 원본 댓글입니다.</div>
            </article>
            <?php
            $printed_replies[] = $group_id.'_root';
        }

        if ($reply_str) {
            for ($k=1; $k<=strlen($reply_str); $k++) {
                $check_path = substr($reply_str, 0, $k);
                $path_exists = false;
                for ($j=0; $j<$cmt_amt; $j++) {
                    if ($list[$j]['wr_comment_reply'] == $check_path && $list[$j]['wr_comment'] == $group_id) {
                        $path_exists = true;
                        break;
                    }
                }
                if (!$path_exists && !in_array($group_id.'_'.$check_path, $printed_replies)) {
                    $p_depth = (strlen($check_path)) * 20; 
                    ?>
                    <article class="is_deleted_placeholder" style="padding-left:<?php echo $p_depth + 10 ?>px;">
                        <div class="deleted_text"><i class="fa fa-times-circle"></i> 삭제된 대댓글입니다.</div>
                    </article>
                    <?php
                    $printed_replies[] = $group_id.'_'.$check_path;
                }
            }
        }
        
        $printed_replies[] = $group_id.'_'.$reply_str; 

        // ── 본 댓글 출력 ──
        $cmt_depth = strlen($reply_str) * 20; 
        // 게시글 작성자와 댓글 작성자의 mb_id가 일치하는지 확인
        $is_author_class = ($list[$i]['mb_id'] && $list[$i]['mb_id'] === $view['mb_id']) ? 'is_author' : '';

        $reply_name = '';
        if ($reply_str) {
            $parent_reply_path = substr($reply_str, 0, -1);
            for ($j=$i-1; $j>=0; $j--) {
                if ($list[$j]['wr_comment_reply'] == $parent_reply_path && $list[$j]['wr_comment'] == $list[$i]['wr_comment']) {
                    $reply_name = get_text($list[$j]['wr_name']);
                    break;
                }
            }
        }

        $comment = $list[$i]['content'];
        $comment = preg_replace("/\[\<a\s.*href\=\"(http|https|ftp|mms)\:\/\/([^[:space:]]+)\.(mp3|wma|wmv|asf|asx|mpg|mpeg)\".*\<\/a\>\]/i", "<script>doc_write(obj_movie('$1://$2.$3'));<\/script>", $comment);
        $cmt_sv = $cmt_amt - $i + 1;
        $c_reply_href = $comment_common_url.'&c_id='.$comment_id.'&w=c#bo_vc_w';
        $c_edit_href = $comment_common_url.'&c_id='.$comment_id.'&w=cu#bo_vc_w';
        $is_comment_reply_edit = ($list[$i]['is_reply'] || $list[$i]['is_edit'] || $list[$i]['is_del']) ? 1 : 0;
    ?>

    <article id="c_<?php echo $comment_id ?>" class="<?php echo $is_author_class; ?>" style="padding-left:<?php echo $cmt_depth + 10 ?>px;">
        <div class="pf_img"><?php echo get_member_profile_img($list[$i]['mb_id']); ?></div>
        
        <div class="cm_wrap">
            <header style="z-index:<?php echo $cmt_sv; ?>">
                <h2><?php echo get_text($list[$i]['wr_name']); ?>님의 댓글</h2>
                <?php echo $list[$i]['name'] ?>
                <?php if ($is_ip_view) { ?>
                <span>(<?php echo $list[$i]['ip']; ?>)</span>
                <?php } ?>
                <span class="bo_vc_hdinfo"><i class="fa fa-clock-o"></i> <time datetime="<?php echo date('Y-m-d\TH:i:s+09:00', strtotime($list[$i]['datetime'])) ?>"><?php echo $list[$i]['datetime'] ?></time></span>
            </header>

            <div class="cmt_contents">
                <p>
                    <?php if (strstr($list[$i]['wr_option'], "secret")) { ?><img src="<?php echo $board_skin_url; ?>/img/icon_secret.gif" alt="비밀글"><?php } ?>
                    <?php if ($reply_name) { ?>
                        <span class="reply_mention">@<?php echo $reply_name; ?></span>
                    <?php } ?>
                    <?php echo $comment ?>
                </p>
            </div>
            <span id="edit_<?php echo $comment_id ?>" class="bo_vc_w"></span><span id="reply_<?php echo $comment_id ?>" class="bo_vc_w"></span>
            <input type="hidden" value="<?php echo strstr($list[$i]['wr_option'],"secret") ?>" id="secret_comment_<?php echo $comment_id ?>">
            <textarea id="save_comment_<?php echo $comment_id ?>" style="display:none"><?php echo get_text($list[$i]['content1'], 0) ?></textarea>
        </div>
        <?php if($is_comment_reply_edit) { ?>
        <div class="bo_vl_opt">
            <ul class="bo_vc_act">
                <?php if ($list[$i]['is_reply']) { ?><li><a href="<?php echo $c_reply_href; ?>" onclick="comment_box('<?php echo $comment_id ?>', 'c'); return false;">답변</a></li><?php } ?>
                <?php if ($list[$i]['is_edit']) { ?><li><a href="<?php echo $c_edit_href; ?>" onclick="comment_box('<?php echo $comment_id ?>', 'cu'); return false;">수정</a></li><?php } ?>
                <?php if ($list[$i]['is_del']) { ?><li><a href="<?php echo $list[$i]['del_link']; ?>" onclick="return comment_delete();">삭제</a></li><?php } ?>
            </ul>
        </div>
        <?php } ?>
    </article>
    <?php } ?>
    <?php if ($i == 0) { ?><p id="bo_vc_empty">등록된 댓글이 없습니다.</p><?php } ?>
</section>

<?php if ($is_comment_write) {
    if($w == '') $w = 'c';
?>
<aside id="bo_vc_w" class="bo_vc_w">
    <h2>댓글쓰기</h2>
    <form name="fviewcomment" id="fviewcomment" action="<?php echo $comment_action_url; ?>" onsubmit="return fviewcomment_submit(this);" method="post" autocomplete="off">
    <input type="hidden" name="w" value="<?php echo $w ?>" id="w">
    <input type="hidden" name="bo_table" value="<?php echo $bo_table ?>">
    <input type="hidden" name="wr_id" value="<?php echo $wr_id ?>">
    <input type="hidden" name="comment_id" value="<?php echo $c_id ?>" id="comment_id">
    <input type="hidden" name="sca" value="<?php echo $sca ?>">
    <input type="hidden" name="sfl" value="<?php echo $sfl ?>">
    <input type="hidden" name="stx" value="<?php echo $stx ?>">
    <input type="hidden" name="spt" value="<?php echo $spt ?>">
    <input type="hidden" name="page" value="<?php echo $page ?>">
    <input type="hidden" name="is_good" value="">

    <?php if ($comment_min || $comment_max) { ?><strong id="char_cnt"><span id="char_count"></span>글자</strong><?php } ?>
    <textarea id="wr_content" name="wr_content" maxlength="10000" required class="required" title="내용" placeholder="댓글내용을 입력해주세요" 
    <?php if ($comment_min || $comment_max) { ?>onkeyup="check_byte('wr_content', 'char_count');"<?php } ?>><?php echo $c_wr_content; ?></textarea>
    
    <div class="bo_vc_w_wr">
        <div class="bo_vc_w_info">
            <?php if ($is_guest) { ?>
            <input type="text" name="wr_name" value="<?php echo get_cookie("ck_sns_name"); ?>" id="wr_name" required class="frm_input required" size="25" placeholder="이름">
            <input type="password" name="wr_password" id="wr_password" required class="frm_input required" size="25" placeholder="비밀번호">
            <?php } ?>
        </div>
        <div class="btn_confirm">
            <span class="secret_cm chk_box">
                <input type="checkbox" name="wr_secret" value="secret" id="wr_secret" class="selec_chk">
                <label for="wr_secret"><span></span>비밀글</label>
            </span>
            <button type="submit" id="btn_submit" class="btn_submit">댓글등록</button>
        </div>
    </div>
    </form>
</aside>

<script>
var save_before = '';
var save_html = document.getElementById('bo_vc_w').innerHTML;

function fviewcomment_submit(f)
{
    var pattern = /(^\s*)|(\s*$)/g;
    f.is_good.value = 0;
    var content = "";
    $.ajax({
        url: g5_bbs_url+"/ajax.filter.php",
        type: "POST",
        data: { "subject": "", "content": f.wr_content.value },
        dataType: "json",
        async: false,
        cache: false,
        success: function(data) { content = data.content; }
    });

    if (content) {
        alert("내용에 금지단어('"+content+"')가 포함되어있습니다");
        f.wr_content.focus();
        return false;
    }

    f.wr_content.value = f.wr_content.value.replace(pattern, "");
    if (!f.wr_content.value) { alert("댓글을 입력하여 주십시오."); return false; }

    <?php if($is_guest) echo chk_captcha_js(); ?>
    set_comment_token(f);
    document.getElementById("btn_submit").disabled = "disabled";
    return true;
}

function comment_box(comment_id, work)
{
    var el_id, form_el = 'fviewcomment', respond = document.getElementById(form_el);
    if (comment_id) {
        if (work == 'c') el_id = 'reply_' + comment_id;
        else el_id = 'edit_' + comment_id;
    } else el_id = 'bo_vc_w';

    if (save_before != el_id) {
        if (save_before) document.getElementById(save_before).style.display = 'none';
        document.getElementById(el_id).style.display = '';
        document.getElementById(el_id).appendChild(respond);
        document.getElementById('wr_content').value = '';
        
        if (work == 'cu') {
            document.getElementById('wr_content').value = document.getElementById('save_comment_' + comment_id).value;
            if (document.getElementById('secret_comment_'+comment_id).value) document.getElementById('wr_secret').checked = true;
            else document.getElementById('wr_secret').checked = false;
        }

        document.getElementById('comment_id').value = comment_id;
        document.getElementById('w').value = work;
        save_before = el_id;
    }
}

function comment_delete() { return confirm("이 댓글을 삭제하시겠습니까?"); }
comment_box('', 'c');

jQuery(function($) {
    $(".cmt_btn").click(function(e){
        e.preventDefault();
        $(this).toggleClass("cmt_btn_op");
        $("#bo_vc").toggle();
    });
});
</script>
<?php } ?>

첨부파일

총 3명이 반응했습니다
|

댓글 3개

수고 하셨습니다.
2026-04-22 (수) 08:38:53
감사합니다 ^^
감사합니다.
댓글을 작성하시려면 로그인이 필요합니다.

그누보드5 팁자료실

2,788건
+
제목 글쓴이 날짜 조회
22-06-20 조회 8,488
09-11 조회 428
09-04 조회 1,113
08-09 조회 1,902
08-05 조회 3,110
07-30 조회 1,905
07-30 조회 1,791
07-15 조회 3,081
07-01 조회 3,222
06-30 조회 3,246
06-22 조회 3,983
06-17 조회 3,145
06-14 조회 1,882
05-29 조회 1,960
05-16 조회 1,936
05-04 조회 3,418
04-29 조회 3,598
04-28 조회 3,418
04-27 조회 3,383
04-25 조회 2,385
04-24 조회 4,150
04-23 조회 3,541
04-14 조회 2,565
04-08 조회 3,395
04-01 조회 5,084
03-31 조회 4,108
03-16 조회 4,278
03-14 조회 5,413
03-13 조회 4,454
03-10 조회 4,213
03-09 조회 4,401