가볍게 참여를 던지고 바로 반응하는, 실시간 강의 참여 플랫폼
강사가 수업 중 질문을 출제하면 학생들이 모바일·노트북으로 즉시 응답합니다. 객관식부터 워드클라우드, 퀴즈, AI 심사 과제까지 — 수업을 재미있게 만드는 10+ 참여 도구를 한 곳에.
Live (배포자 데모): https://jinan-6c884.web.app — fork 사용자는 본인 Firebase 프로젝트로 별도 배포
- 1초 안에 이해되는 UX — 학생이 화면 보자마자 뭘 해야 하는지 안다
- 실시간 반응성 — Firebase RTDB로 모든 변화가 즉시 동기화
- 한국 교실 맞춤 — Pretendard 폰트, 한국어 UI, 강의 흐름에 최적화
- Anti-AI 디자인 — slate 모노크로매틱 + 신중한 인디고 액센트 (자세한 건 DESIGN_SYSTEM.md)
- AI 도구 내장 — 질문 생성, 응답 요약, 과제 예심/본심까지 Gemini 기반
| 기능 | 설명 |
|---|---|
| QR 접속 | 링크/QR로 즉시 참여, 닉네임만 입력 |
| 투표·응답 10종 | 객관식, O/X, 워드클라우드, Q&A, 퀴즈, 척도, 토론, 순위, 빈칸채우기, 체크 |
| 타이머 | 질문별 원형 링 카운트다운, 5초 이하 펄스 |
| 리액션 | 👍🔥❤️😂👏 하단바 (lucide 아이콘, 3초 쿨다운) |
| 손들기·긴급 질문 | 익명으로 강사에게 도움 요청 |
| 공개 Q&A | 질문 올리기 + 추천 기능 |
| 실시간 채팅 | 공개 채팅 + 스태프 1:1 DM |
| 리더보드·업적 | 퀴즈 점수, 연속 정답, 개인 리포트 |
| 다크모드 | Sun/Moon 토글, localStorage 저장 |
| 기능 | 설명 |
|---|---|
| 클래스 대시보드 | 강의별 그룹, 세션 생성/복제, 참여율 차트 |
| 질문 관리 | 추가/수정/삭제/드래그 정렬, 보관함 재사용 |
| 키보드 단축키 | ← → (이동), Space (다음), R (정답), L (리더보드), Esc (대기) |
| 게임 모드 | 추첨(Lottery), 발표자 뽑기, 쉬는 시간 타이머, 접속 현황 |
| 수업 기록 | 참여율·정답률 통계, 어려웠던 질문 분석, CSV 내보내기 |
| 스태프 관리 | 스태프 계정 초대, 역할 분담 |
| 세션 기록 | 질문별 응답 히스토리, 학생 리포트 |
/live?s={sessionId}— 프로젝터·전자칠판용 대형 화면- 뒷자리에서도 보이는 큰 글자, 높은 대비
- 실시간 막대 상승, 워드클라우드 형성, 리액션 버블
| 기능 | 사용자 | 설명 |
|---|---|---|
| AI 질문 생성 | 강사 | 주제 입력 → 3~6개 질문(객관식/OX/워드클라우드/척도) 즉시 생성 |
| 실시간 응답 요약 | 강사 | 워드클라우드·Q&A 응답 5개 이상 → 테마 분류 + 인사이트 |
| 제출 전 AI 예심 | 학생 | 과제 제출 전 1명 AI 코치의 형성 피드백 (강점·개선점·퀵윈) |
| 7명 AI 심사위원 | 강사 | 사후 과제를 7인 패널이 다각도 평가 + 자동 시상 |
- 강사: 과제 등록 → 학생 제출 링크 공유 → 마감 후 AI 심사 → 시상식
- 학생: HTML 파일(또는 ZIP 통째로) 업로드 + PRD + 설명 → 이름·PIN으로 결과 조회
- AI 심사위원 7인: 김기획(PM), 박사용(사용자), 이디자(디자이너), 최실용(팀장), 정창의(대표), 한완성(QA), 강소통(교육자)
- 자동 시상: 대상/최우수/우수 + 기획상/창의상/디자인상/실용상
| 학생 (모바일) | 학생 (데스크톱) |
|---|---|
![]() |
![]() |
| 강사 로그인 |
|---|
![]() |
🚧 본격 기능 화면(투표·퀴즈·워드클라우드·AI 7판사 심사·리더보드 등)은 추가 예정. 본인 인스턴스에서 캡쳐 후
docs/screenshots/에 추가하면 PR 환영.
Frontend React 19 + Vite 7
Styling Tailwind CSS v4 + Framer Motion
Icons lucide-react (이모지 금지)
Fonts Pretendard (한국어) + Inter (숫자/영문)
Backend Firebase Realtime Database + Auth + Storage + Hosting
AI Gemini 2.5 flash-lite (@google/generative-ai)
Drag&Drop @dnd-kit
ZIP JSZip (과제 압축 파일 지원)
Animation lottie-react + Framer Motion
Bulletproof React 패턴 — 기능별 수직 분할:
src/
├── app/routes/ # 얇은 페이지 컴포넌트 (조립만)
│ ├── admin/ # /admin — 강사 대시보드
│ ├── live/ # /live — 프레젠터 화면
│ ├── submit/ # /submit — 과제 제출
│ └── report/ # /report — 학생 리포트
├── components/ui/ # 공통 UI 프리미티브 (비즈니스 로직 금지)
├── features/ # 도메인별 기능 17개 (api/ + components/)
│ ├── session/ # useSession, ConnectionBanner
│ ├── participants/ # useParticipants, ParticipantList
│ ├── voting/ # 9 voter (Choice/OX/Quiz/Wordcloud/Scale/Debate/Ranking/FillBlank/Check)
│ ├── questions/ # AIQuestionGenerator, useUrgentQuestions
│ ├── class-questions/ # ClassQAPanel, ClassQABoard (학생 Q&A)
│ ├── hand-raise/ # useHandRaises, HandRaiseList
│ ├── visualization/ # BarChart, WordCloud, AISummaryBanner, VizRenderer
│ ├── games/ # Lottery, BreakTimer, RandomPicker, JoinShow
│ ├── timer/ # useTimer (서버 시간 동기화)
│ ├── reactions/ # ReactionBar, ReactionOverlay, ChatBubbleOverlay
│ ├── quiz/ # useScores, Leaderboard, useSpeedQuiz
│ ├── chat/ # 공개 채팅 (강사↔학생)
│ ├── dm/ # DMBubble, StaffDMAlert (스태프-학생 1:1)
│ ├── course/ # useCourses (강의 그룹)
│ ├── ai-judge/ # 7판사 LIVE 심사
│ ├── assignments/ # 사후 과제 제출/심사/시상
│ └── report/ # ClassInsightCard, LearningReportCard
├── hooks/ # 크로스피처 훅
├── lib/ # firebase.js, design-tokens.js
└── styles/ # Tailwind + globals
- Import 방향:
lib/ → components/ → features/ → app/routes/(단방향) - Feature 격리: 피처가 다른 피처를 직접 import하지 않음
- Barrel 금지:
index.js재수출 없이 직접 경로 import - Path alias:
@/=src/
- Node.js 22+ (
.env자동 로드를 위해--env-file플래그 사용) - npm 10+
- Firebase 프로젝트 (RTDB + Hosting)
- Gemini API 키 (AI 기능용)
git clone https://github.com/wlsdks/pick.git
cd pick
npm install- Firebase 콘솔에서 프로젝트 생성
- Realtime Database 활성화 (asia-southeast1 권장)
- Storage 활성화 (과제 스크린샷 업로드용)
- Hosting 활성화 (
firebase init hosting) - 프로젝트 설정 → 일반 → 내 앱 → Web 앱 SDK 구성에서 config 값 복사
.env.example을 복사해서 본인 값으로 채우세요:
cp .env.example .env채워야 할 항목:
VITE_GEMINI_API_KEY— Google AI Studio에서 발급VITE_FIREBASE_*— 위 2단계에서 복사한 값들VITE_COURSE_CONTEXT— (선택) 본인 강의 맥락. 7판사 AI 심사가 이 텍스트를 참고해 강의에 맞는 평가를 합니다. 비워두면 일반화된 기본 prompt 사용. 본인 강의에 더 깊이 맞춰진 prompt가 필요하면src/features/assignments/api/prompts.js를 직접 수정
Vite 특성상
VITE_*는 빌드 타임에 dist 번들로 인라인됩니다. 프로덕션 배포 시 Google AI Studio 콘솔에서 HTTP referrer 제한(본인 도메인만 허용)을 걸어 quota 보호하세요. Firebase Web SDK config는 정책상 secret 아니지만(rules로 보호), 본인 프로젝트 격리를 위해 환경 변수로 분리.
firebase deploy --only database --project YOUR_PROJECT_IDstaff-accounts.example.txt 참고. 실제 운영 계정은 staff-accounts.txt (gitignored) 파일로 본인 환경에서 관리하세요. /admin 라우트의 회원가입 폼으로 직접 생성도 가능합니다 (마스터 계정 승인 필요).
index.html의 og:image / twitter:image URL이 https://pick.aslan.it.kr/og-image.jpg로 박혀 있습니다 (배포자 본인 데모 인스턴스용). fork 후 본인 도메인으로 운영할 거면 이 두 줄을 본인 도메인으로 변경하세요.
npm run dev # Vite 개발 서버
npm run build # 프로덕션 빌드
npm run preview # 빌드 결과 미리보기
npm run lint # ESLint
npm run check # lint + buildnpm run build
firebase deploy --only hosting| 경로 | 접근 | 설명 |
|---|---|---|
/ |
학생 | QR/링크 진입, 세션 참여 |
/admin |
강사·스태프 | 로그인 → 대시보드 → 수업 진행 |
/live?s={sessionId} |
누구나 | 프레젠터 화면 (프로젝터용) |
/submit?a={assignmentId} |
학생 | 과제 제출 및 결과 조회 |
/report?s=&p= |
학생 | 개인 학습 리포트 |
sessions/{sessionId}
├── info # 세션 메타 (title, courseName, createdAt)
├── currentQuestion # 활성 질문 ID
├── currentMode # poll | quiz | leaderboard | waiting | game
├── questions/{qId}
│ ├── type # choice | ox | wordcloud | qna | quiz | ...
│ ├── title
│ ├── options[]
│ ├── correctAnswer
│ ├── activatedAt, revealedAt, awardedAt
│ └── votes/{participantId} # { nickname, value, timestamp }
├── participants/{pid}
├── scores/{pid} # 퀴즈 점수, 연속 정답, 업적
├── reactions, handRaises, urgentQuestions, classQuestions
├── chat, staffChat
└── game state (lottery, breakTimer, randomPicker, joinShow)
assignments/{assignmentId}
├── title, description, status, aiJudging
├── submissions/{subId}
│ ├── name, pin (4자리 숫자)
│ ├── fileContent (HTML/ZIP 추출 텍스트)
│ ├── prdContent, description
│ └── submittedAt, updatedAt
├── results/{subId}
│ ├── judges/{judgeId} # 7명 심사위원별 결과
│ └── summary # avgScore, selectedCount, passed
└── awards/{awardId} # 대상/최우수/우수/기획상/...
users/{uid} # 강사 계정
원칙: 화면당 2~3색 이하, 장식 색상 0, 장식 모션 0
CTA bg-slate-900 (dark CTA, indigo 아님)
악센트 indigo (차트 바, 포커스 링, 진행바 전용)
간격 단위 4px 기본, 카드 p-5, 모달 p-6
모션 spring(300/25), 모두 400ms 이하
터치 타겟 모바일 최소 48px
폰트 Pretendard 400~700, 크기 12~36px
자세한 건 DESIGN_SYSTEM.md + CLAUDE.md 참조.
| ❌ AI 기본값 | ✅ Pick |
|---|---|
bg-indigo-600 CTA |
bg-slate-900 dark CTA |
| 컬러 원형 아이콘 배경 | bare lucide 아이콘 |
| 5색 배지/선택지 | slate 모노크로매틱 |
| Sparkles/Stars 아이콘 | 사자 마스코트 (PickMascot) |
강사가 수업 중 즉흥 대응할 때 사용. 주제를 입력하면 Gemini가 다양한 타입의 질문을 생성:
- 지원 타입: choice, ox, wordcloud, scale
- 속도: ~2초 (gemini-2.5-flash-lite)
- 통합: 생성 후 기존
importFromLibrary경로로 Firebase에 저장
wordcloud·Q&A 질문에서 응답 5개 이상 쌓이면 활성화. 결과:
{
"themes": [
{ "label": "개인적 효용", "count": 8, "summary": "..." },
{ "label": "성장/경험", "count": 6, "summary": "..." }
],
"insight": "학생들은 효율성을 가장 큰 동기로 삼고 있으며..."
}- 강사 전용:
isAdmin프롭이 있는 VizRenderer에서만 렌더 (학생 화면 비노출) - 재분석 유도: 응답 +3개 이상 새로 들어오면 "새 응답이 있어요" 배지
본 심사 전 학생이 자발적으로 받는 형성 피드백. 점수 없이 개선 힌트만:
{
"overallImpression": "전반적 인상 1~2문장",
"strengths": ["강점1", "강점2", ...],
"improvements": ["구체적 개선점1", ...],
"quickWin": "지금 당장 바꾸면 좋아질 한 가지"
}- 쿨다운: 2분 (localStorage 기반)
- 활성화 조건: 코드 파일 업로드 후
과제 제출물을 7개 관점에서 병렬 평가 (Promise.all). 각 심사위원의 캐릭터·말투·평가 기준이 구분되어 있음:
| 심사위원 | 역할 | 전문 |
|---|---|---|
| 김기획 | PM | 문제 정의·PRD |
| 박사용 | 실사용자 | 직관성·UX |
| 이디자 | 디자이너 | 레이아웃·시각 |
| 최실용 | 팀장 | 실무 활용성 |
| 정창의 | 대표 | 아이디어 독창성 |
| 한완성 | QA | 기능 완성도 |
| 강소통 | 교육자 | AI 협업 품질 |
- 점수 앵커: 1
3 결함 / 67 평균 / 8 인상적 / 9 베스트 / 10 예외적 - 자동 시상: 대상·최우수·우수 (평균 점수) + 특별상 4개 (판단 심사위원 기준)
상세 규칙은 CLAUDE.md 참조.
- JSX only — TypeScript 없음
- 컴포넌트 200줄 이하 — 넘으면 서브컴포넌트로 분리
- 훅 반환 패턴:
{ data, loading, error, actions } - Firebase listener:
useEffect에서 cleanup 필수 - Performance: 무거운 컴포넌트는
React.memo, prop 핸들러는useCallback - 한국어 UI — 모든 사용자 대면 텍스트는 한국어
- Firebase 쓰기 — 빠른 연속 쓰기는 debounce
- CLAUDE.md — 프로젝트 규칙 (AI 코드 어시스턴트용)
- DESIGN_SYSTEM.md — 디자인 토큰·패턴
- FEATURES.md — 상세 기능 명세
- PRD.md — 제품 요구사항 문서
| 명령 | 설명 |
|---|---|
npm run dev |
Vite 개발 서버 |
npm run build |
프로덕션 빌드 (브로틀리·gzip 압축 포함) |
npm run lint |
ESLint |
npm run check |
lint + build |
npm run preview |
빌드 결과 로컬 미리보기 |
firebase deploy --only hosting |
배포 |
Firebase Hosting. Service Worker가 hashed asset만 캐싱하고 index.html은 네트워크-온리라 배포 후 즉시 반영됨. CDN 캐시 반영에 몇 분 걸릴 수 있음.
npm run build
firebase deploy --only hosting --project jinan-6c884- Gemini API 키:
.env(VITE_GEMINI_API_KEY)로만 주입. UI/localStorage 입력 경로는 제거됨. Vite 특성상 빌드 시 클라이언트 번들에 인라인되므로 Google AI Studio 콘솔에서 referrer 제한 + rate limit으로 quota 보호. 상용화 시 Cloud Functions 서버 프록시 권장 - PIN 저장: 과제 제출 PIN은 평문으로 RTDB에 저장. RTDB Rules에서 schema validation·name/pin immutability 적용됨 (
database.rules.jsonassignments/$assignmentId/submissions). 진정한 read 차단은 Firebase Auth 도입 + owner-scoping 필요 - 동명이인 보호: 클라이언트
isEdit플래그 + RTDB Rules에서 name immutability 강제. 신규 제출 시 같은 이름 차단은 클라이언트 사전 검증 - Auth 부재: Firebase Auth 미사용 — 강사 로그인은 클라이언트가
admins노드를 직접 읽어 username/passwordHash 비교.sessions/assignmentsroot listing은 강사 대시보드에 필요해 read 차단 불가. 진정한 권한 분리는 후속 Auth 도입 phase 필요
개인 프로젝트지만 외부 기여 환영합니다.
- 이슈 등록 전: 기존 이슈 확인
- 버그 신고: bug_report 템플릿 사용 — 재현 단계 필수
- 기능 제안: feature_request 템플릿 사용 — 사용자 시나리오 우선
- PR 절차: CONTRIBUTING.md 참고
- 보안 취약점: 공개 이슈 X — SECURITY.md 절차 따라주세요
- 행동 규범: CODE_OF_CONDUCT.md


