반응형

🎪 우리만의 메시지 게시판 놀이공원 만들기
우리가 만들 게시판을 놀이공원의 특별한 메시지 보드라고 생각해보세요!
- 📝 글쓰기 부스: 새로운 메시지 작성
- 👀 메시지 보드: 모든 메시지들을 실시간으로 보기
- ✏️ 수정 코너: 내가 쓴 메시지 고치기
- 🗑️ 삭제 버튼: 필요없는 메시지 지우기
🏗️ 먼저 Firestore 설정하기
1단계: Firebase 콘솔에서 Firestore 활성화
- Firebase 콘솔 접속
- 여러분의 프로젝트 클릭
- 왼쪽 메뉴에서 "Firestore Database" 클릭
- "데이터베이스 만들기" 버튼 클릭
- "테스트 모드에서 시작" 선택 (나중에 보안 규칙 설정 가능)
- "완료" 클릭
이제 우리만의 데이터 창고가 생겼어요! 📦
🎨 완전한 게시판 시스템 만들기
Firebase Firestore 게시판 시스템
대화형 아티팩트
🎯 코드 설명 - 각 기능별로 쉽게 이해하기
📝 글쓰기 (writeMessage 함수)
javascript
async function writeMessage() {
// 1️⃣ 사용자가 입력한 내용 가져오기
const title = document.getElementById('messageTitle').value;
const content = document.getElementById('messageContent').value;
const author = document.getElementById('messageAuthor').value;
// 2️⃣ Firestore 데이터베이스에 새 문서 추가
await db.collection('messages').add({
title: title, // 제목
content: content, // 내용
author: author, // 작성자
createdAt: firebase.firestore.FieldValue.serverTimestamp() // 작성 시간
});
console.log("🎉 메시지가 데이터베이스에 저장되었어요!");
}
👀 실시간 목록 조회 (onSnapshot)
javascript
// Firebase의 마법! 데이터가 바뀔 때마다 자동으로 알려줌
db.collection('messages')
.orderBy('createdAt', 'desc') // 최신 메시지부터 정렬
.onSnapshot((snapshot) => {
console.log("🔄 데이터가 업데이트되었어요!");
// 변경된 내용을 화면에 표시
displayMessages(snapshot.docs);
});
✏️ 글 수정 (update 함수)
javascript
async function saveEdit(messageId) {
// 수정된 내용 가져오기
const newTitle = document.getElementById(`edit-title-${messageId}`).value;
// Firestore에서 해당 문서 업데이트
await db.collection('messages').doc(messageId).update({
title: newTitle,
updatedAt: firebase.firestore.FieldValue.serverTimestamp()
});
console.log("✏️ 메시지가 수정되었어요!");
}
🗑️ 글 삭제 (delete 함수)
javascript
async function deleteMessage(messageId) {
// 정말 삭제할 건지 확인
if (confirm("정말 삭제하시겠어요?")) {
// Firestore에서 문서 삭제
await db.collection('messages').doc(messageId).delete();
console.log("🗑️ 메시지가 삭제되었어요!");
}
}
🎪 Firestore 데이터 구조 이해하기
우리의 게시판 데이터는 이렇게 저장돼요:
🗄️ Firestore Database
└── 📁 messages (컬렉션)
├── 📄 문서1 (자동 생성 ID)
│ ├── title: "첫 번째 메시지"
│ ├── content: "안녕하세요!"
│ ├── author: "김철수"
│ ├── createdAt: 2024-08-23 14:30:00
│ └── updatedAt: 2024-08-23 14:30:00
├── 📄 문서2
│ ├── title: "두 번째 메시지"
│ └── ...
└── 📄 문서3...
🛠️ 실제로 사용해보기
1단계: Firebase 설정 넣기
코드에서 이 부분을 여러분의 실제 설정으로 바꿔주세요:
javascript
const firebaseConfig = {
apiKey: "your-actual-api-key",
authDomain: "your-project.firebaseapp.com",
projectId: "your-actual-project-id",
storageBucket: "your-project.appspot.com",
messagingSenderId: "123456789",
appId: "your-actual-app-id"
};
2단계: Firestore 보안 규칙 설정 (중요!)
Firebase 콘솔에서:
- Firestore Database → 규칙 탭 클릭
- 아래 규칙을 복사해서 붙여넣기:
javascript
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// messages 컬렉션에 대한 규칙
match /messages/{document} {
// 모든 사람이 읽기 가능
allow read: if true;
// 모든 사람이 쓰기, 수정, 삭제 가능 (테스트용)
allow write: if true;
}
}
}
- "게시" 버튼 클릭
3단계: 테스트해보기!
- HTML 파일을 브라우저에서 열기
- F12 눌러서 콘솔 열기
- 첫 번째 메시지 작성해보기:
- 제목: "첫 번째 메시지!"
- 내용: "안녕하세요! 테스트 메시지예요."
- 작성자: "테스터"
🎯 각각의 마법이 어떻게 작동하는지
🔄 실시간 업데이트의 비밀
Firebase Firestore는 마법의 전령같아요!
javascript
// 이 한 줄이 마법을 만들어요!
db.collection('messages').onSnapshot((snapshot) => {
// 누군가 메시지를 추가/수정/삭제하면 즉시 알려줌!
});
실시간 작동 원리:
- 👀 누군가 새 메시지를 써요
- 🔄 Firestore가 "새 메시지 있어요!" 알림
- 📺 모든 사용자의 화면이 자동으로 업데이트!
📊 데이터 저장 방식
javascript
// 이렇게 저장해요
{
title: "재미있는 이야기", // 문자열
content: "오늘은 날씨가...", // 문자열
author: "김철수", // 문자열
createdAt: Timestamp, // 시간 (Firebase가 자동 생성)
updatedAt: Timestamp // 수정 시간
}
🎮 고급 기능들 이해하기
🔍 데이터 정렬하기
javascript
// 최신 순으로 정렬
.orderBy('createdAt', 'desc')
// 제목 순으로 정렬 (가나다순)
.orderBy('title', 'asc')
// 작성자 순으로 정렬
.orderBy('author', 'asc')
📄 페이지네이션 (많은 데이터 처리)
javascript
// 처음 10개만 가져오기
db.collection('messages')
.orderBy('createdAt', 'desc')
.limit(10)
🔎 데이터 검색하기
javascript
// 특정 작성자의 메시지만 찾기
db.collection('messages')
.where('author', '==', '김철수')
.get()
🚨 문제 해결 가이드
😢 "permission-denied" 에러
→ Firestore 보안 규칙을 확인하세요! 위의 2단계 규칙을 적용했나요?
😅 메시지가 안 보여요
→ F12 → Console에서 에러 확인하고, Firebase 설정이 정확한지 확인하세요!
🔄 실시간 업데이트가 안 돼요
→ 인터넷 연결 상태와 Firestore가 활성화되었는지 확인하세요!
💾 데이터가 저장 안 돼요
→ Firestore Database가 "테스트 모드"로 설정되었는지 확인하세요!
🌟 재미있는 실험해보기
🎪 다중 브라우저 테스트
- 같은 페이지를 두 개의 브라우저에서 열어보세요
- 한쪽에서 메시지를 작성하면
- 다른 쪽에서 실시간으로 나타나는 걸 볼 수 있어요! ✨
📱 모바일에서도 테스트
- 핸드폰 브라우저에서도 열어보세요
- 컴퓨터와 핸드폰이 실시간으로 동기화되는 걸 확인하세요!
🔍 Firebase 콘솔에서 데이터 확인
- Firebase 콘솔 → Firestore Database로 가세요
- 여러분이 작성한 메시지들이 실제로 저장되어 있는 걸 볼 수 있어요!
🎉 축하해요! 완성된 기능들
이제 여러분은:
- ✅ Firestore 데이터베이스에 데이터를 저장할 수 있어요
- ✅ 실시간으로 데이터 변화를 감지할 수 있어요
- ✅ CRUD 기능 (Create, Read, Update, Delete)을 모두 구현했어요
- ✅ 사용자 친화적인 UI로 게시판을 만들었어요
- ✅ 에러 처리와 사용자 피드백을 제공할 수 있어요
🚀 다음 단계 아이디어
🎨 더 발전시켜보기
- 📸 이미지 업로드 기능 추가
- 👍 좋아요/싫어요 버튼 추가
- 💬 댓글 시스템 만들기
- 🔐 사용자 인증과 연결하기
- 🏷️ 태그나 카테고리 기능
- 🔍 검색 기능 강화
🎪 다른 종류의 앱 만들기
- 📝 할일 관리 앱
- 💬 채팅 앱
- 📚 온라인 일기장
- 🎮 게임 점수판
전체 코드
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>🎪 놀이공원 메시지 게시판</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Comic Sans MS', cursive;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1000px;
margin: 0 auto;
}
h1 {
text-align: center;
font-size: 2.5em;
margin: 20px 0;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}
.carnival {
text-align: center;
font-size: 3em;
margin: 20px;
animation: bounce 2s ease-in-out infinite;
}
@keyframes bounce {
0%, 100% { transform: translateY(0px); }
50% { transform: translateY(-15px); }
}
.section {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border-radius: 20px;
padding: 30px;
margin: 20px 0;
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
}
.write-section {
background: linear-gradient(135deg, rgba(255, 107, 53, 0.3), rgba(247, 147, 30, 0.3));
}
.board-section {
background: linear-gradient(135deg, rgba(76, 175, 80, 0.3), rgba(139, 195, 74, 0.3));
}
.form-group {
margin: 15px 0;
}
label {
display: block;
margin-bottom: 8px;
font-weight: bold;
font-size: 1.1em;
}
input, textarea {
width: 100%;
padding: 12px;
border: none;
border-radius: 10px;
font-size: 1em;
background: rgba(255, 255, 255, 0.9);
color: #333;
font-family: inherit;
}
input:focus, textarea:focus {
outline: none;
box-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
transform: scale(1.02);
}
textarea {
height: 120px;
resize: vertical;
}
button {
background: linear-gradient(45deg, #ff6b35, #f7931e);
color: white;
border: none;
padding: 12px 20px;
border-radius: 25px;
font-size: 1em;
cursor: pointer;
margin: 5px;
transition: all 0.3s ease;
font-weight: bold;
font-family: inherit;
}
button:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
.btn-edit {
background: linear-gradient(45deg, #2196F3, #21CBF3);
padding: 8px 15px;
font-size: 0.9em;
}
.btn-delete {
background: linear-gradient(45deg, #e74c3c, #c0392b);
padding: 8px 15px;
font-size: 0.9em;
}
.btn-cancel {
background: linear-gradient(45deg, #95a5a6, #7f8c8d);
}
.message-card {
background: rgba(255, 255, 255, 0.15);
border-radius: 15px;
padding: 20px;
margin: 15px 0;
transition: transform 0.3s ease;
border-left: 5px solid #ffd700;
}
.message-card:hover {
transform: scale(1.02);
}
.message-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
flex-wrap: wrap;
}
.message-title {
font-size: 1.3em;
font-weight: bold;
color: #ffd700;
margin-bottom: 5px;
}
.message-author {
font-size: 0.9em;
color: #b3b3b3;
}
.message-date {
font-size: 0.8em;
color: #999;
}
.message-content {
background: rgba(255, 255, 255, 0.1);
padding: 15px;
border-radius: 10px;
margin: 10px 0;
line-height: 1.6;
}
.message-actions {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.status-message {
padding: 15px;
border-radius: 10px;
margin: 15px 0;
font-weight: bold;
text-align: center;
}
.success {
background: rgba(76, 175, 80, 0.3);
border: 1px solid #4CAF50;
}
.error {
background: rgba(244, 67, 54, 0.3);
border: 1px solid #f44336;
}
.info {
background: rgba(33, 150, 243, 0.3);
border: 1px solid #2196F3;
}
.loading {
text-align: center;
padding: 40px;
}
.loading-spinner {
font-size: 2em;
animation: spin 2s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.empty-message {
text-align: center;
padding: 40px;
color: #b3b3b3;
}
.edit-form {
background: rgba(33, 150, 243, 0.2);
border: 2px solid #2196F3;
border-radius: 15px;
padding: 20px;
margin-top: 15px;
}
@media (max-width: 768px) {
.container {
padding: 10px;
}
h1 {
font-size: 2em;
}
.message-header {
flex-direction: column;
align-items: flex-start;
}
.message-actions {
justify-content: center;
}
}
</style>
</head>
<body>
<div class="container">
<h1>🎪 놀이공원 메시지 게시판</h1>
<div class="carnival">🎡🎢🎠</div>
<!-- 상태 메시지 -->
<div id="statusMessage"></div>
<!-- 글쓰기 섹션 -->
<div class="section write-section">
<h2>📝 새로운 메시지 작성하기</h2>
<div class="form-group">
<label>🎯 제목 (어떤 이야기인가요?):</label>
<input type="text" id="messageTitle" placeholder="재미있는 제목을 써주세요!" maxlength="100">
</div>
<div class="form-group">
<label>📖 내용 (자세히 이야기해주세요!):</label>
<textarea id="messageContent" placeholder="여러분의 이야기를 들려주세요! 오늘 뭘 했는지, 어떤 기분인지..." maxlength="1000"></textarea>
</div>
<div class="form-group">
<label>✍️ 작성자 (누가 쓰는 메시지인가요?):</label>
<input type="text" id="messageAuthor" placeholder="여러분의 이름이나 별명을 써주세요!" maxlength="50">
</div>
<button onclick="writeMessage()">🎪 메시지 게시판에 올리기!</button>
<button onclick="clearForm()" class="btn-cancel">🧹 다시 쓰기</button>
</div>
<!-- 게시판 섹션 -->
<div class="section board-section">
<h2>👀 모든 메시지 보기 (실시간 업데이트!)</h2>
<div id="refreshStatus" class="info status-message">
🔄 실시간으로 새 메시지를 확인하고 있어요!
</div>
<div id="messagesContainer">
<div class="loading">
<div class="loading-spinner">🔄</div>
<p>메시지들을 불러오고 있어요...</p>
</div>
</div>
</div>
</div>
<!-- Firebase SDK -->
<script src="https://www.gstatic.com/firebasejs/10.7.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/10.7.1/firebase-firestore.js"></script>
<script>
// 🔑 Firebase 설정 (여기에 여러분의 실제 설정을 넣어주세요!)
const firebaseConfig = {
apiKey: "your-api-key-here",
authDomain: "your-project.firebaseapp.com",
projectId: "your-project-id",
storageBucket: "your-project.appspot.com",
messagingSenderId: "123456789",
appId: "your-app-id-here"
};
// 🚀 Firebase 초기화
let app;
let db;
let messagesListener = null;
try {
app = firebase.initializeApp(firebaseConfig);
db = firebase.firestore();
console.log("🎉 Firebase와 Firestore 준비 완료!");
showStatus("🎉 놀이공원 게시판이 준비되었어요!", "success");
// 실시간 메시지 감지 시작
startRealTimeListener();
} catch (error) {
console.error("😢 Firebase 초기화 실패:", error);
showStatus("😢 놀이공원 준비 중 오류가 발생했어요. 설정을 확인해주세요!", "error");
}
// 📝 새 메시지 작성 함수
async function writeMessage() {
const title = document.getElementById('messageTitle').value.trim();
const content = document.getElementById('messageContent').value.trim();
const author = document.getElementById('messageAuthor').value.trim();
// 입력값 검사
if (!title) {
showStatus("😅 제목을 써주세요!", "error");
return;
}
if (!content) {
showStatus("😅 내용을 써주세요!", "error");
return;
}
if (!author) {
showStatus("😅 작성자 이름을 써주세요!", "error");
return;
}
console.log("📝 새 메시지 작성 시작...");
showStatus("✨ 메시지를 게시판에 올리고 있어요...", "info");
try {
// Firestore에 새 문서 추가
const docRef = await db.collection('messages').add({
title: title,
content: content,
author: author,
createdAt: firebase.firestore.FieldValue.serverTimestamp(),
updatedAt: firebase.firestore.FieldValue.serverTimestamp()
});
console.log("🎉 메시지 작성 성공! ID:", docRef.id);
showStatus("🎉 메시지가 게시판에 올라갔어요!", "success");
clearForm();
} catch (error) {
console.error("😢 메시지 작성 실패:", error);
showStatus("😢 메시지 올리기에 실패했어요. 다시 시도해주세요!", "error");
}
}
// 👀 실시간 메시지 감지 시작
function startRealTimeListener() {
console.log("👀 실시간 메시지 감지 시작!");
// Firestore에서 실시간으로 데이터 감지
messagesListener = db.collection('messages')
.orderBy('createdAt', 'desc') // 최신 메시지부터
.onSnapshot((snapshot) => {
console.log("🔄 메시지 업데이트 감지! 변경된 문서 수:", snapshot.docChanges().length);
// 변경 사항 로그
snapshot.docChanges().forEach((change) => {
if (change.type === 'added') {
console.log("➕ 새 메시지 추가:", change.doc.data().title);
} else if (change.type === 'modified') {
console.log("✏️ 메시지 수정:", change.doc.data().title);
} else if (change.type === 'removed') {
console.log("🗑️ 메시지 삭제됨");
}
});
displayMessages(snapshot.docs);
}, (error) => {
console.error("😢 실시간 감지 오류:", error);
showStatus("😢 실시간 업데이트 중 오류가 발생했어요.", "error");
});
}
// 🖥️ 메시지들을 화면에 표시
function displayMessages(docs) {
const container = document.getElementById('messagesContainer');
if (docs.length === 0) {
container.innerHTML = `
<div class="empty-message">
<h3>📭 아직 메시지가 없어요!</h3>
<p>첫 번째 메시지를 작성해보세요! 🌟</p>
</div>
`;
return;
}
let html = '';
docs.forEach((doc) => {
const data = doc.data();
const createdAt = data.createdAt ?
data.createdAt.toDate().toLocaleString('ko-KR') :
'방금 전';
html += `
<div class="message-card" id="message-${doc.id}">
<div class="message-header">
<div>
<div class="message-title">${escapeHtml(data.title)}</div>
<div class="message-author">✍️ ${escapeHtml(data.author)}</div>
</div>
<div class="message-date">📅 ${createdAt}</div>
</div>
<div class="message-content">${escapeHtml(data.content).replace(/\n/g, '<br>')}</div>
<div class="message-actions">
<button class="btn-edit" onclick="startEdit('${doc.id}', '${escapeHtml(data.title)}', '${escapeHtml(data.content)}', '${escapeHtml(data.author)}')">
✏️ 수정하기
</button>
<button class="btn-delete" onclick="deleteMessage('${doc.id}', '${escapeHtml(data.title)}')">
🗑️ 삭제하기
</button>
</div>
<div id="edit-${doc.id}" class="edit-form" style="display: none;">
<!-- 수정 폼이 여기에 동적으로 추가됩니다 -->
</div>
</div>
`;
});
container.innerHTML = html;
console.log("🖥️ 메시지 화면 업데이트 완료! 총", docs.length, "개");
}
// ✏️ 메시지 수정 시작
function startEdit(messageId, title, content, author) {
console.log("✏️ 메시지 수정 모드 시작:", messageId);
const editContainer = document.getElementById(`edit-${messageId}`);
editContainer.style.display = 'block';
editContainer.innerHTML = `
<h3>✏️ 메시지 수정하기</h3>
<div class="form-group">
<label>🎯 제목:</label>
<input type="text" id="edit-title-${messageId}" value="${title}" maxlength="100">
</div>
<div class="form-group">
<label>📖 내용:</label>
<textarea id="edit-content-${messageId}" maxlength="1000">${content}</textarea>
</div>
<div class="form-group">
<label>✍️ 작성자:</label>
<input type="text" id="edit-author-${messageId}" value="${author}" maxlength="50">
</div>
<button onclick="saveEdit('${messageId}')">💾 수정 완료!</button>
<button onclick="cancelEdit('${messageId}')" class="btn-cancel">❌ 취소</button>
`;
// 수정 중임을 알리기
showStatus("✏️ 수정 모드입니다. 내용을 바꾸고 '수정 완료'를 눌러주세요!", "info");
}
// 💾 메시지 수정 저장
async function saveEdit(messageId) {
const title = document.getElementById(`edit-title-${messageId}`).value.trim();
const content = document.getElementById(`edit-content-${messageId}`).value.trim();
const author = document.getElementById(`edit-author-${messageId}`).value.trim();
if (!title || !content || !author) {
showStatus("😅 모든 칸을 채워주세요!", "error");
return;
}
console.log("💾 메시지 수정 저장:", messageId);
showStatus("✨ 메시지를 수정하고 있어요...", "info");
try {
await db.collection('messages').doc(messageId).update({
title: title,
content: content,
author: author,
updatedAt: firebase.firestore.FieldValue.serverTimestamp()
});
console.log("🎉 메시지 수정 완료!");
showStatus("🎉 메시지가 수정되었어요!", "success");
cancelEdit(messageId);
} catch (error) {
console.error("😢 메시지 수정 실패:", error);
showStatus("😢 메시지 수정에 실패했어요. 다시 시도해주세요!", "error");
}
}
// ❌ 수정 취소
function cancelEdit(messageId) {
console.log("❌ 메시지 수정 취소:", messageId);
document.getElementById(`edit-${messageId}`).style.display = 'none';
}
// 🗑️ 메시지 삭제
async function deleteMessage(messageId, title) {
// 정말 삭제할 건지 확인
if (!confirm(`🗑️ 정말 "${title}" 메시지를 삭제하시겠어요?\n\n삭제하면 다시 되돌릴 수 없어요!`)) {
return;
}
console.log("🗑️ 메시지 삭제 시작:", messageId);
showStatus("🗑️ 메시지를 삭제하고 있어요...", "info");
try {
await db.collection('messages').doc(messageId).delete();
console.log("🎉 메시지 삭제 완료!");
showStatus("🎉 메시지가 삭제되었어요!", "success");
} catch (error) {
console.error("😢 메시지 삭제 실패:", error);
showStatus("😢 메시지 삭제에 실패했어요. 다시 시도해주세요!", "error");
}
}
// 🧹 입력 폼 정리
function clearForm() {
document.getElementById('messageTitle').value = '';
document.getElementById('messageContent').value = '';
document.getElementById('messageAuthor').value = '';
console.log("🧹 입력 폼을 깨끗하게 정리했어요!");
}
// 📢 상태 메시지 표시
function showStatus(message, type) {
const statusDiv = document.getElementById('statusMessage');
statusDiv.innerHTML = `<div class="status-message ${type}">${message}</div>`;
// 5초 후 성공/에러 메시지 자동 삭제 (info는 유지)
if (type !== 'info') {
setTimeout(() => {
statusDiv.innerHTML = '';
}, 5000);
}
}
// 🛡️ HTML 이스케이프 (보안을 위해)
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML.replace(/'/g, ''');
}
// 🎮 페이지 언로드 시 리스너 정리
window.addEventListener('beforeunload', () => {
if (messagesListener) {
console.log("🧹 실시간 리스너를 정리합니다");
messagesListener();
}
});
// 🌟 페이지 로드 시 안내
window.onload = function() {
console.log("🎪 놀이공원 게시판에 오신 걸 환영해요!");
console.log("🎯 기능 안내:");
console.log(" 📝 메시지 작성: 제목, 내용, 작성자를 입력하고 '올리기' 클릭");
console.log(" 👀 실시간 조회: 다른 사람이 메시지를 올리면 자동으로 보여져요");
console.log(" ✏️ 메시지 수정: '수정하기' 버튼으로 내용 바꾸기");
console.log(" 🗑️ 메시지 삭제: '삭제하기' 버튼으로 메시지 지우기");
console.log(" 🔄 모든 변화가 실시간으로 반영돼요!");
showStatus("🎪 놀이공원 메시지 게시판에 오신 걸 환영해요! 첫 메시지를 작성해보세요!", "info");
};
</script>
</body>
</html>

반응형
'데이터베이스' 카테고리의 다른 글
| [Firebase] 5장 실시간 채팅 기능 구현 (1) | 2025.08.23 |
|---|---|
| [Firebase] 4장 FirebaseStorage를 활용한 파일 업로드 실습 (3) | 2025.08.23 |
| [Firebase] 2장 사용자 인증(Authentication) 실습 (0) | 2025.08.23 |
| [Firebase] 1장: Firebase 소개 및 개발 환경 설정 (0) | 2025.08.23 |