Post

[미니프로젝트] 파이어베이스를 이용한 댓글 기능 샘플 제작

🚀 JS 코드


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// Firebase SDK 라이브러리 가져오기
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-app.js";
import {
    getFirestore,
    collection,
    doc,
    addDoc,
    query,
    where,
    orderBy,
    getDocs,
    setDoc,
    serverTimestamp,
} from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";


// Firebase 구성 정보 설정
const firebaseConfig = {
    ...
};


// Firebase 인스턴스 초기화
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);



// 파라미터 가져오기
// 현재는 임시로 파라미터로 처리
// 추후 개인 게시물의 키값으로 변경예정
function getQueryParam(key) {
    const params = new URLSearchParams(window.location.search);
    return params.get(key);
}

const key = getQueryParam('key');
const targetId = key || "main";

console.log("targetId: ", targetId);



// 댓글 등록
export async function registerComment(commentText) {
    const docData = {
        targetId,
        comment: commentText,
        createdAt: serverTimestamp()
    };
    
    const docRef = await addDoc(collection(db, "comments"), docData);

    // 생성된 문서 ID
    const newId = docRef.id;

    // ID 필드 추가
    await setDoc(doc(db, "comments", newId), {
        id: newId,
        ...docData
    })
}


// 댓글 가져오기
export async function loadComments(callback) {
    const q = query(
        collection(db, "comments"),
        where("targetId", "==", targetId),
        orderBy("createdAt", "asc")
    );

    const querySnapshot = await getDocs(q);
    querySnapshot.forEach(doc => {
        callback(doc.data());
    });
}


// Input Enter Event
$('#commentInput').keypress(function (e) {
    if (e.which === 13) { // 13은 Enter 키 코드
        e.preventDefault();
        $('#commentButton').click();
    }
});

🚀 HTML 코드


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<head>
    <script type="module">
        import { registerComment, loadComments } from '../js/comments.js';

        const $commentList = $('#commentList');
        const $commentButton = $('#commentButton');

        // 등록 버튼
        $commentButton.click(async function () {
            const comment = $('#commentInput').val();

            await registerComment(comment);

            window.location.reload();
        });

        // 댓글 가져오기
        if ($commentList.length > 0) {
            $commentList.empty();

            await loadComments((row) => {
                const id = row.id || "";
                const comment = row.comment;
                
                const tempHtml = `<li data-id="${id}">${comment}</li>`;
                $commentList.append(tempHtml);
            });

            // 댓글의 스크롤 위치를 최하단으로 이동
            $commentList.scrollTop($commentList.prop("scrollHeight"))
        }
    </script>
</head>

<body>
    <!-- 샘플이기에 일단 키 값은 URL 쿼리 파라미터로 대체 -->
    <a href="?key=">메인</a>
    <a href="?key=A">A</a>
    <a href="?key=B">B</a>

    <ul id="commentList">
        <li>1</li>
        <li>2</li>
        <li>3</li>
    </ul>

    <div id="commentInputGroup">
        <input type="text" name="" id="commentInput" placeholder="댓글을 입력해주세요.">
        <button type="button" id="commentButton"><img src="../images/icon_send.svg"></button>
    </div>
</body>

🚀 CSS 코드


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#commentList {
    list-style: none;
    padding: 0;
    margin: 0;

    display: flex;
    flex-direction: column;
    align-items: flex-end;

    max-height: 400px;

    overflow: auto;
}

#commentList > li {
    position: relative;

    width: max-content;
    padding: 5px 15px;
    margin: 5px 10px 5px 0;

    background-color: #e5e5e5;
    border-radius: 12px;

    font-size: 16px;
    line-height: 24px;
}

#commentList > li::before {
    content: "";

    position: absolute;
    right: 0;
    bottom: 2px;
    transform: translateX(50%) rotate(10deg);

    width: auto;
    height: auto;

    /* background-color: red; */

    border: 10px solid transparent;
    border-left-color: #e5e5e5;
    border-right: 0;
    border-bottom: 0;
    
    box-sizing: border-box;
}

#commentInputGroup {
    display: flex;

    padding: 0 10px;
    margin: 10px 0;
    border: 1px solid #dedede;
    border-radius: 1000px;
}

#commentInput {
    border: 0;
    background: none;
    outline: none;

    width: 100%;
    height: 40px;
    padding: 0 10px;

    font-size: 16px;
}

#commentButton {
    flex-shrink: 0;
    
    border: 0;
    background: none;
    padding: 0;
    margin: 0;

    width: 40px;
    height: 40px;

    cursor: pointer;
}

#commentButton img {
    width: 50%;
    height: 50%;
    object-fit: contain;
}

🚀 Firebase 구조


Image

  • content: 댓글의 내용
  • createdAt: 등록일자
  • id: 댓글의 유니크키 값
  • targetId: 댓글이 출력될 대상의 유니크키 값

🚀 결과


B 링크를 눌렀을 때 출력되는 댓글들

Image