Firebase의 Cloud DB 솔루션인 Firestore는 그 자체가 Database인데, 이를 이용하면 게시판을 만들 수 있다. 게시판 자체도 일종의 DB라고 볼 수 있기 때문이다. 다만 취미로 Firebase를 사용하는 정도의 사용자 경우에, 다소 완성도가 떨어지더라도 게시판을 편하고 쉽게 만드는 방법을 인터넷을 찾아보아도 적당한 것을 찾을 수 없어 직접 만들게 되었다. 물론 내가 잘 사용하곤 하는 Copilot의 도움을 받았다.
게시판의 형태는 다음과 같이 자동으로 부여된 순번과, 제목, 간단한 글자 정보와 작성자, 작성일 등이 나타나게 했고, 글의 생성 번호의 역순으로 정렬되도록 구성했다.
이러한 게시판의 테이블 형식 스타일은 다음과 같은 스타일 요소를 만들어 적용하였고,
게시판 테이블내 각각의 요소의 표시는 다음과 같이 함수 형태의 코드로 표시되도록 하였는데,
실제 실행 코드는 다음 부분으로, 이 부분에서 위에 설명한 readpost() 함수를 이용하여 게시판을 표시하되
핸들러를 이용하여 다음과 같은 별도의 페이지(writeboard.html)로 이동하여 작성하도록 했는데,
이러한 글쓰기 양식은 다음과 같은 style 요소를 사용하고,
입력하는 데이터는 다음과 같은 컨테이너화된 폼을 이용하여 입력받도록 구성했다.
또한 게시판에 글을 등록할 때 부여할 자동 생성 순번은 미리 만들어둔 counterDB에서 값을 가져오고, counterDB에는 1이 증가된 값을 다시 저장하는 함수의 형태로 구성했다.
물론 이렇게 하기 위해서는 미리 기존에 만들어 사용하던 counterDB라는 컬렉션에 게시판의 글번호 counter 값을 저장하기 위해, 다음과 postCounter라는 문서(=record)를 미리 만들고 초기값으로 1을 넣어두었다. 이를 코드 시행전에 미리 만들어두지 않으면 해당 코드는 동작하지 않으므로 미리 만들어두어야 한다.
실제 게시판 입력 사항의 등록은 다음과 같이 function 형태로 작성한 후, (참고로 이 함수 내에서 게시판 글번호 값을 가져오는 함수를 호출하게 됨)
다음과 같이 실행 코드에서 이 함수를 호출하는 방식을 사용하였다. (참고 : 글의 등록이 마쳐지면, 위의 함수에서 지정한 대로, 게시판 화면인 noticeboard.html 페이지로 이동)
물론 이러한 게시판 글을 실제 등록이 될 수 있도록 다음과 같이 Firestore 내에 posts라는 이름으로 컬렉션을 미리 만들고, 실제 컬렉션 생성을 위해 test라는 이름의 문서(레코드)를 하나 만들어 두었다.
이렇게 만들어진 코드는 게시판 내용을 표시하는 페이지와, 글쓰기 페이지의 2개 페이지로 구성되는데, 각각의 완성된 정체 코드는 다음과 같다.
1) 게시판 표시 페이지 – noticeboard.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>게시판</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<style>
table {
border-collapse: collapse; /* 셀 구분선을 합칩니다 */
width: 100%; /* 테이블의 너비를 화면 전체로 설정합니다 */
margin: 0 5px; /* 테이블의 양쪽 마진을 10px로 설정합니다 */
}
table, th, td {
border: 1px solid black; /* 테이블과 셀에 모두 외곽선을 추가합니다 */
}
.left-aligned-cell { /* 새로운 클래스 추가 */
text-align: left; /* 텍스트를 좌측에 맞춥니다 */
}
input, textarea {
border: 1px solid black !important; /* 외곽선의 너비를 2px로 설정합니다 */
}
</style>
</head>
<body>
<script src="https://www.gstatic.com/firebasejs/8.6.5/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.6.5/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.6.5/firebase-firestore.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.6.5/firebase-storage.js"></script>
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
<script>
const firebaseConfig = { 각 사용자별로 정해지는 접속정보로 생략
};
firebase.initializeApp(firebaseConfig);
</script>
<div class="container mt-5 mb-3">
<h4> 게시판 ...</h4>
<table id="posts" style="text-align: center; border: 2px solid black;">
<tr>
<th style="font-weight: bold; width: 100px;">No.</th>
<th style="font-weight: bold; width: 400px;">제목</th>
<th style="font-weight: bold; width: 1500px;">내용</th>
<th style="font-weight: bold; width: 100px;">작성자</th>
<th style="font-weight: bold; width: 150px;">Date</th>
</tr>
<!-- 이곳에 게시물이 동적으로 추가됩니다 -->
</table>
<button class="btn btn-primary mt-5" id="towrite">글 쓰기</button>
</div>
<script>
var db = firebase.firestore();
function readPosts() {
db.collection("posts").orderBy("postNumber", "desc").get().then((querySnapshot) => {
var postsTable = document.getElementById('posts');
while (postsTable.rows.length > 1) {
postsTable.deleteRow(postsTable.rows.length - 1);
}
querySnapshot.forEach((doc) => {
var row = postsTable.insertRow();
var postNumberCell = row.insertCell();
var titleCell = row.insertCell();
var contentCell = row.insertCell();
contentCell.className = 'left-aligned-cell'; // CSS 클래스 추가
contentCell.textContent = doc.data().content;
var authorCell = row.insertCell();
var createdAtCell = row.insertCell();
postNumberCell.textContent = doc.data().postNumber;
titleCell.textContent = doc.data().title;
contentCell.textContent = doc.data().content;
authorCell.textContent = doc.data().author;
createdAtCell.textContent = doc.data().createdAt.toDate().toLocaleString();
});
});
}
window.onload = function() {
readPosts();
}
$('#towrite').click(function () {
setTimeout(() => window.location.href = "/writeboard.html", 100)
});
</script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
</body>
</html>2) 글쓰기 페이지 – writeboard.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>게시판 새글 쓰기</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<style>
input, textarea {
border: 1px solid black !important;
}
</style>
</head>
<body>
<script src="https://www.gstatic.com/firebasejs/8.6.5/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.6.5/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.6.5/firebase-firestore.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.6.5/firebase-storage.js"></script>
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
<script>
const firebaseConfig = { 사용자별 접속정보로 생략
};
firebase.initializeApp(firebaseConfig);
</script>
<div class="container mt-5">
<form id="post-form">
<h4> 글쓰기 ...</h4>
<input type="text" class="form-control mt-4 mb-5" id="title" placeholder="제목">
<textarea class="form-control mt-4 mb-4" id="content" rows="24" cols="50">내용</textarea>
<input type="text" class="form-control mt-4 mb-5" id="author" placeholder="글쓴이"> <!-- 글쓴이 필드 추가 -->
<button type="submit" class="btn btn-danger mt-2 mb-5">게시</button>
</form>
</div>
<script>
var db = firebase.firestore();
function getNextpostNumber() {
return db.runTransaction((transaction) => {
return transaction.get(db.collection('counterDB').doc('postCounter')).then((counterDoc) => {
if (!counterDoc.exists) {
throw "Counter document does not exist!";
}
var newpostNumber = counterDoc.data().postNumber + 1;
transaction.update(db.collection('counterDB').doc('postCounter'), { postNumber: newpostNumber });
return newpostNumber;
});
});
}
function writePost(title, content, author) {
getNextpostNumber().then((postNumber) => {
db.collection("posts").add({
postNumber: postNumber,
title: title,
content: content,
author: author,
createdAt: firebase.firestore.FieldValue.serverTimestamp()
})
.then((docRef) => {
console.log("Document written with ID: ", docRef.id);
setTimeout(() => window.location.href = "/noticeboard.html", 500)
})
.catch((error) => {
console.error("Error adding document: ", error);
});
})
}
document.getElementById('post-form').addEventListener('submit', function(e) {
e.preventDefault();
var title = document.getElementById('title').value;
var content = document.getElementById('content').value;
var author = document.getElementById('author').value;
writePost(title, content, author);
});
</script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
</body>
</html>