[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -0,0 +1,347 @@
|
||||
---
|
||||
id: web-webtransport-hid-usb
|
||||
title: WebTransport / WebHID / WebUSB / WebSerial
|
||||
category: Coding
|
||||
status: draft
|
||||
source_trust_level: B
|
||||
verification_status: conceptual
|
||||
created_at: 2026-05-09
|
||||
updated_at: 2026-05-09
|
||||
tags: [web, transport, hardware, vibe-coding]
|
||||
tech_stack: { language: "TS", applicable_to: ["Frontend"] }
|
||||
applied_in: []
|
||||
aliases: [WebTransport, WebHID, WebUSB, WebSerial, Web Bluetooth, Web NFC, hardware API]
|
||||
---
|
||||
|
||||
# WebTransport / WebHID / WebUSB
|
||||
|
||||
> Browser 가 native-like 영역. **WebTransport (HTTP/3 stream), WebHID (game controller, MIDI), WebUSB (firmware flash), WebSerial (Arduino)**.
|
||||
|
||||
## 📖 핵심 개념
|
||||
- WebTransport: WebSocket 보다 빠름 (HTTP/3, UDP).
|
||||
- WebHID: human interface device.
|
||||
- WebUSB: 임의 USB.
|
||||
- WebSerial: serial port.
|
||||
|
||||
## 💻 코드 패턴
|
||||
|
||||
### WebTransport
|
||||
```ts
|
||||
const transport = new WebTransport('https://server.example.com/wt');
|
||||
await transport.ready;
|
||||
|
||||
// Bidirectional stream
|
||||
const stream = await transport.createBidirectionalStream();
|
||||
const writer = stream.writable.getWriter();
|
||||
await writer.write(new TextEncoder().encode('hello'));
|
||||
await writer.close();
|
||||
|
||||
const reader = stream.readable.getReader();
|
||||
const { value } = await reader.read();
|
||||
console.log(new TextDecoder().decode(value));
|
||||
```
|
||||
|
||||
→ HTTP/3 + QUIC. Multiple stream 가 1 connection.
|
||||
|
||||
### Datagram (unreliable, fast)
|
||||
```ts
|
||||
const writer = transport.datagrams.writable.getWriter();
|
||||
await writer.write(new Uint8Array([1, 2, 3]));
|
||||
|
||||
const reader = transport.datagrams.readable.getReader();
|
||||
const { value } = await reader.read();
|
||||
```
|
||||
|
||||
→ UDP-like. Game / WebRTC alternative.
|
||||
|
||||
### WebTransport server (Node)
|
||||
```ts
|
||||
// 일부 server library 만 지원
|
||||
import { Http3Server } from '@fails-components/webtransport';
|
||||
|
||||
const server = new Http3Server({ port: 4433, host: '0.0.0.0' });
|
||||
server.startServer();
|
||||
|
||||
const sessions = await server.sessionStream('/wt');
|
||||
for await (const session of sessions) {
|
||||
// ... handle
|
||||
}
|
||||
```
|
||||
|
||||
→ HTTP/3 stack 필요. Bun / Node 가 부분 지원.
|
||||
|
||||
### WebHID (game controller)
|
||||
```ts
|
||||
const devices = await navigator.hid.requestDevice({
|
||||
filters: [{ vendorId: 0x054c }], // Sony
|
||||
});
|
||||
|
||||
const device = devices[0];
|
||||
await device.open();
|
||||
|
||||
device.addEventListener('inputreport', (e) => {
|
||||
const { data, reportId } = e;
|
||||
// Parse report (game controller state)
|
||||
});
|
||||
```
|
||||
|
||||
### MIDI controller
|
||||
```ts
|
||||
const access = await navigator.requestMIDIAccess();
|
||||
|
||||
for (const input of access.inputs.values()) {
|
||||
input.onmidimessage = (e) => {
|
||||
const [status, note, velocity] = e.data;
|
||||
// Note on, note off, ...
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### WebUSB (firmware update)
|
||||
```ts
|
||||
const device = await navigator.usb.requestDevice({
|
||||
filters: [{ vendorId: 0x2341 }], // Arduino
|
||||
});
|
||||
|
||||
await device.open();
|
||||
await device.selectConfiguration(1);
|
||||
await device.claimInterface(0);
|
||||
|
||||
// Send
|
||||
await device.transferOut(1, new TextEncoder().encode('hello'));
|
||||
|
||||
// Receive
|
||||
const r = await device.transferIn(2, 64);
|
||||
console.log(new Uint8Array(r.data!.buffer));
|
||||
```
|
||||
|
||||
→ Sketch upload, firmware flash.
|
||||
|
||||
### WebSerial (Arduino, ESP32)
|
||||
```ts
|
||||
const port = await navigator.serial.requestPort();
|
||||
await port.open({ baudRate: 9600 });
|
||||
|
||||
const writer = port.writable!.getWriter();
|
||||
await writer.write(new TextEncoder().encode('led on\n'));
|
||||
|
||||
const reader = port.readable!.getReader();
|
||||
while (true) {
|
||||
const { value, done } = await reader.read();
|
||||
if (done) break;
|
||||
console.log(new TextDecoder().decode(value));
|
||||
}
|
||||
```
|
||||
|
||||
### Web Bluetooth
|
||||
```ts
|
||||
const device = await navigator.bluetooth.requestDevice({
|
||||
filters: [{ services: ['heart_rate'] }],
|
||||
});
|
||||
|
||||
const server = await device.gatt!.connect();
|
||||
const service = await server.getPrimaryService('heart_rate');
|
||||
const char = await service.getCharacteristic('heart_rate_measurement');
|
||||
|
||||
await char.startNotifications();
|
||||
char.addEventListener('characteristicvaluechanged', (e) => {
|
||||
const value = (e.target as any).value;
|
||||
console.log('heart rate:', value.getUint8(1));
|
||||
});
|
||||
```
|
||||
|
||||
### Web NFC (Android)
|
||||
```ts
|
||||
if ('NDEFReader' in window) {
|
||||
const ndef = new NDEFReader();
|
||||
await ndef.scan();
|
||||
|
||||
ndef.onreading = ({ message }) => {
|
||||
for (const r of message.records) {
|
||||
console.log(r.recordType, r.data);
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
→ Android Chrome 만.
|
||||
|
||||
### Permission
|
||||
```
|
||||
모든 = user gesture (click) 필수.
|
||||
사용자 가 device picker 에서 grant.
|
||||
HTTPS 만.
|
||||
```
|
||||
|
||||
### Use case
|
||||
```
|
||||
WebTransport: WebSocket 의 modern, low-latency game / streaming.
|
||||
WebHID: Game controller, MIDI, custom HID.
|
||||
WebUSB: Hardware programmer, firmware tool.
|
||||
WebSerial: Arduino IDE in browser, debugging.
|
||||
Web Bluetooth: IoT, fitness tracker.
|
||||
Web NFC: Tag reader, payment (Android).
|
||||
```
|
||||
|
||||
### Browser support
|
||||
```
|
||||
WebTransport: Chrome 97+, FF / Safari 가 부분.
|
||||
WebHID: Chrome 89+, Edge.
|
||||
WebUSB: Chrome 61+. FF / Safari 가 안 (security 우려).
|
||||
WebSerial: Chrome 89+.
|
||||
Web Bluetooth: Chrome 56+.
|
||||
Web NFC: Chrome Android 81+.
|
||||
```
|
||||
|
||||
→ 대부분 Chrome / Edge / Opera 만.
|
||||
|
||||
### Permission Policy
|
||||
```html
|
||||
<!-- iframe 가 사용 -->
|
||||
<iframe allow="hid; usb; serial; bluetooth"></iframe>
|
||||
```
|
||||
|
||||
### Reconnect
|
||||
```ts
|
||||
// USB
|
||||
navigator.usb.addEventListener('connect', (e) => {
|
||||
// device 가 다시 connected
|
||||
});
|
||||
navigator.usb.addEventListener('disconnect', (e) => {});
|
||||
|
||||
// 같은 device 가 자동 reconnect 안 됨 — 사용자 picker 다시.
|
||||
```
|
||||
|
||||
### Persistent permission
|
||||
```ts
|
||||
// 이전 grant 된 device list
|
||||
const devices = await navigator.hid.getDevices();
|
||||
for (const d of devices) {
|
||||
if (!d.opened) await d.open();
|
||||
}
|
||||
```
|
||||
|
||||
→ User 가 picker 안 다시 — handle 가 영구.
|
||||
|
||||
### 함정
|
||||
```
|
||||
- Chrome 만 = userbase 한계.
|
||||
- HID parsing 어려움 (binary).
|
||||
- Connection drop 자주.
|
||||
- Sandbox 안에서만 (system file system 안 됨).
|
||||
- HTTPS only.
|
||||
```
|
||||
|
||||
### Native vs Web
|
||||
```
|
||||
Web:
|
||||
- Distribution easy
|
||||
- 자동 update
|
||||
- 작은 surface (특정 device 만)
|
||||
|
||||
Native:
|
||||
- 큰 권한
|
||||
- Performance
|
||||
- 모든 device
|
||||
|
||||
→ 작은 use case = Web. 큰 = Native.
|
||||
```
|
||||
|
||||
### WebGPU (powerful)
|
||||
```ts
|
||||
const adapter = await navigator.gpu.requestAdapter();
|
||||
const device = await adapter!.requestDevice();
|
||||
// ... shader, pipeline, render
|
||||
```
|
||||
|
||||
→ GPU 사용. ML / 3D.
|
||||
|
||||
→ [[Frontend_WebGPU_Patterns]].
|
||||
|
||||
### File over USB (e.g. ADB)
|
||||
```ts
|
||||
// Chrome OS 의 Android device debug
|
||||
const device = await navigator.usb.requestDevice({
|
||||
filters: [{ classCode: 0xff, subclassCode: 0x42 }], // ADB
|
||||
});
|
||||
```
|
||||
|
||||
→ Chrome OS 의 Android Studio web 같은 것.
|
||||
|
||||
### Audio / video device
|
||||
```ts
|
||||
// MediaDevices API (다름)
|
||||
const stream = await navigator.mediaDevices.getUserMedia({
|
||||
audio: { deviceId: '...' },
|
||||
video: { deviceId: '...' },
|
||||
});
|
||||
```
|
||||
|
||||
→ Camera, microphone, screen share.
|
||||
|
||||
### WebRTC vs WebTransport
|
||||
```
|
||||
WebRTC:
|
||||
- Peer-to-peer
|
||||
- DTLS + SRTP
|
||||
- 복잡 setup (signaling, ICE)
|
||||
|
||||
WebTransport:
|
||||
- Client-server
|
||||
- HTTP/3 + QUIC
|
||||
- Simple
|
||||
|
||||
→ Game / streaming = WebTransport. Conferencing = WebRTC.
|
||||
```
|
||||
|
||||
### Browser WASM 가 native-like
|
||||
```
|
||||
+ 위 device API
|
||||
+ WebGPU (GPU)
|
||||
+ WASM (CPU)
|
||||
+ File System Access (file)
|
||||
+ Service Worker (background)
|
||||
+ Push API (notification)
|
||||
+ Periodic Sync
|
||||
|
||||
→ "Web 가 Win32 / macOS API 와 비슷" 가 modern.
|
||||
```
|
||||
|
||||
### Production 사용
|
||||
- StackBlitz / WebContainer — Node in browser
|
||||
- Figma — file, hardware (palette device)
|
||||
- Photopea — Photoshop in browser
|
||||
- VS Code Web — File system + ext
|
||||
- Adafruit — Arduino in browser (WebSerial + WebUSB)
|
||||
|
||||
## 🤔 의사결정 기준
|
||||
| 작업 | 추천 |
|
||||
|---|---|
|
||||
| Low-latency game/stream | WebTransport |
|
||||
| Game controller / MIDI | WebHID |
|
||||
| Firmware flash | WebUSB |
|
||||
| Arduino REPL | WebSerial |
|
||||
| IoT 통신 | Web Bluetooth |
|
||||
| NFC tag | Web NFC (Android) |
|
||||
| Cross-browser 필요 | WebSocket / Bluetooth fallback X |
|
||||
| GPU compute | WebGPU |
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **모든 browser 가정**: Chrome 만 흔함.
|
||||
- **User gesture 없이**: error.
|
||||
- **HTTPS 안**: blocked.
|
||||
- **HID report parse 무기준**: binary 잘못.
|
||||
- **Disconnect handling 없음**: hang.
|
||||
- **Persistent permission 무시**: 매번 picker.
|
||||
- **WebTransport server 없이**: fail.
|
||||
|
||||
## 🤖 LLM 활용 힌트
|
||||
- WebTransport = HTTP/3 stream + datagram (WebSocket 의 모던).
|
||||
- WebHID / WebUSB / WebSerial = Chrome 만.
|
||||
- User gesture + HTTPS 의무.
|
||||
- Native vs Web — 작은 use case.
|
||||
|
||||
## 🔗 관련 문서
|
||||
- [[Web_WebRTC_Realtime]]
|
||||
- [[Web_WebSocket_Reconnect]]
|
||||
- [[Frontend_WebGPU_Patterns]]
|
||||
Reference in New Issue
Block a user