root 9a9967bed8 feat: mock payment flow + flash token page (test mode)
- Mock purchase: order create → mock-pay → FlashToken issued instantly (no real billing)
- Flash page (/flash/:token): esp-web-tools integration, token state display, consume on complete
- Orders route: create/mock-pay/me/refund with full audit logging
- Flash route: GET validate, GET manifest (esp-web-tools compatible), POST consume
- MinIO file proxy (/api/files/*): browser CORS solved, firmware served through backend
- Schema: chipFamily on Project, flashOffset on ProjectFile
- ProjectNew: chipFamily selector + firmware flash offset option
- MyOrders: real order list with flash token status and buttons
- Dockerfile: prisma db push (no migration files needed for dev)
- TOSS_PAYMENT_GUIDE.md: step-by-step guide for real payment after business registration

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 06:18:19 +09:00
2026-05-17 03:27:30 +09:00
2026-05-17 03:27:30 +09:00

ESP32 Web Flasher

브라우저에서 ESP32 계열 마이컴에 펌웨어를 플래시하는 Docker 기반 웹 애플리케이션입니다.
별도 소프트웨어 설치 없이 Chrome/Edge 브라우저와 USB 케이블만으로 펌웨어를 구워 넣을 수 있습니다.

주요 특징

  • Web Serial API — 드라이버·CLI 도구 없이 브라우저 직접 시리얼 통신
  • esp-web-tools v10 (Espressif 공식) — 안정적인 웹 컴포넌트 기반 플래싱
  • 다중 바이너리 지원 — bootloader / partition table / application 개별 또는 병합 바이너리
  • 펌웨어 관리 — 업로드·목록·삭제, UUID 기반 REST API
  • 실시간 로그 — 색상 구분 시리얼 모니터 내장
  • Docker Compose — 단일 명령으로 백엔드(Node.js) + 프론트엔드(Nginx) 기동

지원 칩

칩 패밀리 지원 여부
ESP32-S3 ✓ 권장 (내장 USB-OTG 지원)
ESP32-S2
ESP32-C3
ESP32

브라우저 호환성

브라우저 지원
Chrome 89+
Edge 89+
Firefox ✗ (Web Serial 미지원)
Safari ✗ (Web Serial 미지원)

참고: HTTP(localhost 제외)에서는 Web Serial API가 동작하지 않습니다. 외부 배포 시 HTTPS가 필수입니다.

빠른 시작

사전 요구사항

  • Docker Engine 20.10+
  • Docker Compose v2+
  • Chrome 89+ 또는 Edge 89+

실행

git clone https://gitea.byunc.com/byun/webflash.git
cd webflash
docker compose up -d --build

브라우저에서 http://localhost:3100 접속

중지

docker compose down

펌웨어 데이터까지 삭제하려면:

docker compose down -v

사용 방법

1단계 — 시리얼 연결 확인

  1. ESP32를 USB 케이블로 PC에 연결
  2. 포트 연결 버튼 클릭 → 브라우저 포트 선택 대화상자에서 포트 선택
  3. VID/PID가 표시되고 Espressif(0x303A) 장치 여부 확인

2단계 — 펌웨어 업로드

펌웨어 업로드 탭에서:

  • 이름, 버전, 칩 패밀리 입력
  • .bin 파일 드래그&드롭 또는 클릭하여 선택
    • 병합 바이너리(merged.bin): 펌웨어 하나만 업로드
    • 분리 파일: bootloader(0x0000) + partitions(0x8000) + app(0x10000)
  • 서버에 업로드 클릭

3단계 — 플래시

플래시 탭에서:

  1. 목록에서 펌웨어 선택
  2. ESP32S3 플래시 실행 버튼 클릭
  3. esp-web-tools 대화상자에서 포트 선택 후 플래시 진행

플래시 파일 준비

Arduino IDE에서 내보내기

Sketch → Export Compiled Binary
→ 스케치 폴더에 .bin 파일 생성

병합 바이너리 생성 (권장)

esptool.py --chip esp32s3 merge_bin \
  -o merged.bin \
  0x0     bootloader.bin \
  0x8000  partitions.bin \
  0x10000 app.bin

PlatformIO 사용 시

pio run                      # 개별 바이너리 빌드
pio run --target mergebin    # 병합 바이너리 생성

프로젝트 구조

webflash/
├── backend/
│   ├── Dockerfile           # Node.js 20 Alpine 이미지
│   ├── package.json
│   └── server.js            # Express REST API 서버
├── frontend/
│   ├── Dockerfile           # Nginx Alpine 이미지
│   ├── nginx.conf           # 리버스 프록시 설정
│   ├── index.html           # 단일 페이지 앱 (한국어 UI)
│   ├── css/style.css        # 다크 테마 스타일
│   └── js/app.js            # Web Serial API 로직
└── docker-compose.yml

REST API

메서드 경로 설명
GET /api/health 헬스 체크
GET /api/firmware 펌웨어 목록 조회
POST /api/firmware/upload 펌웨어 업로드
GET /api/firmware/:id/manifest esp-web-tools 매니페스트 생성
DELETE /api/firmware/:id 펌웨어 삭제
GET /firmware/files/:filename 바이너리 파일 서빙

업로드 요청 예시

curl -X POST http://localhost:3100/api/firmware/upload \
  -F "name=My Firmware" \
  -F "version=1.0.0" \
  -F "chipFamily=ESP32-S3" \
  -F "firmware=@app.bin" \
  -F "bootloader=@bootloader.bin" \
  -F "partitions=@partitions.bin"

매니페스트 응답 예시

{
  "name": "My Firmware",
  "version": "1.0.0",
  "new_install_prompt_erase": true,
  "builds": [{
    "chipFamily": "ESP32-S3",
    "parts": [
      { "path": "/firmware/files/xxxx-bootloader.bin", "offset": 0 },
      { "path": "/firmware/files/xxxx-partitions.bin", "offset": 32768 },
      { "path": "/firmware/files/xxxx-app.bin",        "offset": 65536 }
    ]
  }]
}

환경 변수

변수 기본값 설명
PORT 3000 백엔드 포트
NODE_ENV production 실행 환경
ALLOWED_ORIGIN * CORS 허용 오리진

기술 스택

영역 기술
백엔드 Node.js 20, Express 4, Multer, UUID
프론트엔드 Vanilla JS, HTML5, CSS3
웹 서버 Nginx Alpine (리버스 프록시)
플래싱 esp-web-tools v10, Web Serial API
컨테이너 Docker, Docker Compose
저장소 파일 시스템 + JSON 메타데이터

보안 고려사항

  • 파일 업로드는 .bin 확장자 및 32 MB 크기 제한
  • XSS 방지를 위한 HTML 이스케이프 처리
  • 외부 배포 시 HTTPS 적용 필수
  • ESP32 Flash Encryption 활성화 권장 (펌웨어 덤프 방지)

동시 접속 및 상업 서비스 확장성

아키텍처 특성 — 서버는 플래싱에 관여하지 않음

플래싱은 브라우저가 Web Serial API를 통해 USB로 직접 처리합니다.
서버는 manifest JSON과 펌웨어 .bin 파일을 제공하는 역할만 담당합니다.

사용자 브라우저  →  서버에서 manifest + .bin 다운로드 (수초)
사용자 브라우저  →  USB → ESP32  (플래싱, 서버 무관)

병목 지점 분석

구분 서버 부하 한계
HTML/JS/CSS 서빙 Nginx — 매우 가벼움 수천 명 동시 가능
Manifest JSON ~1 KB, 순간적 수천 건/초
펌웨어 .bin 다운로드 ← 실질 병목 네트워크 대역폭
플래시 실행 서버 무관 (브라우저-USB) 무제한

대역폭 기준 동시 플래시 추정

merged.bin 16 MB 기준 (다운로드 시간 ~수초):

  내부망 (1 Gbps)       : 125 MB/s ÷ 16 MB ≈ 동시 7~8명 다운로드
                          다운로드는 수초면 끝나므로 실질 50~100명 처리 가능
  인터넷 (100 Mbps 업로드): 12.5 MB/s ÷ 16 MB ≈ 동시 1명 한계
  인터넷 (1 Gbps 업로드)  : 125 MB/s ÷ 16 MB ≈ 동시 7~8명

  app.bin 단독 (~1 MB) 사용 시 약 16배 여유

현재 구조의 한계 (MVP 수준)

항목 현재 상태 문제점
메타데이터 _metadata.json 파일 동시 쓰기 시 데이터 손상 위험
인증 없음 누구나 업로드·삭제 가능
HTTPS 없음 외부망에서 Web Serial API 불가
펌웨어 저장 로컬 Docker 볼륨 서버 장애 시 소실 가능
Rate Limiting 없음 무차별 다운로드 가능

상업 서비스 전환 로드맵

단기 — 필수 적용

  1. HTTPS 적용 — 외부 접속 시 Web Serial API 동작 필수 조건
  2. 인증 추가 — 구매자에게만 플래시 토큰 발급
  3. Rate Limiting — IP당 다운로드 횟수 제한 (nginx limit_req)

중기 — 확장성 확보

  1. 메타데이터 DB화 — JSON 파일 → SQLite 또는 PostgreSQL
  2. 펌웨어 스토리지 — 로컬 볼륨 → S3 호환 오브젝트 스토리지 (CDN 연동 가능)
  3. 플래시 이력 로깅 — 판매 증거, 환불 분쟁 대응용

장기 — 라이선스 모델

  1. 1회용 플래시 토큰 — 구매 1건 = 플래시 1회
  2. MAC 주소 바인딩 — 기기별 라이선스 잠금 (esp_efuse_mac_get_default())
  3. 결제 연동 — Stripe / 토스페이먼츠

권장 최종 아키텍처

[결제] → [라이선스 서버] → 1회용 토큰 발급
                              ↓
                       [웹 플래셔] → Web Serial → ESP32
                              ↓
                       [플래시 로그] → MAC 바인딩 → 라이선스 활성화

결론

현재 구조(내부망)로는 50~100명 동시 처리 무리 없음.
인터넷 상업 서비스는 HTTPS + 인증 + 스토리지 개선 3가지 선행 후 운영 권장.


라이선스

MIT

Description
No description provided
Readme 1.5 MiB
Languages
JavaScript 59.7%
HTML 33.4%
CSS 6.5%
Dockerfile 0.4%