Firebase로 학급 대시보드 만들기 | 전체 학생 진행도 한눈에 — 교사용 관리 도구
퀴즈 교구를 배포하고 학생 응답 데이터를 Firebase에 저장하는 것까지 했는데, 그 데이터를 보기 좋게 한눈에 확인할 방법이 없으면 결국 Firebase Console에서 JSON을 뚫어지게 쳐다보게 됩니다.
이 글에서는 Firebase Realtime Database에 쌓인 학생 데이터를 읽어서, 학급 대시보드로 시각화하는 방법을 다룹니다. 학생별 진행도, 평균 점수, 문항별 정답률을 차트와 표로 보여주는 교사용 관리 페이지를 만듭니다.
📌 한눈에 보기
- 대상: Firebase Realtime DB에 학생 데이터를 저장 중인 교사
- 결과물: 학급 대시보드 HTML 페이지 (교사 전용)
- 핵심 기능: 학생별 진행도 / 평균 점수 차트 / 문항별 정답률 / 실시간 업데이트
- 기술: Firebase JS SDK + Canvas API (Chart.js 라이브러리)
- 소요 시간: 약 30~40분
- 비용: 무료 (Firebase Spark Plan)
대시보드가 필요한 이유
Firebase Console에서 데이터를 직접 보면 이런 형태입니다.
Firebase Console에서 보이는 원본 데이터
{
"responses": {
"1반_김민수": { "score": 80, "total": 100, "timestamp": 1719100000 },
"1반_이서연": { "score": 95, "total": 100, "timestamp": 1719100120 },
"1반_박준호": { "score": 60, "total": 100, "timestamp": 1719100300 },
...
}
}
데이터가 10개만 돼도 JSON 구조를 눈으로 읽는 건 고역입니다. 30명이면? 불가능에 가깝죠.
❌ Firebase Console 직접 확인
JSON 데이터를 하나하나 읽으며 점수 파악. 스크롤 끝없이. 전체 현황 파악 불가.
30명 데이터 확인에 20분
✅ 대시보드
막대 차트로 점수 분포 한눈에. 미제출 학생 자동 표시. 실시간 업데이트.
접속하는 즉시 전체 현황 파악
대시보드가 있으면 이런 질문에 즉시 답할 수 있습니다.
- "우리 반에서 아직 안 푼 학생이 누구지?"
- "평균 점수가 몇 점이지?"
- "어떤 문항을 가장 많이 틀렸지?"
- "1반과 2반의 성적 차이가 있나?"
Step 1. 데이터 구조 확인
대시보드를 만들기 전에, Firebase에 저장된 데이터 구조를 확인합니다. 이전 글에서 다룬 퀴즈 교구 기준으로 아래와 같은 구조를 사용합니다.
권장 데이터 구조
{
"quiz-results": {
"quiz-01": {
"1반_김민수": {
"score": 80,
"total": 100,
"answers": [true, true, false, true, true, false, true, true, false, true],
"timestamp": 1719100000
},
"1반_이서연": {
"score": 95,
"total": 100,
"answers": [true, true, true, true, true, false, true, true, true, true],
"timestamp": 1719100120
}
}
}
}
answers 배열은 문항별 정오답 기록입니다. 이게 있어야 문항별 정답률을 분석할 수 있어요.
💡 데이터 구조가 다르다면?
이미 다른 구조로 데이터를 저장하고 있다면, 대시보드 코드에서 경로만 바꾸면 됩니다. 핵심은 학생 이름(또는 ID)과 점수가 저장되어 있으면 대시보드를 만들 수 있다는 겁니다.
Step 2. Firebase에서 데이터 읽기
대시보드 HTML 파일을 만들고, Firebase에서 데이터를 읽어오는 코드를 작성합니다.
dashboard.html 기본 구조
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>학급 대시보드</title>
<!-- Chart.js (차트 라이브러리) -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<!-- Firebase SDK -->
<script src="https://www.gstatic.com/firebasejs/10.12.0/firebase-app-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/10.12.0/firebase-database-compat.js"></script>
</head>
<body>
<div id="dashboard">
<h1>학급 대시보드</h1>
<div id="summary"></div>
<canvas id="scoreChart" width="700" height="350"></canvas>
<div id="studentTable"></div>
</div>
<script>
// Firebase 설정 (본인 프로젝트 값으로 교체)
const firebaseConfig = {
apiKey: "여기에-API키",
authDomain: "프로젝트명.firebaseapp.com",
databaseURL: "https://프로젝트명-default-rtdb.firebaseio.com",
projectId: "프로젝트명"
};
firebase.initializeApp(firebaseConfig);
const db = firebase.database();
</script>
</body>
</html>
① Firebase 설정값 찾기
Firebase Console → 프로젝트 선택 → 프로젝트 설정(톱니바퀴) → "내 앱" 섹션에서 firebaseConfig 값을 복사합니다.
② 데이터 읽기 코드
Firebase Realtime Database에서 퀴즈 결과를 읽어오는 코드입니다. </script> 닫는 태그 바로 위에 추가하세요.
데이터 읽기 + 변환
// 퀴즈 결과 읽기
const quizRef = db.ref('quiz-results/quiz-01');
quizRef.on('value', (snapshot) => {
const data = snapshot.val();
if (!data) {
document.getElementById('summary').innerHTML
= '<p>아직 제출된 응답이 없습니다.</p>';
return;
}
// 데이터를 배열로 변환
const students = Object.entries(data).map(([name, info]) => ({
name: name,
score: info.score,
total: info.total,
percent: Math.round((info.score / info.total) * 100),
answers: info.answers || [],
time: new Date(info.timestamp * 1000)
}));
// 점수순 정렬
students.sort((a, b) => b.percent - a.percent);
// 화면에 표시
showSummary(students);
showChart(students);
showTable(students);
});
quizRef.on('value', ...)는 데이터가 바뀔 때마다 자동으로 다시 호출됩니다. 학생이 퀴즈를 제출하면 대시보드가 자동으로 업데이트됩니다.
Step 3. 차트로 시각화 (Chart.js)
Chart.js는 Canvas 기반의 차트 라이브러리입니다. CDN 한 줄 추가하면 바로 쓸 수 있고, 막대 차트, 도넛 차트, 라인 차트 등 다양한 형태를 지원합니다.
점수 분포 막대 차트
let chartInstance = null;
function showChart(students) {
const ctx = document.getElementById('scoreChart').getContext('2d');
// 기존 차트 제거 (실시간 업데이트 시)
if (chartInstance) chartInstance.destroy();
const names = students.map(s => s.name);
const scores = students.map(s => s.percent);
const colors = scores.map(s =>
s >= 80 ? '#4caf50' : // 초록: 우수
s >= 60 ? '#ff9800' : // 주황: 보통
'#f44336' // 빨강: 미달
);
chartInstance = new Chart(ctx, {
type: 'bar',
data: {
labels: names,
datasets: [{
label: '점수(%)',
data: scores,
backgroundColor: colors,
borderRadius: 6
}]
},
options: {
responsive: true,
scales: {
y: { beginAtZero: true, max: 100 }
},
plugins: {
legend: { display: false }
}
}
});
}
💡 색상 기준 커스터마이즈
위 코드에서 80점 이상은 초록, 60점 이상은 주황, 미만은 빨강으로 표시합니다. 이 기준을 여러분의 평가 기준에 맞게 바꾸세요. 예를 들어 90/70으로 바꾸면 더 엄격한 기준이 됩니다.
Step 4. 요약 통계 표시
요약 통계 (평균, 최고, 최저, 제출률)
function showSummary(students) {
const totalStudents = 30; // 전체 학생 수 (본인 학급에 맞게 수정)
const submitted = students.length;
const avg = Math.round(
students.reduce((sum, s) => sum + s.percent, 0) / submitted
);
const highest = Math.max(...students.map(s => s.percent));
const lowest = Math.min(...students.map(s => s.percent));
document.getElementById('summary').innerHTML = `
<div style="display:grid; grid-template-columns:repeat(4,1fr);
gap:16px; margin:20px 0;">
<div style="background:#e3f2fd; padding:16px; border-radius:10px;
text-align:center;">
<div style="font-size:2rem; font-weight:700; color:#1565c0;">
${submitted}/${totalStudents}
</div>
<div style="color:#666; margin-top:4px;">제출 현황</div>
</div>
<div style="background:#e8f5e9; padding:16px; border-radius:10px;
text-align:center;">
<div style="font-size:2rem; font-weight:700; color:#2e7d32;">
${avg}점
</div>
<div style="color:#666; margin-top:4px;">평균 점수</div>
</div>
<div style="background:#fff3e0; padding:16px; border-radius:10px;
text-align:center;">
<div style="font-size:2rem; font-weight:700; color:#e65100;">
${highest}점
</div>
<div style="color:#666; margin-top:4px;">최고 점수</div>
</div>
<div style="background:#fce4ec; padding:16px; border-radius:10px;
text-align:center;">
<div style="font-size:2rem; font-weight:700; color:#c62828;">
${lowest}점
</div>
<div style="color:#666; margin-top:4px;">최저 점수</div>
</div>
</div>
`;
}
Step 5. 학생별 진행도 테이블
학생 목록 테이블
function showTable(students) {
let html = `
<table style="width:100%; border-collapse:collapse; margin-top:24px;">
<thead>
<tr style="background:#f5f5f5;">
<th style="border:1px solid #ddd; padding:10px;">순위</th>
<th style="border:1px solid #ddd; padding:10px;">이름</th>
<th style="border:1px solid #ddd; padding:10px;">점수</th>
<th style="border:1px solid #ddd; padding:10px;">진행도</th>
<th style="border:1px solid #ddd; padding:10px;">제출 시간</th>
</tr>
</thead>
<tbody>`;
students.forEach((s, i) => {
const barColor = s.percent >= 80 ? '#4caf50' :
s.percent >= 60 ? '#ff9800' : '#f44336';
const timeStr = s.time.toLocaleString('ko-KR');
html += `
<tr>
<td style="border:1px solid #ddd; padding:10px;
text-align:center;">${i + 1}</td>
<td style="border:1px solid #ddd; padding:10px;">${s.name}</td>
<td style="border:1px solid #ddd; padding:10px;
text-align:center; font-weight:700;">
${s.score}/${s.total}
</td>
<td style="border:1px solid #ddd; padding:10px;">
<div style="background:#eee; border-radius:10px; height:20px;
overflow:hidden;">
<div style="background:${barColor}; width:${s.percent}%;
height:100%; border-radius:10px;
text-align:center; color:white; font-size:12px;
line-height:20px;">
${s.percent}%
</div>
</div>
</td>
<td style="border:1px solid #ddd; padding:10px;
font-size:0.9rem; color:#666;">${timeStr}</td>
</tr>`;
});
html += '</tbody></table>';
document.getElementById('studentTable').innerHTML = html;
}
각 학생마다 프로그레스 바가 표시되어, 점수가 시각적으로 바로 파악됩니다. 80% 이상은 초록, 60% 이상은 주황, 미만은 빨강으로 직관적으로 구분됩니다.
Step 6. 실시간 업데이트
이미 위 코드에서 quizRef.on('value', ...)를 사용했기 때문에, 별도 작업 없이 실시간 업데이트가 됩니다.
⚡ 실시간 업데이트가 작동하는 방식
- 교사가 대시보드를 브라우저에서 열어 둡니다.
- 학생이 퀴즈를 풀고 제출합니다.
- 제출된 데이터가 Firebase에 저장됩니다.
- Firebase가
on('value')리스너를 통해 대시보드에 변경을 알립니다. - 대시보드가 자동으로 갱신됩니다. 새로고침 필요 없음!
수업 중에 대시보드를 켜 놓으면, 학생이 퀴즈를 제출할 때마다 차트가 실시간으로 업데이트되는 걸 볼 수 있습니다.
💡 실시간 업데이트 끄기
실시간이 필요 없고 처음 한 번만 읽으면 되는 경우, .on('value', ...)를 .once('value').then(...)으로 바꾸면 됩니다. 데이터 읽기 횟수를 줄여서 Firebase 무료 요금제 한도를 아낄 수 있습니다.
대시보드 스타일 다듬기
기능은 완성됐지만, 보기 좋게 스타일을 입히면 더 편하게 사용할 수 있습니다. <head> 안에 아래 CSS를 추가하세요.
대시보드 CSS
<style>
body {
font-family: 'Noto Sans KR', sans-serif;
background: #f5f5f5;
margin: 0; padding: 20px;
}
#dashboard {
max-width: 900px;
margin: 0 auto;
background: white;
padding: 30px;
border-radius: 16px;
box-shadow: 0 2px 12px rgba(0,0,0,0.08);
}
h1 {
color: #e65100;
border-bottom: 3px solid #ffcc80;
padding-bottom: 10px;
}
@media (max-width: 600px) {
#dashboard { padding: 16px; }
/* 요약 통계를 2열로 변경 */
#summary > div > div { grid-template-columns: repeat(2, 1fr) !important; }
}
</style>
보안: 대시보드 접근 제한
대시보드는 교사만 봐야 합니다. 학생이 다른 학생의 점수를 볼 수 있으면 안 되겠죠. 간단한 방법 두 가지를 소개합니다.
방법 1: 비밀번호 입력 (간단)
대시보드 페이지에 접속하면 비밀번호를 물어보는 방식입니다. 완벽한 보안은 아니지만, 학생이 우연히 접근하는 것은 막을 수 있어요.
간단 비밀번호 체크
// 페이지 로드 시 비밀번호 확인
const password = prompt('대시보드 비밀번호를 입력하세요:');
if (password !== '선생님비번2026') {
document.body.innerHTML = '<h2>접근 권한이 없습니다.</h2>';
throw new Error('인증 실패');
}
방법 2: Firebase 보안 규칙 (권장)
Firebase Console에서 Database → Rules에서 읽기 권한을 제한합니다.
Firebase 보안 규칙 예시
{
"rules": {
"quiz-results": {
// 누구나 쓰기 가능 (학생이 결과를 저장)
".write": true,
// 읽기는 인증된 사용자만
".read": "auth != null"
}
}
}
이 규칙을 적용하면, Firebase Authentication으로 로그인한 사용자만 데이터를 읽을 수 있습니다. 교사의 구글 계정만 허용하도록 설정할 수 있어요.
⚠️ 보안 규칙은 반드시 설정하세요
Firebase 기본 보안 규칙은 "테스트 모드"로 시작하는 경우가 많은데, 이 모드는 누구나 읽기/쓰기가 가능합니다. 학생 데이터가 외부에 노출될 수 있으므로, 반드시 보안 규칙을 설정하세요.
활용 팁
💡 팁 1: 반별 필터 추가
여러 반을 가르치면, 데이터를 반별로 저장하고 대시보드에 반 선택 드롭다운을 추가하세요. db.ref('quiz-results/quiz-01')에서 경로를 db.ref('quiz-results/' + selectedClass + '/quiz-01')로 바꾸면 됩니다.
💡 팁 2: 엑셀 다운로드 버튼
대시보드에 "CSV 다운로드" 버튼을 추가하면, 데이터를 엑셀로 바로 내려받을 수 있습니다. 성적 처리할 때 편합니다. FileSaver.js 라이브러리를 쓰면 간단합니다.
💡 팁 3: 문항별 정답률 분석
answers 배열 데이터가 있으면, 문항별로 정답률을 계산해서 "학생들이 가장 많이 틀린 문항"을 찾을 수 있습니다. 이 정보로 다음 수업에서 보충 설명할 내용을 정할 수 있어요.
💡 팁 4: 대시보드를 배포하지 마세요
대시보드 HTML은 교사의 로컬 컴퓨터에만 두는 것을 권장합니다. GitHub Pages나 Firebase Hosting에 올리면 학생이 URL을 알아낼 수 있어요. 로컬에서 브라우저로 열어도 Firebase 데이터는 정상적으로 읽어옵니다.
💡 팁 5: Claude에게 대시보드 만들어달라고 하세요
위 코드를 처음부터 직접 타이핑할 필요 없습니다. Claude에게 "Firebase Realtime DB에 저장된 퀴즈 데이터를 읽어서 학급 대시보드 HTML을 만들어줘. Chart.js로 점수 분포 차트를 넣고, 학생별 진행도 테이블도 넣어줘"라고 요청하면, 완성된 대시보드 코드를 한 번에 받을 수 있습니다. 이 글의 코드는 구조와 원리를 이해하기 위한 것이고, 실제로는 AI에게 맡기는 게 훨씬 빠릅니다.
이 글이 도움이 되셨다면 댓글로 알려주세요. 대시보드를 만들면서 궁금한 점이 있으면 질문 남겨 주시면 답변드리겠습니다.
대화 참여하기