boot-welcome-usb
v0.1.0
Published
EventBoot Welcome AOA Module
Maintainers
Readme
boot-welcome-usb
Android AOA(Android Open Accessory) 프로토콜을 통해 USB로 연결된 Mac/PC와 통신하는 Expo Native Module.
개요
WelcomeUSBFront 기능에서 사용되며, Android 태블릿(Accessory)이 Mac의 EventBoot-Desktop(Host)과 USB 케이블을 통해 QR 코드 접수 데이터를 주고받습니다.
Android 태블릿 (이 모듈) ──USB 케이블── Mac (eventboot-desktop)
Accessory 모드 Host 모드프로젝트 구조
boot-welcome-usb/
├── android/
│ ├── build.gradle
│ └── src/main/java/net/eventboot/welcomeusb/
│ ├── BootWelcomeUsbModule.kt # Expo Module 정의
│ ├── AoaAccessoryManager.kt # USB Accessory 연결 관리
│ └── AoaProtocol.kt # 메시지 프레이밍
├── src/
│ ├── index.ts # 모듈 export
│ ├── BootWelcomeUsb.types.ts # TypeScript 타입
│ ├── BootWelcomeUsbModule.ts # 네이티브 모듈 바인딩
│ └── useUsbAoa.ts # React Hook
├── example/ # 테스트용 예제 앱
├── build/ # TypeScript 빌드 산출물
├── expo-module.config.json
└── package.json개발 환경 설정
모듈 빌드
cd modules/boot-welcome-usb
# TypeScript 빌드
npm run build
# 빌드 산출물 삭제
npm run clean
# 린트
npm run lintexample 앱 실행
cd modules/boot-welcome-usb/example
npm install
npx expo run:androidexample 앱은
package.json의expo.autolinking.nativeModulesDir설정으로 상위 모듈을 자동 인식합니다.
eventboot 프로젝트에서 사용하기
1. 의존성 등록
package.json (프로젝트 루트)에 로컬 경로로 추가:
{
"dependencies": {
"boot-welcome-usb": "./modules/boot-welcome-usb"
}
}npm install2. Config Plugin 등록
app.json의 plugins 배열에 AOA Config Plugin이 등록되어 있어야 합니다:
{
"plugins": [
"./plugins/withAndroidAoaAccessory"
]
}이 플러그인은 Android Manifest에 USB Accessory 관련 설정을 자동으로 추가합니다.
3. 네이티브 빌드
Expo Native Module이므로 네이티브 빌드가 필요합니다. Expo Go에서는 동작하지 않습니다.
# 개발 빌드
eas build --profile development --platform android
# 또는 로컬 빌드
npx expo run:androidAPI 사용법
기본 함수
import BootWelcomeUsbModule, {
getConnectionState,
requestPermission,
sendData,
addListener,
} from 'boot-welcome-usb';
// 연결 상태 확인
const state = await getConnectionState();
// 'DISCONNECTED' | 'CONNECTED' | 'AOA_SWITCHING' | 'AOA_READY' | 'ERROR'
// USB 권한 요청
const granted = await requestPermission();
// 데이터 전송 (JSON 문자열)
const success = await sendData(JSON.stringify({
id: 'uuid-123',
type: 'REQUEST',
path: '/scan',
body: JSON.stringify({ qrcode: 'TICKET-001' }),
}));이벤트 리스너
import { addListener } from 'boot-welcome-usb';
// 연결 상태 변경
const sub1 = addListener('onConnectionStateChanged', (event) => {
console.log('상태:', event.state, event.message);
});
// 데이터 수신 (Host로부터 응답)
const sub2 = addListener('onDataReceived', (event) => {
const message = JSON.parse(event.messageJson);
console.log('수신:', message);
});
// 에러
const sub3 = addListener('onError', (event) => {
console.error('에러:', event.code, event.message);
});
// 정리
sub1.remove();
sub2.remove();
sub3.remove();React Hook
import { useUsbAoa } from 'boot-welcome-usb';
function MyComponent() {
const { connectionState, isConnected } = useUsbAoa();
return <Text>{isConnected ? '연결됨' : '미연결'}</Text>;
}useWelcomeUSBFront Hook (앱 내부)
실제 WelcomeUSBFront 화면에서는 이 훅을 사용합니다. 기존 HTTP 방식과 동일한 WelcomeDeskApiResult를 반환합니다:
import { useWelcomeUSBFront } from '@/hooks/welcome/useWelcomeUSBFront';
function WelcomeUSBScreen() {
const { connectionState, isConnected, sendQRCode, requestPermission } = useWelcomeUSBFront();
const handleScan = async (qrcode: string) => {
const result = await sendQRCode(qrcode);
// result: { customer, receptionResult, isError, errorMessage }
};
}통신 프로토콜
메시지 프레이밍
USB 스트림은 메시지 경계가 없으므로 Length-Prefix 프레이밍을 사용합니다:
+-------------------+------------------------+
| Length (4 bytes) | JSON Payload (N bytes) |
| Big-Endian uint32 | UTF-8 encoded |
+-------------------+------------------------+메시지 형식
interface AoaMessage {
id: string; // UUID (요청-응답 매칭)
type: 'REQUEST' | 'RESPONSE' | 'PING' | 'PONG';
path: string; // "/scan", "/ping"
body?: string; // JSON 문자열
}요청/응답 예시
QR 스캔 요청 (Android → Mac):
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"type": "REQUEST",
"path": "/scan",
"body": "{\"qrcode\":\"TICKET-12345\"}"
}QR 스캔 응답 (Mac → Android):
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"type": "RESPONSE",
"path": "/scan",
"body": "{\"data\":{\"customer\":{\"name\":\"홍길동\"},\"receptionResult\":\"OK_IN\"}}"
}테스트 환경
- Android 에뮬레이터: USB Accessory 테스트 불가. 실기기 필수.
- ADB 충돌: AOA 모드 전환 시 ADB 연결이 끊어질 수 있음.
adb kill-server후 테스트 권장. - Mac에서 EventBoot-Desktop 실행 필수: USB Host 역할을 하는
modules/eventboot-desktopElectron 앱이 Mac에서 실행 중이어야 함.
