ESP32-S3 Bench Guide
ESP32-S3 N16R8 bench guide: live pinout, lessons, simulators and the bench log.
단어장 한 번이면 수업 게임이 바로 됩니다. 초등 영어 교실 도구 · 클래스뱅크
Translation pending
EducationTypeScript · JavaScriptGitHub @pointbank2608-commitsOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Loading the full README. The saved excerpt is shown below.
학원용 포인트 뱅킹 웹앱. React + Vite + Supabase. 아직 적용되지 않았습니다. 이 단계를 먼저 하셔야 앱이 동작합니다. 1. https://supabase.com/dashboard 접속 → 해당 프로젝트 선택 2. 왼쪽 메뉴에서 SQL Editor → New query 3. supabase/schema.sql 파일 내용을 전부 복사해서 붙여넣기 4. Run 클릭 여러 번 실행해도 안전하게 작성되어 있으니, 나중에 스키마를 고칠 때도 같은 방식으로 다시 실행하면 됩니다. 이미 schema.sql을 한 번 실행한 뒤 기능이 추가되면, supabase/ 폴더의 번호 붙은 파일만 따로 실행하면 됩니다. 새 프로젝트를 처음부터 세팅할 때는 schema.sql 하나만 실행하면 됩니다 (마이그레이션 내용이 이미 포함돼 있음). Supabase는 기본적으로 회원가입 시 이메일 인증을 요구합니다. 개발 중에는 꺼두는 게 편합니다. • Dashboard → Authentication → Sign In / Providers → Email → Confirm email 끄기 실제 서비스 오픈 전에는 다시 켜세요.Read the full README ↗
학원용 포인트 뱅킹 웹앱. React + Vite + Supabase.
pointbank/
├─ supabase/schema.sql ← DB 스키마 (SQL Editor에서 실행)
├─ app/ ← React 앱
│ ├─ .env.local ← Supabase URL / anon key (git 커밋 금지)
│ └─ src/
│ ├─ lib/ supabase 클라이언트, API 함수, 타입
│ ├─ context/ 인증 상태, 토스트
│ ├─ components/ 통장 카드
│ └─ pages/ 인증 · 온보딩 · 반별통장 · 순위 · 설정 · 학생화면
└─ point-bank-prototype.html ← 원본 프로토타입 (참고용, 이제 미사용)
아직 적용되지 않았습니다. 이 단계를 먼저 하셔야 앱이 동작합니다.
supabase/schema.sql 파일 내용을 전부 복사해서 붙여넣기여러 번 실행해도 안전하게 작성되어 있으니, 나중에 스키마를 고칠 때도 같은 방식으로 다시 실행하면 됩니다.
이미 schema.sql을 한 번 실행한 뒤 기능이 추가되면, supabase/ 폴더의 번호 붙은 파일만 따로 실행하면 됩니다.
| 파일 | 내용 |
|---|---|
002_daily_board.sql | 일일 마감(settlements) + 결과 보기 집계 함수(ranking_summary) |
003_student_scope.sql | 순위 조회 함수를 선생님/원장 전용으로 제한 |
004_academy_logo.sql | 학원 로고 업로드 (academies.logo_url + logos 스토리지 버킷) |
005_attendance.sql | 출석부 (attendance 테이블 — 등원/하원 체크 + 월별 기록) |
006_disable_student_login.sql | 보안 — 학생 로그인 잠금 (베타 기간 동안 원장·선생님만) |
007_platform_admin.sql | 플랫폼 관리자 ([email protected] 전용 — 가입 학원 목록/삭제) |
008_ladder_order_games.sql | 사다리타기 · 랜덤 공 뽑기 게임 + 게임 배경음악 업로드 |
009_fix_logo_storage.sql | 학원 로고 업로드 RLS 수정 (upsert 시 SELECT 정책 누락 문제) |
010_homework_tracking.sql | 프리셋·거래 내역에 "숙제 관련" 표시(is_homework) 추가 |
011_game_images.sql | 명칭이 있는 다이어그램 · 이미지 퀴즈용 사진 업로드 (game-images 스토리지 버킷) |
새 프로젝트를 처음부터 세팅할 때는 schema.sql 하나만 실행하면 됩니다 (마이그레이션 내용이 이미 포함돼 있음).
Supabase는 기본적으로 회원가입 시 이메일 인증을 요구합니다. 개발 중에는 꺼두는 게 편합니다.
실제 서비스 오픈 전에는 다시 켜세요.
npm run dev --prefix app
+ 학생 추가오늘 마감 → 학생별 오늘 적립분을 확인하고 통장에 적립하기누적 총액을 항상 띄워두면 격차가 고정된 사실처럼 보여서, 뒤처진 학생은 따라잡을 수 없다고 느낍니다. 그래서 수업 중에 보는 화면은 오늘 적립분만 크게 표시하고, 매일 0에서 다시 시작합니다.
취소로 되돌릴 수 있습니다.결과 보기 화면에서 봅니다.포인트는 버튼을 누르는 즉시 DB에 저장됩니다. 마감은 저장이 아니라 "확정 + 잠금"이라, 수업 중에 브라우저가 닫히거나 새로고침돼도 기록이 날아가지 않습니다.
날짜는 선생님 브라우저의 로컬 시간 기준입니다 (transactions.created_at은 UTC로 저장되지만,
조회할 때 로컬 자정을 UTC로 변환해 넘깁니다).
설정 화면의 선생님 초대 코드(6자리)를 전달 → 그 선생님이 회원가입 후 "선생님으로 합류하기"에서 입력.
위 "베타 기간: 학생 로그인 잠금" 참고. 코드는 남아 있고(StudentPage.tsx 등) DB 실행 권한만
회수한 상태라, 다시 열 때 새로 개발할 필요는 없습니다.
| 테이블 | 역할 |
|---|---|
academies | 학원 (테넌트 최상위). 포인트 단위, 선생님 초대 코드 보유 |
classes | 반 |
students | 학생 계좌. claim_code로 학생 계정과 연결 |
profiles | 로그인 사용자 (owner / teacher / student / admin) |
transactions | 거래 원장. 잔액은 저장하지 않고 이 합으로 계산 |
presets | 지급/차감 사유 버튼 |
redemptions | 포인트 상품 교환 내역 (추후 기능용, 테이블만 준비) |
game_templates | 미니게임 템플릿 (추후 기능용, 테이블만 준비) |
잔액을 따로 저장하지 않는 이유: "누가 언제 왜 줬는지"를 항상 추적·정정할 수 있어야 하기 때문입니다. 잔액은 student_balances 뷰가 합산해줍니다.
지금은 원장·선생님만 로그인할 수 있습니다. 학생 온보딩 옵션과 claim_student() 함수 실행
권한을 껐습니다.
이유: academies.invite_code(선생님 초대 코드)와 students.claim_code(학생 통장 연결 코드)는
같은 학원 소속이면 누구나 조회할 수 있는 구조입니다. 학생 계정이 존재하면, 학생이 이 값을 읽어
① 선생님 초대 코드로 재가입해 선생님 권한을 얻거나 ② 다른 학생의 claim_code로 그 학생 계정을
가로챌 수 있었습니다. 이메일 인증까지 꺼둔 상태(현재 베타 세팅)라 실제로 가능한 공격 경로였습니다.
학생 로그인을 다시 열려면:
supabase/006_disable_student_login.sql 맨 아래 주석 처리된 grant execute 실행app/src/pages/OnboardingPage.tsx, AuthPage.tsx, SettingsPage.tsx에서 이 마이그레이션 때
지워진 학생 관련 UI 복구 (git log에서 "학생 로그인 잠금" 커밋 참고).env.local의 anon key는 공개되어도 되는 값입니다. 프론트엔드 번들에 어차피 포함됩니다.schema.sql의 4번 섹션.
create_academy / join_academy_as_teacher / claim_student / claim_admin 네 함수로만 가능합니다. 아무나 임의 학원에 owner로 끼어드는 걸 막기 위해서입니다.supabase/007_platform_admin.sql 실행 후, [email protected]으로 정상 회원가입만 하면 됩니다
(비밀번호는 Supabase 최소 자리수인 6자 이상이어야 합니다). 이 이메일로 로그인하면 온보딩 화면에
"관리자로 시작하기" 버튼이 뜨고, 누르면 학원 소속 없이 /admin으로 들어갑니다.
claim_admin() RPC가 서버에서 auth.users.email을 다시 확인하는 방식이라, 클라이언트가
임의로 admin을 자칭할 수 없습니다. admin_list_academies()/claim_admin() 둘 다 anon 롤의 실행
권한을 명시적으로 회수해뒀습니다 (Supabase는 함수 생성 시 anon에게도 기본 EXECUTE 권한을 자동
부여하므로, authenticated/public만 revoke하면 구멍이 남습니다 — 006_disable_student_login.sql
작업 때 발견한 것과 같은 함정).다섯 게임 모두 game_templates 테이블 하나를 공유합니다 (game_type 컬럼으로 구분, CHECK 제약이
없어 새 게임 종류를 추가해도 스키마 변경이 필요 없습니다). 반 전용/학원 공용 여부, 만들기·이름변경·
삭제 흐름은 다섯 게임이 동일해서 app/src/lib/useGameTemplates.ts 훅 하나로 공통 처리합니다.
app/src/lib/ladder.ts — 같은 행에서 인접한 가로줄이 겹치지 않게만 배치하면 항상 1:1 대응이
된다는 성질을 이용했고, 2~10명 기준 2만 회 시뮬레이션으로 검증했습니다.LotteryMachine.tsx).
순위 이름표("1등"/"1번" 등)는 선생님이 직접 눌러서 수정할 수 있고, 참가자 수와 항상 맞춰
자동으로 늘어나거나 줄어듭니다.app/src/components/TimeBomb.tsx). 정확히 언제
터질지는 아무도(선생님도) 알 수 없습니다 — setTimeout으로 한 번만 무작위 지연을 걸어두는
방식이라 서버 폴링 없이 클라이언트에서 그대로 동작합니다.app/src/components/TimerMatch.tsx). 숫자를 보이게/숨기게 고를 수 있고(숨기면 감으로만 맞혀야
하는 원래 방식), "공용 도전판"(참가자 구분 없이 반복 도전)과 "참가자별 순위"(한 명씩 도전하고
끝나면 오차 순 순위표) 중에서도 고를 수 있습니다. 시간 측정은 performance.now() 기준이라
브라우저 프레임 속도와 무관하게 정확합니다.app/src/components/StudentRosterPicker.tsx, app/src/lib/useGameTemplates.ts).game_templates.config (jsonb)에 각각 따로 저장합니다.
배경음악은 진행 중 반복 재생, 결과 사운드는 결과가 확정되는 순간 한 번 재생됩니다(둘 다 한 번도
안 건드린 게임은 결과 사운드가 "짜잔"으로 기본 재생됨 — resolveResultSound()).
app/src/lib/gameMusic.ts) — 어느 학원에서
써도 저작권 문제가 없도록.game-audio 공개 스토리지 버킷에
<academy_id>/<uuid>.<확장자>로 저장(008_ladder_order_games.sql). 업로드하는 음원의
저작권 책임은 업로드한 학원에 있습니다 (서비스가 기본 제공하는 음원이 아님).Vite 결과물은 정적 파일이라 그대로 올라갑니다. 백엔드는 Supabase에 있으니 서버는 필요 없습니다.
git init 부터)app 으로 지정 ← 저장소 루트가 아니라 app/ 폴더가 프로젝트입니다
.env.local은 git에 안 올라가므로 여기서 따로 넣어야 함)
VITE_SUPABASE_URLVITE_SUPABASE_ANON_KEYDashboard → Authentication → URL Configuration
https://내프로젝트.vercel.app안 하면 이메일 인증 링크나 비밀번호 재설정 링크가 localhost:3000으로 가버립니다.
app/vercel.json — SPA rewrite 설정.
react-router를 쓰기 때문에, 이게 없으면 /board 같은 주소에서 새로고침 시 404가 납니다.
(Vercel이 실제 파일을 찾다가 없으니 404를 내는데, 모든 경로를 index.html로 넘겨야 라우터가 처리합니다)네비게이션의 게임 메뉴를 누르면 게임 목록(/games)이 뜨고, 카드를 눌러 개별 게임으로 들어갑니다.
앞으로 추가되는 미니게임(사다리, 빙고 …)은 전부 이 목록 안에 카드로 들어갑니다 —
네비게이션에 메뉴가 하나씩 늘어나지 않습니다.
game_templates 테이블을 그대로 씁니다(스키마 변경 없음).
설정 → 학원 로고에서 이미지를 올리면 헤더 좌측 브랜드 마크(기본은 🐷)가 학원 로고로 바뀝니다.
logos/<academy_id>/logo.png 로 학원마다 고정. RLS로 자기 학원 폴더에만 쓸 수 있고, 읽기는 공개 버킷이라 누구나 가능합니다 (로그인 화면 등에서도 문제없이 표시하려면 공개가 맞습니다).스마트폰에서 앱처럼 아이콘으로 실행할 수 있습니다.
app/public/manifest.webmanifest, icon-192/512.png, apple-touch-icon.png)을
붙여서 Android Chrome은 자체 설치 배너를 띄우고, iOS Safari는 홈 화면 추가 시 아이콘/이름이 제대로 잡힙니다.InstallBanner 컴포넌트가 기기를 감지해서 안내합니다:
localStorage에 기억해서 다시 안 뜹니다.game_templates.game_type 값만 늘리면 되는 구조.redemptions 활용)