Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
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
Tags
more
Archives
Today
Total
관리 메뉴

이지은님의 블로그

자기소개 페이지 - Firebase를 활용한 실시간 방명록 기능 구현 및 관리(CRUD) 본문

프로젝트

자기소개 페이지 - Firebase를 활용한 실시간 방명록 기능 구현 및 관리(CRUD)

queenriwon3 2024. 12. 26. 15:58

프로젝트 "우리 팀을 소개해요"

프로젝트 명 : 우리 팀을 소개해요.

개발 기간 : 2024.12.23 ~ 2024.12.27 (5일)

프로젝트 소개 : 팀원을 소개하는 웹사이트입니다.

프로젝트 목표 : 프로젝트를 함께 만드는 팀원에 대해 소개합니다.

 

github : https://github.com/Sparta-1Team/IntroduceTeam1

ppt : https://www.canva.com/design/DAGaWUhm2SU/fB2soVbGQ41-DNIKaICY-Q/view?utm_content=DAGaWUhm2SU&utm_campaign=designshare&utm_medium=link2&utm_source=uniquelinks&utlId=h2fd2e92a29

distribution page : https://sparta-1team.github.io/IntroduceTeam1/

 

개발 환경

  • environment : Visual Studio Code, git, github
  • development : HTML5, CSS3, Javascript, jQuery3.3.1, bootstrap5.0.2
  • DB : Firebase10.7.1
  • communication : figma, slack, notion, zep, google docs

 


기능 담당

기능 역할담당
메인 큰 틀, 팀 프로필 카드, 헤더 메뉴 문성준,조은종
팀원 모달창 문성준
방명록 및 firebase연동 이지은(해당내용기록)
개인페이지 상세 최다원
개인페이지 등록 및 연결 송윤정

 


 

 

차례

  1. 방명록 레이아웃 구현
  2. 방명록 firebase연동
  3. 방명록 작성(firebase 데이터 쓰기) - 날짜추가, 데이터베이스 구성
  4. 방명록 업데이트(firebase 데이터 읽기)
  5. 방명록 삭제(firebase 데이터 삭제) (수정)
  6. 방명록 수정(firebase 데이터 업데이트) (수정)
  7. 앞으로, 아쉬운점, 소감

 

 

1. 방명록 레이아웃 구현

//html 부분
<div class="guestbook-main">
    <div class="guestbook-posting">
        <div class="guestbook-postingbox" id="postingbox">
            <div class="form-floating mb-3">
                <input type="text" class="form-control" id="nickname" placeholder="닉네임">
                <label for="nickname">닉네임</label>
            </div>
            <div class="form-floating mb-3">
                <input type="text" class="form-control" id="content" placeholder="방명록">
                <label for="content">방명록</label>
            </div>
            <div class="guestbook-passbtnbox">
                <div class="col-auto">
                    <span id="passwordHelpInline" class="form-text">(선택)비밀번호(최대8자)
                    </span>
                </div>
                <div class="col-auto">
                    <input type="password" id="password" class="form-control" maxlength="8" aria-describedby="passwordHelpInline">
                </div>

                <div class="guestbook-postingbtn">
                    <button type="button" class="btn btn-outline-primary guestbook-postingbutton"
                        id="guestbook-postingbutton">작성</button>
                </div>
            </div>
        </div>

    </div>
    <div class="guestbook-list" id="guestbook-list">
        <!-- 추가되는 곳 -->
    </div>

</div>
//css 부분

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.3.1/dist/css/bootstrap.min.css"
    integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
    integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">


<style>
    .guestbook-posting {
        display: flex;
        flex-direction: row;
    }

    .guestbook-postingbox {
        width:100%;
        margin-top: 14px;
        display: flex;
        flex-direction: column;
        margin-bottom: 14px;
    }

    .guestbook-passbtnbox{
        margin-left: auto;
        display: flex;
        flex-direction: row;
        gap: 10px;
        align-items: center;
    }

    .guestbook-postingbtn {
        justify-content: center;
        margin-left: auto;

    }

    .guestbook-postingbutton {
        margin: auto;
        padding: 10px 30px;
        background-color: #4CAF50;
        color: white;
    }

    .guestbook-repleimage {
        height: 60px;
        width: 60px;
        border-radius: 50%;
    }

    .guestbook-list {
        gap: 20px;
    }

    .guestbook-listrow {
        display: flex;
        flex-direction: row;
        background-color: #eeeeee;
        padding: 20px 20px 0px 20px;
        border-radius: 10px;
        margin-bottom: 10px;
    }

    .guestbook-listcolumn {
        display: flex;
        flex-direction: column;
        margin-left: 20px;
        flex-grow: 1;

    }

    .guestbook-listcontent {
        padding-top: 10px;
        padding-left: 5px;
        flex-grow: 1;
        border-top: 2px solid white;

    }

    .guestbook-listcolumn>div {
        display: flex;
        flex-direction: row;
        gap: 10px;
        margin-left: 5px;
    }

    .guestbook-listdatetime {
        font-size: 12px;
        color: #777777;
    }

    .guestbook-deleteBtn {
        font-size: 14px;
        color: gray;
    }

    .guestbook-deleteBtn:hover {
        color: darkred;
        text-decoration: underline;
        outline: none;
    }

    .guestbook-deleteBtn:focus {
        outline: none;
        box-shadow: none;
    }

    .guestbook-correctionBtn {
        font-size: 14px;
        color: gray;
    }

    .guestbook-correctionBtn:hover {
        color: darkred;
        text-decoration: underline;
        outline: none;
    }

    .guestbook-correctionBtn:focus {
        outline: none;
        box-shadow: none;
    }
</style>

 

방명록은 크게 posting부분과 list부분으로 나뉘는데,

posting에서는 닉네임과 방명록, 비밀번호를 작성할 수 있는 곳과 작성후 완료할 수 있는 버튼을 구현했다.

list에서는 일반적인 프로필 사진과 작성한 방명록의 닉네임, 방명록 내용, 방명록을 작성한 날짜와 시간, 삭제 버튼까지 만들었다.

 

구현내용

 

 

 

2. 방명록 firebase연동

import { initializeApp } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-app.js";
import { getFirestore } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";

// Firebase 구성 정보 설정
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
    firebase 구성정보 설정
};

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

 

웹사이트에서 작성한 방명록을 저장하기 위해 firebase와 연동을 하는 코드를 구성한다. const firebaseConfig={}; 안에 firebase콘솔을 생성할 때 받은 코드를 삽입하면 내가 만든 콘솔과 연동할 수 있다.

아래는 연동을 하고 firebase firestore에서 데이터베이스 구조를 만들고 있는 사진이다.

 

 

 

3. 방명록 작성(firebase 데이터 쓰기) - 날짜추가, 데이터베이스 구성

import { collection, addDoc } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";

let user = $('#name').text();

$("#guestbook-postingbutton").click(async function () {

    let nickname = $('#nickname').val();
    let content = $('#content').val();
    let password = $('#password').val();
    let now = new Date();
    let localDateTime = now.toLocaleString();

    if (nickname === '' || content === '') {
        alert('닉네임과 방명록 내용을 써주세요.');
    } else {
        let doc = {
            'nickname': nickname,
            'content': content,
            'datetime': localDateTime,
            'password': password
        };
        await addDoc(collection(db, `guestBook/${user}/entries`), doc);
        alert('방명록이 작성되었습니다!');
        window.location.reload();
    }
});

 

방명록 작성의 과정은 다음과 같다

1) 개인페이지 이름을 문자열로 불러온다.

2) 입력한 닉네임, 방명록 내용, 비밀번호를 불러온다.

3) 현재 날짜를 불러와 닉네임, 방명록 내용, 비밀번호와 함께 디렉토리 형식으로 저장한다.

4) 저장한 내용을 guestbook / "입력받은 개인페이지 이름" / entries에 저장한다.

 

 

(1) 경로에 관하여

 

원래는 메인페이지에서 하나의 방명록을 구현하려고 했지만, 팀원분들과 회의를 통해 개인페이지별 방명록을 구현하면 좋을 것 같다고 판단. 경로에 페이지 이름을 추가하여 각 개인페이지마다 개별적으로 방명록을 작성할 수 있도록 구성했다.

이때 그냥 db > guestBook > user 로 경로를 간단하게 구성하고 싶었지만 firebase자체적으로 특정 갯수의 경로를 지원하지 않는다는 오류가 있었다.

Uncaught FirebaseError: Invalid collection reference. Collection references must have an odd number of segments, but guestBook/entries has 2.

 

이 오류는 Firestore에서 컬렉션 경로에 홀수 개의 세그먼트가 있어야 한다는 규칙을 어겼을 때 발생한다고 한다.

  • 컬렉션/문서/컬렉션/...처럼 홀수 세그먼트로 끝나야 한다.
  • 컬렉션/컬렉션 또는 문서/문서는 불가하다.

그래서 guestbook / "입력받은 개인페이지 이름" / entries 라는 경로를 구현했다.

 

 

(2) 간단한 예외처리

만약, 닉네임 또는 방명록에 아무것도 작성하지 않으면 빈 내용이 데이터베이스에 등록될 것이다. 이를 방지하기 위한 처리도 해놓았다.

입력이 필요한 닉네임, 내용란에 아무것도 작성되지 않으면 닉네임과 방명록을 작성해달라는 팝업이 뜨고 데이터베이스에 데이터가 올라가지 않는다. 비밀번호는 선택사항이기 때문에 조건문에 넣진 않았다.

if (nickname === '' || content === '') {
    alert('닉네임과 방명록 내용을 써주세요.');
}

 

(3) 날짜와 시간

방명록을 작성하면서 언제 작성했는지 날짜와 시간을 저장하는 것도 필요하다. 현재 날짜와 시간을 문자열로 받아서 함께 데이터 베이스에 저장하도록 했다.

let now = new Date();
let localDateTime = now.toLocaleString();

 

 

 

4. 방명록 업데이트(firebase 데이터 읽기)

import { getDocs } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";

let docs = await getDocs(collection(db, `guestBook/${user}/entries`));
docs.forEach((doc) => {
    let row = doc.data();
    console.log(row);

    let nickname = row['nickname'];
    let content = row['content'];
    let datetime = row['datetime'];
    let id = doc.id;

    let temp_guestbook = `<div class="guestbook-listrow" id="comment-${id}">
        <img src="https://mblogthumb-phinf.pstatic.net/MjAyMDExMDFfMTY0/MDAxNjA0MjI4ODc1MDgx.20zY0e0fjnqLYvyFxN2FuZl75yr0p-lejDrTdLzRargg.aDqPo9fsnwOujN45rK3vW-dUi2usn0wBwQE8xmstEBUg.JPEG.gambasg/%EC%9C%A0%ED%8A%9C%EB%B8%8C_%EA%B8%B0%EB%B3%B8%ED%94%84%EB%A1%9C%ED%95%84_%EA%B0%88%EC%83%89.jpg?type=w400"
            class="img-thumbnail guestbook-repleimage" alt="...">
        <div class="guestbook-listcolumn">
            <div style="display: flex; align-items: center;">
                <h6>${nickname}</h6>
                <div style="display: flex; margin-left: auto; align-items: center; gap:5px;">
                    <div class="form-floating mb-3 style="margin-left: auto;">
                        <div class="row g-3 align-items-center">
                            <div class="col-auto">
                                <input type="password" class="user-password" data-id="${id}" class="form-control" aria-describedby="passwordHelpInline">
                            </div>
                        </div>                            
                    </div>
                    <button class="btn guestbook-correctionBtn" id="correctionButton" data-id="${id}">수정</button>
                    <button class="btn guestbook-deleteBtn" id="deleteButton" data-id="${id}">삭제</button>
                </div>
            </div>
            <p class="guestbook-listcontent">${content}</p>
            <p class="guestbook-listdatetime">${datetime}</p>
        </div>

    </div>`

    $('#guestbook-list').append(temp_guestbook);

});

 

방명록 업데이트의 과정은 다음과 같다.

1) 이름을 불러온 경로에서 필드들을 읽어온다.

2) 필드를 row에 저장 이후 nickname, content, datetime, id를 추출한다.(비밀번호는 출력할 필요가 없으니 추출하지 않는다.)

3) 추출한 이름, 내용, 시간날짜는 텍스트 형식으로 출력되도록하고, id는 삭제하기 용이하도록 버튼과 div의 id에 삽입되도록 한다.

4) 이후 만든 것을 레이아웃 list에 append되도록 한다.

 

id란 외에는 어렵지 않았다. 이름과 내용 시간날짜들을 차례로 삽입하면 문제 없었기 때문이다.

다만 이후 이어질 챕터에 삭제 및 수정기능을 구현하기 위해(해당된 id의 데이터를 삭제하기 위해) 업데이트될 데이터, 삭제버튼, 수정버튼, 비밀번호 입력란에 각각의 이름(id)를 부여할 필요가 있었다.

각 방명록의 필드 id는 다음과 같이 획득할 수 있다.

let id = doc.id;

구현내용



 

5. 방명록 삭제(firebase 데이터 삭제) - 비밀번호를 입력해야 삭제될 수 있도록 수정

import { doc, deleteDoc } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";

// 수정전 삭제 코드
$("#deleteButton").click(async function () {
    const commentId = $(this).data("id");
    try {
        // Firebase에서 해당 댓글 삭제
        await deleteDoc(doc(db, `guestBook/${user}/entries`, commentId));

        $(`#comment-${commentId}`).remove();
        alert('댓글이 삭제되었습니다!');
    } catch (error) {
        console.error("댓글 삭제 실패:", error);
        alert('댓글 삭제에 실패했습니다.');
    }

});

 

삭제코드의 과정은 다음과 같다.

1) 각 방명록에서 삭제 버튼을 누르면, 해당 삭제버튼의 id를 불러올 수 있다.

2) 이후 정해진 경로에서 가져온 id의 문서를 삭제한다.

3) 그리고 html상에서도 해당 id가 적힌 div를 remove함으로 웹사이트에서 삭제가 완료된다.

4) 만약 삭제에 실패했다면 삭제가 되지 않았다는 팝업을 띄운다.

 

데이터를 읽을때 id와 함께 읽어야한다는 것이 제일 중요한 것 같다. 어떤 문서를 삭제할 것인지 특정한 key가 되기 때문이다. 이 key를 사용하여 데이터 베이스의 문서도 지우고, html상에서 해당 id의 부분을 삭제할 수도 있다.

 

 

 

 

- 수정 24.12.27 비밀번호를 받아 삭제할 수 있도록 수정 + 이벤트 위임

import { doc, deleteDoc } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";

$(document).on('click', '.guestbook-deleteBtn', async function () {
    const commentId = $(this).data("id");
    const passwordUser = $(`.user-password[data-id='${commentId}']`).val();

    // 삭제 확인
    const userConfirmed = confirm("정말로 이 방명록을 삭제하시겠습니까?");
    if (!userConfirmed) {
        return;
    }

    const docRef = doc(db, `guestBook/${user}/entries`, commentId);

    const docSnap = await getDoc(docRef);
    if (docSnap.exists()) {
        let row = docSnap.data();
        let password = row['password'];

        if (password !== passwordUser) {
            alert('비밀번호가 올바르지 않습니다.');
            return;
        }

        await deleteDoc(docRef);
        $(`#comment-${commentId}`).remove();
        alert('방명록이 삭제되었습니다!');
    } else {
        alert("방명록을 찾을 수 없습니다.");
    }
});

 

삭제코드의 과정은 다음과 같다.

1) 각 방명록에서 비밀번호를 입력하고(선택사항) 삭제 버튼을 누르면, 해당 삭제버튼의 id과 입력한 비밀번호를 얻을 수 있다.

2) 정말 방명록을 삭제할 것인지 여부를 묻는 팝업창을 띄우고 입력받는다.

3) 이후 입력받은 비밀번호(passwordUser)와 등록된 비밀번호(password)가 같으면 정해진 경로에서 가져온 id의 문서를 삭제한다.

4) 그리고 html상에서도 해당 id가 적힌 div를 remove함으로 웹사이트에서 삭제가 완료된다.

5) 만약 삭제에 실패했다면 삭제가 되지 않았다는 팝업을 띄운다.

 

수정전과 다른 것은 크게 두가지이다.

1. 비밀번호를 입력받아 삭제

2. 동적 DOM 업데이트 및 이벤트 리스너 연결

 

 

1. 비밀번호를 입력받아 삭제

먼저 방명록을 등록할 때부터 비밀번호를 입력받는다.(선택사항) 

이후 삭제하고 싶을 때 방명록 댓글에서 삭제버튼옆에 비밀번호를 입력받고 삭제버튼을 누를 수 있다.

입력받은 비밀번호와 들록된 비밀번호가 같다면 정해진 경로에서 가져온 id의 문서를 삭제할 수 있다.

const commentId = $(this).data("id");
const passwordUser = $(`.user-password[data-id='${commentId}']`).val();			// 입력받은 비밀번호

const docRef = doc(db, `guestBook/${user}/entries`, commentId);
const docSnap = await getDoc(docRef);

if (docSnap.exists()) {
    let row = docSnap.data();
    let password = row['password'];			// 등록된 비밀번호

    if (password !== passwordUser) {
        alert('비밀번호가 올바르지 않습니다.');
        return;
    }

    await deleteDoc(docRef);
    
    ...
}

 

 

 

2. 동적 DOM 업데이트 및 이벤트 리스너 연결

수정전 코드($("#deleteButton").click(async function () {...} ))는 코드자체가문제가 있는 것은 아니다. 하지만 각 방명록 댓글 당 먹히지 않는 삭제버튼이 많아 애먹었다. 이 삭제 버튼은 고정된 id값이 아니라 데이터 베이스에 동적인 id값을 가져오기때문에 이에 대한 오류가 존재하는 것 같았다. 오류 코드도 뜨지 않으니 다소 막막했기때문에 할수없이 챗gpt의 도움을 받도록 했다.

 

동적 DOM 업데이트 및 이벤트 리스너 연결: 삭제 버튼은 댓글을 추가할 때 동적으로 생성된다. $('#guestbook-list').append(temp_guestbook) 코드를 사용하여 DOM에 요소를 동적으로 추가하는데, 이로 인해 삭제 버튼에 대한 이벤트 리스너가 추가되지 않아서 이벤트가 발동하지 않을 수 있다.

jQuery의 .click() 이벤트 리스너는 기존의 DOM 요소에는 바로 작동하지만, 동적으로 추가된 요소에 대해서는 작동하지 않기 때문에, 이런 경우에는 이벤트 위임을 사용해야한다.

 

jQuery의 이벤트 리스너가 만능이 아니라는 뜻이다.

 

 

 

해결방법은 다음과 같다.

이벤트 위임 사용: 동적으로 생성된 삭제 버튼에도 이벤트가 제대로 등록되도록, $(document).on('click', '.deleteButton', function() {...}) 와 같은 방식으로 이벤트 위임을 적용하면 된다는 것이다.

 

이벤트 위임이란 요컨대 버튼 하나하나에 기능을 주는 것이 아닌, 동적으로 해당되는 모든 버튼을 다룬다는 것이다. 이러한 점에서 삭제버튼은 이벤트위임방식과 상성이 잘 맞다는 것을 확인 할 수 있다.

 

 

정적버튼 이벤트와 동적버튼 이벤트의 코드의 차이점은 다음과 같다.

$("#deleteButton").click(async function () {...} )  // id = "deleteButton" 사용(정적버튼)

$(document).on('click', '.guestbook-deleteBtn', async function () {...} )  // class = "guestbook-deleteBtn" 사용(동적버튼)

각 삭제버튼이 동적버튼에 해당된다면, 수정 버튼도 똑같이 동적 이벤트 위임 방법을 통해 실행할 수 있는게 아닐까? 수정에 관한 내용은 다음 챕터에서 계속 이어진다.

 

 

 

- 수정 24.12.27 비밀번호텍스트 입력란에 id부여

const passwordUser = $(`.user-password[data-id='${commentId}']`).val();

여러 개의 방명록이 기록되었을때, 각 비밀번호 란의 id도 개별적으로 설정되어야 한다. 특정한 data-id값의 텍스트박스를 불러오려면 [data-id='${Id변수}']를 불러와야 한다.

 

 

6. 방명록 수정(firebase 데이터 업데이트)

import { getDoc, updateDoc } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-firestore.js";

$(document).on('click', '.guestbook-correctionBtn', async function () {
    const commentId = $(this).data("id");
    const passwordUser = $(`.user-password[data-id='${commentId}']`).val();

    const docRef = doc(db, `guestBook/${user}/entries`, commentId);
    const docSnap = await getDoc(docRef);

    let row = docSnap.data();
    let password = row['password'];

    if (password !== passwordUser) {
        alert('비밀번호가 올바르지 않습니다.');
        return;
    }

    let nickname = row['nickname'];
    let content = row['content'];
    let datetime = row['datetime'];

    $('#nickname').val(nickname);
    $('#content').val(content);
    $('#password').val(password); 


    //수정시 작성버튼(업로드 내용과 비슷하나 updateDoc을 사용)
    $('#guestbook-postingbutton').off('click').on('click', async function () {
        let newNickname = $('#nickname').val();
        let newContent = $('#content').val();
        let newPassword = $('#password').val();
        let now = new Date();
        let localDateTime = now.toLocaleString();

        if (newNickname === '' || newContent === '') {
            alert('닉네임과 방명록 내용을 써주세요.');
        } else {
            let updatedDoc = {
                'nickname': newNickname,
                'content': newContent,
                'datetime': localDateTime,
                'password': newPassword
            };

            await updateDoc(docRef, updatedDoc);
            alert('방명록이 수정되었습니다!');
            window.location.reload();
        }
    });
});

 

수정 과정은 다음과 같다.

1) 각 방명록에서 비밀번호를 입력하고(선택사항) 수정 버튼을 누르면, 해당 수정버튼의 id과 입력한 비밀번호를 얻을 수 있다.

2) 입력받은 비밀번호(passwordUser)와 등록된 비밀번호(password)가 같으면 정해진 경로에서 가져온 id의 문서를 불러와 닉네임, 방명록 내용, 비밀번호를 방명록 작성창에 삽입한다.

3) 사용자가 자유롭게 방명록작성창에서 수정할 수 있도록 한 뒤, 작성 버튼을 누르면

4) 작성한 내용대로 닉네임, 내용, 시간, 비밀번호를 업데이트 시킨다.

5) 마지막으로 방명록이 수정되었다는 알림을 띄우고, 페이지를 새로고침한다.

 

전체적으로 앞부분은 삭제, 뒷부분은 등록으로 이루어져있다. 달랐던 부분은 addDoc해야했던 것과는 달리 업데이트기능을 하기 위해서는 updateDoc을 사용해야한다는 것이다.

 

 

 

 

7. 앞으로, 아쉬운점, 소감

웹사이트에서 firebase를 다루어보면서 강의에서는 읽기 쓰기만 다루었었는데, 이번 프로젝트를 통해 업데이트와 삭제까지 확장시킬 수 있었던 것 같다. git의 사용방법을 배우고 다른 팀원들과 협업할 수 있는 기억이 평소 코드를 작성했을 때보다 더 특별하게 다가온 것 같다.

아쉬운 점이 있다면 외관적인 조화가 조금 아쉽고(프론트엔드 전공이 아니니 크게 신경쓸 것은 아니긴하다.) 코드 작성의 최적화가 아쉽게 다가온다. 좀더 효울적으로 함수를 사용했으면 더 좋았겠다는 생각은 있다. 

추가하고 싶은 것은 로그인 기능과 관리자와 사용자를 구분할 수 있게 된다면, 각 사용자별 기능에 대한 차별화를 두고싶고, 공감 또는 덧글 기능, 이미지업로드 기능 등을 추가하고 싶다.

 

 

 

 

guestBook 완성 github: https://github.com/queenriwon/guestBook

 

GitHub - queenriwon/guestBook

Contribute to queenriwon/guestBook development by creating an account on GitHub.

github.com