ESP32-S3 Bench Guide
ESP32‑S3 N16R8 벤치 가이드: 실시간 핀아웃, 강의, 시뮬레이터 및 벤치 로그
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
이해에서 창작까지, AI 교육을 켜다 — AI Bridge 교육 모델(이해·활용·구현·창작)의 차시별 수업 자료를 운영하는 교육 사이트. • 전체 명세: docs/SPEC.md • 작업 규칙: CLAUDE.md Next.js 15 (App Router, TypeScript) · Tailwind CSS 4 + shadcn/ui · Pretendard · Supabase(Postgres/Auth/Storage) · Claude API · Vitest · Playwright Supabase 키가 없어도 사이트는 뜬다(빈 사이트). 연결 상태는 /api/health에서 확인한다. 커밋 전에 lint, typecheck, test가 모두 통과해야 한다.README 전문 보기 ↗
이해에서 창작까지, AI 교육을 켜다 — AI Bridge 교육 모델(이해·활용·구현·창작)의 차시별 수업 자료를 운영하는 교육 사이트.
docs/SPEC.mdCLAUDE.mdNext.js 15 (App Router, TypeScript) · Tailwind CSS 4 + shadcn/ui · Pretendard · Supabase(Postgres/Auth/Storage) · Claude API · Vitest · Playwright
npm install
cp .env.example .env.local # 값 채우기 (아래 "환경변수" 참고)
npm run dev # http://localhost:3000
Supabase 키가 없어도 사이트는 뜬다(빈 사이트). 연결 상태는 /api/health에서 확인한다.
/api/health 응답의 supabase | 의미 |
|---|---|
not_configured | Supabase 환경변수가 비어 있음 |
ok | Supabase Auth 서버 응답 정상 |
error | 주소·키가 틀렸거나 접속 불가(방화벽 확인) — HTTP 503 |
| 명령 | 설명 |
|---|---|
npm run dev | 개발 서버 |
npm run build / npm start | 운영 빌드 / 실행 |
npm run lint | ESLint |
npm run typecheck | TypeScript 검사 |
npm test | Vitest 단위 테스트 |
npm run test:e2e | Playwright E2E (빌드 후 서버를 띄워 실행) |
npm run format | Prettier 정리 |
커밋 전에 lint, typecheck, test가 모두 통과해야 한다.
npx playwright install chromium
sudo npx playwright install-deps chromium # 브라우저 실행용 시스템 라이브러리 (최초 1회)
개발 컨테이너(
.devcontainer)는 sudo가 막혀 있어install-deps를 할 수 없다. 컨테이너에서 E2E를 돌리려면 Dockerfile에RUN npx -y playwright@<버전> install-deps chromium(root 단계)을 추가하고 재빌드하거나, 호스트에서 실행한다. 이미 떠 있는 서버를 대상으로 하려면PLAYWRIGHT_BASE_URL=http://localhost:3000 npm run test:e2e.
인증·RLS E2E(e2e/auth.spec.ts, e2e/rls.spec.ts)
.env.local의 Supabase 값(서버 전용 키 포함)으로 개발용 원격 프로젝트에 접속한다. 운영 프로젝트 키로 돌리지 않는다.generateLink)로 로그인 코드를 받아 세션 쿠키를 넣고, 보호자 동의 토큰도 테스트가 직접 만든다.e2e+<랜덤>@example.com 계정을 만들고 테스트가 끝나면 지운다. 중간에 끊기면 대시보드 Authentication → Users에서 e2e+로 검색해 지운다.npm run dev를 두 개 동시에 띄우지 않는다. .next 캐시를 같이 써서 Server Action을 찾지 못하는 오류(UnrecognizedActionError)가 날 수 있다. 이런 오류가 나면 개발 서버를 끄고 .next를 지운 뒤 다시 띄운다..env.example 참고. .env* 파일은 커밋하지 않는다(.env.example만 예외).
| 변수 | 공개 | 설명 |
|---|---|---|
NEXT_PUBLIC_SITE_URL | O | 사이트 주소. 도메인은 코드에 쓰지 않고 이 값으로만 참조 |
NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY | O | Supabase 프로젝트 URL·anon 키 |
SUPABASE_SERVICE_ROLE_KEY | 서버 전용 | RLS 우회 키. lib/supabase/admin.ts에서만 사용 |
RESEND_API_KEY, MAIL_FROM | 서버 전용 | 메일 발송(P1부터) |
ANTHROPIC_API_KEY, ANTHROPIC_MODEL, AI_PRICE_* | 서버 전용 | AI 토론 주제 생성(P7) |
ADMIN_EMAILS | 서버 전용 | 최초 관리자 이메일(쉼표 구분, P1부터) |
CRON_SECRET | 서버 전용 | Vercel Cron 인증(휴지통 자동 삭제, P3부터) |
서버 전용 값은 lib/env.server.ts(import "server-only")를 통해서만 읽는다. 클라이언트 컴포넌트에서 가져오면 빌드가 실패한다.
supabase.com에서 프로젝트 생성 (리전: Northeast Asia (Seoul) 권장)
개발 컨테이너 방화벽 등록 — cp .devcontainer/allowed-domains.example.txt .devcontainer/allowed-domains.txt 후 복사본에서 (복사본은 git 무시됨)
YOUR-PROJECT-REF.supabase.co → 대시보드 Project Settings → API → Project URL의 호스트YOUR-POOLER-HOST.pooler.supabase.com → 대시보드 Connect → Session pooler의 host.env.local에 NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY 입력
npm run dev 후 http://localhost:3000/api/health에서 "supabase":"ok" 확인
CLI 연결(마이그레이션용, P1부터)
npx supabase login
npx supabase link --project-ref <project-ref>
npx supabase migration new <name> # DB 변경은 항상 마이그레이션 파일로
npx supabase db push
DB 변경 적용: npx supabase db push 후 npx supabase gen types typescript --linked > types/database.ts
로컬 supabase/config.toml에는 반영되어 있지만, 원격 프로젝트는 대시보드에서 직접 맞춰야 한다.
| 항목 | 값 |
|---|---|
| Enable Email provider | 켬 |
| Confirm email | 끔(코드 입력 자체가 이메일 확인) |
| Email OTP Expiration | 600 (10분) |
| Email OTP Length | 6 |
Magic Link와 Confirm signup 두 템플릿의 제목을 [AI Bridge:ON] 로그인 코드로, 본문을 supabase/templates/otp-code.html 내용으로 바꾼다. 기본 템플릿은 링크를 보내므로, 바꾸지 않으면 코드가 아니라 링크가 간다.
http://localhost:3000, 운영 전환 시 NEXT_PUBLIC_SITE_URL과 같은 주소(docs/SPEC.md 2.2)smtp.resend.com, Port 465, User resend, Password = Resend API 키, 발신 주소 = MAIL_FROM의 주소.RESEND_API_KEY, MAIL_FROM).
npm run dev 터미널에 출력한다.otp_attempts). Supabase Auth API를 직접 호출하는 우회는 Supabase 자체의 검증 횟수 제한에 의존한다.ADMIN_EMAILS의 이메일은 첫 로그인 후 가입 정보 입력(/signup)을 마치면 관리자로 만들어진다(SPEC F-01). 이미 가입한 계정을 관리자로 바꾸는 것은 관리자 화면(P6)에서 한다.pg_cron 작업(purge-expired-accounts, 매시간)이 삭제한다.menus 테이블에서 읽는다. 관리자는 헤더의 내 계정 메뉴 → 관리자(/admin/menus)에서 추가·수정·숨기기·삭제·순서 변경을 한다. 바꾸면 헤더에 바로 반영된다(menus 캐시 태그 재검증).p2_menus에 들어 있어 npx supabase db push 때 함께 들어간다. 같은 slug가 이미 있으면 건너뛴다./admin/menus에서 수정 → 외부 주소 입력 → 보이기.admin, login, me, search 등)와 겹치는 값은 막는다(lib/menus/schema.ts의 RESERVED_SLUGS = DB check 제약).e2e/helpers/menus.ts). 공개 메뉴 트리가 캐시되어 DB에 직접 넣은 메뉴는 바로 보이지 않기 때문이다.Request rate limit reached). 5분 뒤 다시 돌리거나 --workers=2로 나눠 돌린다.e2e/menus.spec.ts)는 slug가 e2e-로 시작하는 메뉴를 만들고 지운다. 중간에 끊기면 Table Editor에서 e2e-로 시작하는 메뉴를 지운다(하위 메뉴부터).테이블: posts(series 문서·board 글), post_revisions(게시물당 최근 20개), attachments. 파일은 Storage post-files 버킷(공개 읽기, 파일 최대 50MB·이미지 10MB, SVG·HTML 불가).
본문 보호: 비회원·회원은 posts.content·draft_* 컬럼을 DB에서 직접 읽을 수 없다(컬럼 권한). 관리자는 get_post_editor_content() RPC로 읽고, 공개 화면은 서버가 교사 전용 블록을 뺀 뒤 내려준다. 쿼리에서 select("*") 대신 컬럼을 나열한다.
공개 글 수정: 공개된 글을 고치면 draft_title·draft_content에만 저장되고, "변경 사항 공개"를 눌러야 사이트에 반영된다.
휴지통 자동 삭제: Vercel Cron이 매일 03:00(KST) /api/cron/purge-trash를 호출해 30일 지난 글과 파일을 지운다. Vercel 환경변수에 CRON_SECRET(16자 이상 무작위 값, 예: openssl rand -hex 32)을 넣어야 동작한다.
커스텀 블록: 콜아웃, YouTube, 교사 전용 박스(제목 아래 Tab으로 들여 쓴 블록이 박스 안 내용, 공개 본문·검색 평문에서 통째로 빠짐), 온라인 저지 문제 링크(문제 번호만 저장).
온라인 저지 문제 주소·홈 문구: 관리자 → 설정(/admin/settings)에서 바꾼다. 문제 주소를 넣으면 문제 버튼이 바로 활성화되고, 비우면 "준비 중"으로 돌아간다.
게시물 관리(/admin/posts): 메뉴·상태 필터, 제목 검색, 여러 개 선택 → 다른 메뉴로 이동(게시글↔게시판이면 경고, 주소가 겹치면 -2 등을 붙임)·휴지통, 복제(초안, 첨부 파일도 새 경로로 복사), 메뉴 하나만 고르면 차시 순서 드래그.
휴지통(/admin/trash): 복구·영구 삭제(파일 포함). 메뉴가 삭제된 글은 복구할 메뉴를 고른다.
게시물이 있는 메뉴를 삭제하면 "다른 메뉴로 옮긴 뒤 삭제" 또는 "함께 휴지통으로"를 고른다(DB도 휴지통에 없는 글이 있는 메뉴 삭제를 막는다).
에디터(/admin/posts/[id]): BlockNote 0.51.4(Mantine UI). 0.52부터는 협업용 선택 의존성(yjs v14 rc) 때문에 npm 설치가 실패해 버전을 고정했다. 블록 종류를 바꾸면 components/editor/schema.ts와 lib/posts/content.ts의 ALLOWED_BLOCK_TYPES를 함께 고친다.
attachments에 기록한다./메뉴, /메뉴/글, /그룹/하위메뉴, /그룹/하위메뉴/글(app/(site)/[...path]). 하위 메뉴는 그룹 아래에만 있으므로 주소가 겹치지 않는다.components/content/post-content.tsx)가 블록 JSON을 그린다. HTML 문자열을 넣지 않고, 코드 블록은 서버에서 구문 강조한다. 블록 종류를 추가하면 렌더러에도 추가한다.?preview=1, 에디터의 "미리보기"). 학생·비회원에게는 404.posts 태그). 관리자 화면에서 게시물을 바꾸면 바로 갱신된다. DB에서 직접 고친 내용은 최대 1시간 뒤에 반영되므로, 급하면 관리자 화면에서 아무 게시물이나 다시 공개하거나 설정을 저장한다./search): 제목·본문 평문 부분 일치(pg_trgm), 공개 글만, 교사 전용 박스 내용은 검색되지 않는다.icn1, vercel.json). Supabase(서울)와 가까워 응답이 빠르다..env.example의 변수 입력
NEXT_PUBLIC_SITE_URL: 도메인 연결 전에는 Vercel 기본 주소(https://<프로젝트>.vercel.app). Production 환경에서 비어 있으면 빌드가 실패한다. Preview 환경은 비워 두면 배포별 주소(NEXT_PUBLIC_VERCEL_URL)를 쓴다(Vercel 설정의 "Automatically expose System Environment Variables"가 켜져 있어야 함)main에 push할 때마다 자동 배포, PR마다 미리보기 배포/api/health 확인도메인(aibridgeon.com) 연결은 docs/SPEC.md 2.2 체크리스트를 따른다.
app/
(site)/ 공개 사이트 (헤더·푸터 레이아웃)
[menu]/ 메뉴 주소(/메뉴, /그룹/하위메뉴)
admin/ 관리자 화면 (admin 전용, menus = 메뉴 관리)
api/health/ 연결 상태 확인
components/
layout/ 헤더·푸터·로고·모바일 메뉴
ui/ shadcn/ui
lib/
env.ts 공개 환경변수 (zod 검증)
env.server.ts 서버 전용 환경변수
menus/ 메뉴 입력 검증·트리 변환·조회(캐시)
site.ts 사이트명·부제·AI Bridge 4단계
supabase/ client(브라우저) / server(쿠키 세션) / admin(service role) / middleware
supabase/ config.toml, migrations, seed.sql
types/database.ts Supabase 타입 (P1부터 `supabase gen types`로 생성)
e2e/ Playwright 테스트
app/globals.css에 정의. 로고 시안 확정 전 임시값이며, 확정 시 :root/.dark의 브랜드·단계 색상 블록만 바꾼다.
--brand → text-brand, bg-brand--stage-literacy(이해) / --stage-usage(활용) / --stage-coding(구현) / --stage-project(창작) → bg-stage-coding text-stage-coding-foreground 등container-site 유틸리티(모바일 좌우 여백 16px)components/layout/logo.tsx 한 곳에서 교체