드라이버 설치의 번거로움 없이, 브라우저 안에서 키패드, 게임 컨트롤러, 특수 장치와 통신하는 방법을 알아봅니다. Web HID API의 핵심 개념부터 보안 제어, 실무 예제와 한계점까지 완벽하게 파헤칩니다.
과거 웹 브라우저는 키보드, 마우스, 게임패드(Gamepad API)를 제외한 특수 USB 장치나 HID(Human Interface Device) 기기와 직접 통신할 수 없었습니다. 사용자가 커스텀 매크로 키패드나 특수 컨트롤러를 연결하려면 반드시 제조사가 제공하는 별도의 네이티브 프로그램을 설치해야 했습니다.
Web HID API는 웹 애플리케이션이 운영체제의 HID 드라이버 레이어와 직접 소통할 수 있도록 돕는 차세대 웹 표준 API입니다. 이제 사용자는 복잡한 프로그램 설치 없이, 웹사이트 방문 후 버튼 클릭 한 번으로 자신만의 하드웨어를 브라우저와 연동할 수 있습니다. 예를 들어, 사용자가 커스텀 매크로 패드를 PC에 연결하고 웹 페이지에서 키 배치나 RGB 백라이트 색상을 즉시 변경하는 시나리오가 가능해집니다.
기존 웹 환경에서 물리 장치와 통신하려면 다음과 같은 복잡한 파이프라인을 거쳐야 했습니다.
msi, exe), macOS(dmg), Linux(AppImage, deb) 등 OS별 빌드를 각각 관리해야 하는 극심한 유지보수 부담이 존재합니다.아래 표는 전통적인 데스크톱 연동 방식과 Web HID API 연동 방식의 상세 스펙을 비교한 것입니다.
| 구분 | 전통적 데스크톱 연동 (Electron/C++) | Web HID API 연동 (Browser) |
|---|---|---|
| 설치 과정 | 필수 (msi, dmg 등 대용량 파일 다운로드) | 불필요 (웹 브라우저 즉시 구동) |
| 업데이트 반영 | 수동 다운로드 및 재부팅 필요 | 웹 서버 배포 즉시 자동 반영 |
| 크로스 플랫폼 | OS별 소스 분기 및 별도 빌드 필수 | 단일 웹 소스코드로 모든 OS 대응 |
| 보안 모델 | OS 루트 권한 의존 (잠재적 시스템 위험) | 브라우저 샌드박스 및 명시적 권한 모델 |
| 메모리 점유율 | 앱 당 수백 MB (Chromium 임베디드 등) | 브라우저 탭 내부 구동으로 경량화 |
Web HID API는 USB 및 블루투스 스펙 중 HID 클래스를 준수하는 장치들과 통신합니다. 브라우저는 악성 스크립트가 사용자의 장치를 무단으로 탈취하는 것을 막기 위해 다음과 같은 엄격한 원칙으로 동작합니다.
navigator.hid.requestDevice)를 거쳐 브라우저가 제공하는 시스템 장치 선택 팝업을 띄워야 합니다.Input Report: 장치에서 PC(브라우저)로 들어오는 데이터 (예: 키 입력, 조이스틱 좌표)Output Report: PC(브라우저)에서 장치로 나가는 제어 데이터 (예: LED 점등, 럼블 모터 작동)Feature Report: 설정 값 읽기 및 쓰기를 위한 양방향 패킷Web HID API를 사용하기 위해서는 현대적인 모던 브라우저(Chrome 89+, Edge 등 Chromium 기반 엔진) 환경이 필수적입니다. 또한 웹 보안 정책(Secure Contexts)에 따라 **반드시 HTTPS 환경(또는 로컬 개발을 위한 localhost)**에서만 동작합니다. 프로덕션 환경에서는 HTTP 접속 시 객체 자체가 정의되지 않아 에러가 발생합니다.
현재 브라우저가 해당 API를 지원하는지 검증하고 초기화하는 실무 코드 예시는 다음과 같습니다:
// 브라우저의 Web HID API 지원 여부 검증
function checkHidSupport() {
if ('hid' in navigator) {
console.log('✅ Web HID API를 지원하는 브라우저입니다.');
return true;
} else {
console.warn('❌ 이 브라우저는 Web HID API를 지원하지 않습니다. Chromium 기반 브라우저를 이용해주세요.');
return false;
}
}
if (checkHidSupport()) {
// 초기화 로직 실행
console.log('HID 모듈 로딩 완료');
}사용자가 웹 페이지 내의 ‘장치 연결’ 버튼을 클릭했을 때, 특정 벤더 ID(VID)와 제품 ID(PID)를 필터링하여 브라우저의 장치 선택 팝업을 띄우고 권한을 획득하는 실무 코드입니다.
async function connectCustomHardware() {
try {
// 특정 제조사(Vendor ID: 0x1234, 예: 커스텀 키보드 제조사)의 장치만 필터링
const filters = [
{
vendorId: 0x1234,
productId: 0x5678, // 특정 모델 ID (생략 시 해당 벤더의 모든 기기 허용)
usagePage: 0x01, // Generic Desktop Controls
usage: 0x06 // Keyboard
}
];
// 사용자에게 시스템 장치 선택 프롬프트 표시 (사용자 인터랙션 필수)
const devices = await navigator.hid.requestDevice({ filters });
if (devices.length === 0) {
console.log('사용자가 장치를 선택하지 않았습니다.');
return null;
}
const device = devices[0];
// 장치가 이미 열려있지 않다면 세션 오픈
if (!device.opened) {
await device.open();
}
console.log(`🎉 장치 연결 성공: ${device.productName} (VID: ${device.vendorId}, PID: ${device.productId})`);
return device;
} catch (error) {
console.error('🚨 장치 연결 중 치명적 오류 발생:', error);
throw error;
}
}장치가 성공적으로 연결된 후, 사용자가 물리 버튼을 누르거나 다이얼을 돌릴 때 발생하는 inputreport 이벤트를 실시간으로 리스닝하여 바이트 데이터를 파싱하는 방법입니다.
function attachInputReportListener(device) {
device.addEventListener('inputreport', (event) => {
const { device: sourceDevice, reportId, data } = event;
// DataView 객체를 사용하여 바이트 배열을 안전하게 파싱
// 예: 첫 번째 바이트는 키 상태, 두 번째 바이트는 조도 센서 값
const keyState = data.getUint8(0);
const sensorValue = data.getUint16(1, true); // 리틀 엔디언 적용
console.log(`📥 [Input Report] ID: ${reportId} | 키 상태: 0x${keyState.toString(16)} | 센서: ${sensorValue}`);
// UI 업데이트 함수 호출
updateDashboardUI({ keyState, sensorValue });
});
}웹 대시보드 조작을 통해 하드웨어로 제어 신호(예: RGB LED 색상 변경, LCD 화면 텍스트 전송 등)를 보낼 때는 sendReport 메서드를 사용합니다. 장치마다 요구하는 reportId와 페이로드 규격이 다르므로 제조사 스펙 문서를 참고해야 합니다.
async function sendLedCommandToDevice(device, red, green, blue) {
const reportId = 0x01; // 하드웨어 스펙에 정의된 LED 제어 리포트 ID
// 페이로드 데이터 구성 (예: Report ID 제외한 데이터 바이트 배열)
const data = new Uint8Array([
red, // Red 채널 (0-255)
green, // Green 채널 (0-255)
blue, // Blue 채널 (0-255)
0x01 // 모드 설정 플래그 (0x01: 고정 점등)
]);
try {
// 지정된 Report ID로 바이트 데이터 전송
await device.sendReport(reportId, data);
console.log(`📤 하드웨어로 LED 제어 명령 전송 완료 (RGB: ${red}, ${green}, ${blue})`);
} catch (error) {
console.error('🚨 하드웨어 데이터 전송 실패:', error);
}
}사용자가 USB 케이블을 물리적으로 뽑거나 전원을 강제로 차단했을 때, 브라우저는 전역 navigator.hid 객체를 통해 disconnect 이벤트를 발생시킵니다. 이를 감지하여 애플리케이션 상태를 안전하게 초기화할 수 있습니다.
let activeConnectedDevice = null;
// 연결 해제 이벤트 글로벌 리스너 등록
navigator.hid.addEventListener('disconnect', (event) => {
const disconnectedDevice = event.device;
if (activeConnectedDevice && disconnectedDevice === activeConnectedDevice) {
console.warn(`⚠️ 경고: 연결된 장치(${disconnectedDevice.productName})가 분리되었습니다.`);
// 애플리케이션 상태 초기화 및 UI 비활성화
activeConnectedDevice = null;
resetDashboardUI();
}
});
function resetDashboardUI() {
const statusBadge = document.getElementById('status-badge');
statusBadge.textContent = '연결 끊김';
statusBadge.className = 'badge-disconnected';
}실무 환경에서 커스텀 매크로 키패드의 펌웨어 설정을 웹 대시보드에서 실시간으로 제어하고 입력을 시각화하는 통합 애플리케이션 구조 예시입니다.
<!-- 실무 대시보드 HTML 구조 예시 -->
<div id="app-container">
<button id="connect-btn">하드웨어 키패드 연결</button>
<div id="status-badge" class="badge-disconnected">연결 대기 중</div>
<div id="log-console"></div>
</div>// 프론트엔드 연동 로직
document.addEventListener('DOMContentLoaded', () => {
const connectBtn = document.getElementById('connect-btn');
let currentDevice = null;
connectBtn.addEventListener('click', async () => {
try {
currentDevice = await connectCustomHardware();
if (currentDevice) {
// UI 상태 업데이트
document.getElementById('status-badge').textContent = `연결됨: ${currentDevice.productName}`;
document.getElementById('status-badge').className = 'badge-connected';
// 입력 리스너 활성화
attachInputReportListener(currentDevice);
// 초기화 명령 전송 (예: 백라이트 파란색 설정)
await sendLedCommandToDevice(currentDevice, 0, 0, 255);
}
} catch (err) {
console.error('대시보드 초기화 실패:', err);
}
});
});Web HID API를 도입할 때 개발자들이 흔히 빠지는 함정과 보안 제약 사항은 다음과 같습니다.
navigator.hid.requestDevice() 함수는 반드시 click, touchend 등 사용자의 명시적인 DOM 이벤트 핸들러 내부 콜백 스택에서만 호출되어야 합니다. setTimeout이나 비동기 통신 이후에 호출하면 보안 예외(SecurityError)가 발생합니다.requestDevice)이 수반되어야 합니다.inputreport 이벤트 수신과 sendReport 메서드 호출을 통해 손쉽게 양방향 통신 시스템을 구축할 수 있습니다.✨ 한 줄 요약 Web HID API는 복잡한 드라이버 설치 장벽을 허물고, 웹 브라우저를 통해 안전하고 강력한 물리 하드웨어 제어 경험을 제공하는 최신 웹 표준입니다.
