이미지 로딩 중...
AI Generated
2025. 11. 9. · 2 Views
AI 음성 3편 음성 인식 구현 완벽 가이드
웹 브라우저에서 Web Speech API를 활용한 실시간 음성 인식 구현 방법을 다룹니다. 음성을 텍스트로 변환하는 기본 원리부터 실무에서 활용할 수 있는 고급 기법까지, 음성 인식 기술의 모든 것을 알아봅니다.
목차
- Web Speech API 기초
- SpeechRecognition 객체 생성
- 실시간 음성 인식 구현
- 음성 인식 이벤트 처리
- 언어 설정과 다국어 지원
- 음성 명령 시스템 구현
- 음성 인식 정확도 향상
- 모바일 음성 인식 대응
1. Web Speech API 기초
시작하며
여러분이 웹 애플리케이션을 만들 때 사용자가 키보드 대신 음성으로 입력할 수 있다면 얼마나 편리할까요? 특히 모바일 환경이나 핸즈프리 상황에서 음성 입력은 필수적인 기능이 되었습니다.
많은 개발자들이 음성 인식을 구현하려면 복잡한 AI 모델을 학습시키거나 유료 API를 사용해야 한다고 생각합니다. 하지만 실제로는 브라우저에 이미 강력한 음성 인식 기능이 내장되어 있습니다.
바로 이럴 때 필요한 것이 Web Speech API입니다. 이 API를 사용하면 추가 라이브러리 없이 브라우저만으로 실시간 음성 인식 기능을 구현할 수 있습니다.
개요
간단히 말해서, Web Speech API는 브라우저에서 음성을 텍스트로 변환하거나 텍스트를 음성으로 변환할 수 있는 표준 웹 API입니다. 이 API가 필요한 이유는 사용자 경험을 크게 향상시킬 수 있기 때문입니다.
음성 검색, 음성 메모, 음성 명령 등 다양한 기능을 구현할 수 있습니다. 예를 들어, 고객 센터 챗봇에서 음성으로 질문을 입력받거나, 회의록 작성 도구에서 실시간으로 음성을 텍스트로 변환하는 경우에 매우 유용합니다.
기존에는 Google Cloud Speech-to-Text나 AWS Transcribe 같은 유료 서비스를 사용했다면, 이제는 브라우저 내장 기능만으로 무료로 구현할 수 있습니다. Web Speech API의 핵심 특징은 첫째, 별도 설치 없이 브라우저에서 바로 사용 가능하고, 둘째, 실시간 스트리밍 인식을 지원하며, 셋째, 다양한 언어를 지원한다는 점입니다.
이러한 특징들이 개발 비용과 시간을 크게 절약해주고 사용자에게 즉각적인 반응을 제공할 수 있게 합니다.
코드 예제
// 브라우저 호환성 체크 및 SpeechRecognition 객체 생성
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
if (!SpeechRecognition) {
console.error('이 브라우저는 음성 인식을 지원하지 않습니다.');
alert('Chrome, Edge, Safari 브라우저를 사용해주세요.');
} else {
console.log('음성 인식 API 사용 가능');
// 기본 설정 확인
const recognition = new SpeechRecognition();
console.log('기본 언어:', recognition.lang || 'ko-KR');
console.log('연속 인식:', recognition.continuous);
console.log('임시 결과:', recognition.interimResults);
}
설명
이것이 하는 일: 이 코드는 브라우저가 음성 인식 기능을 지원하는지 확인하고, 지원한다면 SpeechRecognition 객체를 생성하여 기본 설정을 출력합니다. 첫 번째로, window.SpeechRecognition || window.webkitSpeechRecognition 부분은 브라우저 호환성을 체크합니다.
Chrome과 Edge는 webkitSpeechRecognition을, 표준을 따르는 브라우저는 SpeechRecognition을 사용합니다. 이렇게 두 가지를 모두 확인함으로써 대부분의 모던 브라우저에서 작동할 수 있습니다.
그 다음으로, if (!SpeechRecognition) 조건문이 실행되면서 API를 지원하지 않는 경우 사용자에게 친절한 안내 메시지를 보여줍니다. Firefox는 아직 이 API를 지원하지 않으므로, 사용자에게 Chrome이나 Edge, Safari를 사용하도록 안내하는 것이 중요합니다.
마지막으로, API가 지원되는 경우 새로운 SpeechRecognition 인스턴스를 생성하고 기본 설정값들을 콘솔에 출력합니다. lang은 인식할 언어, continuous는 연속 인식 여부, interimResults는 중간 결과 표시 여부를 나타냅니다.
여러분이 이 코드를 사용하면 프로젝트에 음성 인식 기능을 도입하기 전에 사용자 브라우저의 호환성을 미리 체크할 수 있습니다. 또한 지원하지 않는 브라우저 사용자에게 대체 입력 방법을 제공하거나 적절한 안내를 할 수 있어 사용자 경험이 크게 향상됩니다.
실전 팁
💡 브라우저 호환성 체크는 앱 초기화 단계에서 한 번만 수행하고 결과를 전역 상태로 관리하세요. 매번 체크하면 불필요한 오버헤드가 발생합니다.
💡 Firefox 사용자를 위해 폴백(fallback) UI를 준비하세요. 음성 입력 버튼 대신 일반 텍스트 입력창으로 자동 전환되도록 구현하면 좋습니다.
💡 모바일 Safari에서는 사용자 제스처(터치, 클릭) 이후에만 음성 인식을 시작할 수 있습니다. 페이지 로드 시 자동으로 시작하려고 하면 에러가 발생합니다.
💡 HTTPS 환경에서만 작동합니다. 로컬 개발 시에는 localhost도 허용되지만, 배포 환경에서는 반드시 SSL 인증서를 적용하세요.
💡 개발자 도구의 콘솔에서 기본 설정값을 확인하면 브라우저별 차이를 파악할 수 있어 디버깅에 도움이 됩니다.
2. SpeechRecognition 객체 생성
시작하며
여러분이 음성 인식 기능을 처음 구현할 때 가장 먼저 마주치는 것이 바로 SpeechRecognition 객체 설정입니다. 어떤 속성을 어떻게 설정하느냐에 따라 음성 인식의 품질과 사용자 경험이 완전히 달라집니다.
많은 초보자들이 기본 설정만으로 시작했다가 "왜 한 문장만 인식하고 멈추지?", "왜 인식 중간 결과가 안 보이지?" 같은 문제를 겪습니다. 이는 객체의 속성을 제대로 이해하지 못했기 때문입니다.
바로 이럴 때 필요한 것이 올바른 SpeechRecognition 객체 초기화입니다. 속성들을 정확히 설정하면 여러분이 원하는 방식으로 음성 인식이 작동합니다.
개요
간단히 말해서, SpeechRecognition 객체는 음성 인식의 동작 방식을 제어하는 설정의 집합입니다. 이 객체를 통해 언어, 인식 모드, 결과 표시 방식 등을 자유롭게 설정할 수 있습니다.
이 설정이 필요한 이유는 사용 사례마다 요구사항이 다르기 때문입니다. 음성 검색은 한 문장만 인식하면 되지만, 회의록 작성 도구는 계속해서 음성을 인식해야 합니다.
예를 들어, 음성 메모 앱에서는 사용자가 말을 멈출 때까지 계속 인식해야 하고, 중간 결과도 실시간으로 보여줘야 사용자가 자신이 말한 내용을 확인할 수 있습니다. 기존에는 한 번 설정하면 변경하기 어려웠다면, 이제는 상황에 따라 동적으로 설정을 변경할 수 있습니다.
핵심 속성은 첫째 continuous (연속 인식 여부), 둘째 interimResults (중간 결과 표시), 셋째 lang (인식 언어), 넷째 maxAlternatives (대체 결과 개수)입니다. 이러한 속성들이 음성 인식의 정확도와 사용자 피드백 속도를 결정합니다.
코드 예제
// SpeechRecognition 객체 생성 및 속성 설정
const recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
// 연속 인식 활성화 - 사용자가 멈추지 않고 계속 말할 수 있음
recognition.continuous = true;
// 중간 결과 표시 - 최종 결과 전에 임시 결과를 실시간으로 받음
recognition.interimResults = true;
// 인식 언어 설정 - 한국어로 설정
recognition.lang = 'ko-KR';
// 대체 결과 개수 - 가장 확실한 5개의 결과를 받음
recognition.maxAlternatives = 5;
console.log('음성 인식 설정 완료:', recognition);
설명
이것이 하는 일: 이 코드는 SpeechRecognition 객체를 생성하고 실무에서 가장 많이 사용되는 4가지 핵심 속성을 설정합니다. 첫 번째로, continuous = true 설정은 사용자가 말을 계속할 수 있게 합니다.
기본값은 false로, 한 문장을 인식하면 자동으로 멈춥니다. 하지만 대부분의 실무 애플리케이션에서는 연속 인식이 필요하므로 true로 설정하는 것이 일반적입니다.
그 다음으로, interimResults = true를 설정하면 사용자가 말하는 동안 중간 결과를 실시간으로 받을 수 있습니다. 이는 마치 카카오톡 음성 메시지를 텍스트로 변환할 때 중간에 글자가 바뀌는 것과 같은 효과입니다.
사용자는 자신이 말한 내용이 제대로 인식되고 있는지 즉시 확인할 수 있어 훨씬 좋은 사용자 경험을 제공합니다. 세 번째로, lang 속성은 인식할 언어를 지정합니다.
'ko-KR'은 한국어, 'en-US'는 미국 영어, 'ja-JP'는 일본어입니다. 언어 설정이 정확해야 인식률이 높아집니다.
다국어 서비스라면 사용자의 언어 설정에 따라 동적으로 변경해야 합니다. 마지막으로, maxAlternatives 속성은 음성 인식 엔진이 반환할 대체 결과의 개수를 지정합니다.
기본값은 1이지만, 5로 설정하면 가장 확실한 5개의 결과를 받을 수 있습니다. 이는 동음이의어나 발음이 비슷한 단어를 처리할 때 유용합니다.
여러분이 이 설정을 사용하면 사용자가 자연스럽게 대화하듯 음성을 입력할 수 있고, 실시간으로 결과를 확인하며, 정확한 언어로 인식받을 수 있습니다. 또한 여러 대체 결과를 활용하여 사용자에게 선택지를 제공하거나 문맥에 맞는 결과를 자동으로 선택하는 고급 기능도 구현할 수 있습니다.
실전 팁
💡 continuous = true를 사용할 때는 반드시 명시적인 종료 버튼을 제공하세요. 그렇지 않으면 배터리 소모가 크고 불필요한 음성까지 계속 인식합니다.
💡 maxAlternatives를 5 이상으로 설정해도 실제로는 3-4개만 반환되는 경우가 많습니다. 브라우저와 상황에 따라 다르므로 너무 큰 값을 설정하지 마세요.
💡 모바일에서는 interimResults = true가 배터리를 더 소모합니다. 필요하지 않다면 false로 설정하여 최종 결과만 받는 것이 효율적입니다.
💡 언어를 변경할 때는 인식을 중지하고 재시작해야 합니다. 인식 중에 lang을 변경해도 적용되지 않습니다.
💡 개발 중에는 console.log(recognition)으로 모든 속성을 확인하세요. 브라우저마다 지원하는 속성과 기본값이 다를 수 있습니다.
3. 실시간 음성 인식 구현
시작하며
여러분이 음성 인식 앱을 만들 때 가장 중요한 것은 바로 "시작"과 "중지" 기능입니다. 사용자가 버튼을 클릭하면 음성 인식이 시작되고, 다시 클릭하면 멈춰야 합니다.
이것이 제대로 작동하지 않으면 사용자는 앱을 신뢰할 수 없습니다. 많은 개발자들이 start() 메서드만 호출하면 끝이라고 생각합니다.
하지만 실제로는 에러 처리, 상태 관리, 재시작 로직 등 고려해야 할 것이 많습니다. 예를 들어, 이미 실행 중인 인식을 다시 시작하려고 하면 에러가 발생합니다.
바로 이럴 때 필요한 것이 올바른 음성 인식 라이프사이클 관리입니다. 시작, 중지, 재시작을 안정적으로 처리하면 사용자에게 끊김 없는 경험을 제공할 수 있습니다.
개요
간단히 말해서, 음성 인식 라이프사이클은 start(시작) → 인식 중 → stop/abort(중지) → 재시작의 흐름을 안정적으로 관리하는 것입니다. 이것이 필요한 이유는 음성 인식이 비동기적으로 작동하고 여러 상태를 가지기 때문입니다.
사용자가 빠르게 버튼을 여러 번 클릭하거나, 네트워크 오류가 발생하거나, 브라우저가 자동으로 인식을 중지하는 등 예상치 못한 상황이 많습니다. 예를 들어, 음성 명령 앱에서 사용자가 "시작" 버튼을 연타하면 여러 개의 인식 세션이 동시에 실행되어 결과가 중복으로 출력될 수 있습니다.
기존에는 단순히 start/stop만 호출했다면, 이제는 상태를 추적하고 중복 실행을 방지하며 자동 재시작 로직을 구현해야 합니다. 핵심 기능은 첫째, 현재 인식 상태를 추적하는 플래그 관리, 둘째, 중복 실행 방지 로직, 셋째, 에러 발생 시 자동 재시작, 넷째, 사용자 인터페이스와의 동기화입니다.
이러한 기능들이 안정적이고 예측 가능한 음성 인식 경험을 만들어줍니다.
코드 예제
let isRecognizing = false;
const startButton = document.getElementById('startBtn');
const statusText = document.getElementById('status');
function startRecognition() {
// 이미 실행 중이면 중복 실행 방지
if (isRecognizing) return;
try {
recognition.start();
isRecognizing = true;
startButton.textContent = '중지';
startButton.classList.add('active');
statusText.textContent = '듣고 있습니다...';
} catch (error) {
console.error('시작 실패:', error);
statusText.textContent = '시작 실패. 다시 시도하세요.';
}
}
function stopRecognition() {
if (!isRecognizing) return;
recognition.stop();
isRecognizing = false;
startButton.textContent = '시작';
startButton.classList.remove('active');
statusText.textContent = '대기 중';
}
// 버튼 클릭 이벤트
startButton.addEventListener('click', () => {
isRecognizing ? stopRecognition() : startRecognition();
});
// 자동 종료 시 상태 업데이트
recognition.onend = () => {
isRecognizing = false;
startButton.textContent = '시작';
startButton.classList.remove('active');
statusText.textContent = '종료됨';
};
설명
이것이 하는 일: 이 코드는 음성 인식의 시작과 중지를 안전하게 처리하고, UI와 상태를 동기화하며, 중복 실행을 방지합니다. 첫 번째로, isRecognizing 플래그는 현재 음성 인식이 실행 중인지 추적합니다.
startRecognition() 함수 시작 부분에서 이 플래그를 체크하여, 이미 실행 중이면 함수를 즉시 종료합니다. 이렇게 하면 사용자가 시작 버튼을 여러 번 클릭해도 하나의 인식 세션만 실행됩니다.
그 다음으로, try-catch 블록으로 recognition.start()를 감싸서 에러를 안전하게 처리합니다. 이미 실행 중인 인식을 다시 시작하려고 하면 "recognition has already started" 에러가 발생하는데, 이를 캐치하여 사용자에게 친절한 메시지를 보여줍니다.
또한 마이크 권한이 없거나 브라우저가 API를 지원하지 않는 경우에도 에러를 적절히 처리합니다. 세 번째로, UI 업데이트 로직이 상태 변경과 함께 실행됩니다.
버튼 텍스트가 "시작"에서 "중지"로 바뀌고, active 클래스가 추가되어 시각적 피드백을 제공하며, 상태 텍스트도 "듣고 있습니다..."로 변경됩니다. 이러한 즉각적인 피드백이 사용자에게 앱이 제대로 작동하고 있음을 알려줍니다.
마지막으로, recognition.onend 이벤트 핸들러는 음성 인식이 자동으로 종료될 때 (예: 일정 시간 동안 음성이 없을 때) 상태를 리셋합니다. 이 핸들러가 없으면 내부적으로는 인식이 중지되었지만 UI는 여전히 "실행 중" 상태를 표시하는 불일치가 발생합니다.
여러분이 이 패턴을 사용하면 음성 인식이 예측 가능하게 작동하고, 사용자가 언제든지 제어할 수 있으며, 에러 상황에서도 앱이 멈추지 않고 적절한 피드백을 제공합니다. 또한 상태와 UI가 항상 동기화되어 사용자 혼란을 방지할 수 있습니다.
실전 팁
💡 stop() 대신 abort()를 사용하면 결과 이벤트 없이 즉시 중단됩니다. 사용자가 취소 버튼을 눌렀을 때는 abort()가 더 적합합니다.
💡 isRecognizing 플래그만으로는 부족할 수 있습니다. 실제 프로덕션에서는 "starting", "listening", "processing", "stopped" 같은 상세한 상태 머신을 구현하는 것이 좋습니다.
💡 모바일에서는 화면이 꺼지면 음성 인식이 자동으로 중지됩니다. visibilitychange 이벤트를 감지하여 화면이 다시 켜졌을 때 재시작할지 물어보세요.
💡 연속 인식 모드에서 장시간 실행하면 메모리 누수가 발생할 수 있습니다. 주기적으로 (예: 5분마다) 인식을 중지하고 재시작하는 것이 안전합니다.
💡 React나 Vue 같은 프레임워크를 사용한다면 isRecognizing을 state로 관리하여 자동으로 UI가 업데이트되도록 하세요.
4. 음성 인식 이벤트 처리
시작하며
여러분이 음성 인식을 시작했다면, 이제 가장 중요한 부분인 "결과를 받아서 처리하기"를 구현해야 합니다. 사용자가 말한 내용을 텍스트로 받아서 화면에 표시하거나, 명령으로 해석하거나, 서버로 전송하는 등의 작업이 필요합니다.
많은 개발자들이 onresult 이벤트만 처리하다가 "왜 중간 결과와 최종 결과가 섞여서 나오지?", "에러가 발생했는데 어떻게 처리하지?" 같은 문제를 겪습니다. 음성 인식에는 여러 종류의 이벤트가 있고, 각각을 올바르게 처리해야 합니다.
바로 이럴 때 필요한 것이 완전한 이벤트 핸들링입니다. result, error, end, start 등 모든 이벤트를 적절히 처리하면 안정적인 음성 인식 앱을 만들 수 있습니다.
개요
간단히 말해서, 음성 인식 이벤트는 인식 과정의 각 단계에서 발생하는 신호들로, result(결과), error(에러), end(종료), start(시작), soundstart(소리 감지) 등이 있습니다. 이벤트 처리가 필요한 이유는 각 상황에 맞는 적절한 대응을 해야 하기 때문입니다.
결과 이벤트에서는 텍스트를 추출하고, 에러 이벤트에서는 원인을 파악하여 재시도하거나 사용자에게 알리고, 종료 이벤트에서는 필요시 자동으로 재시작해야 합니다. 예를 들어, 음성 메모 앱에서는 중간 결과를 실시간으로 표시하다가 최종 결과가 확정되면 서버에 저장하는 식으로 구분해서 처리해야 합니다.
기존에는 모든 결과를 단순히 화면에 출력했다면, 이제는 중간 결과와 최종 결과를 구분하고, 에러 종류별로 다르게 처리하며, 자동 재시작 여부를 판단해야 합니다. 핵심 이벤트는 첫째 onresult (음성 인식 결과), 둘째 onerror (에러 발생), 셋째 onend (인식 종료), 넷째 onnomatch (일치하는 결과 없음)입니다.
이러한 이벤트들을 모두 처리해야 사용자에게 완전한 피드백을 제공할 수 있습니다.
코드 예제
let finalTranscript = '';
let interimTranscript = '';
// 결과 이벤트 - 가장 중요한 이벤트
recognition.onresult = (event) => {
interimTranscript = '';
// 모든 결과를 순회하며 처리
for (let i = event.resultIndex; i < event.results.length; i++) {
const transcript = event.results[i][0].transcript;
if (event.results[i].isFinal) {
// 최종 결과 - 서버 저장, 명령 실행 등
finalTranscript += transcript + ' ';
console.log('최종 결과:', transcript);
} else {
// 중간 결과 - 실시간 UI 업데이트
interimTranscript += transcript;
console.log('중간 결과:', transcript);
}
}
// 화면 업데이트
document.getElementById('final').textContent = finalTranscript;
document.getElementById('interim').textContent = interimTranscript;
};
// 에러 이벤트 - 반드시 처리 필요
recognition.onerror = (event) => {
console.error('음성 인식 에러:', event.error);
switch(event.error) {
case 'no-speech':
statusText.textContent = '음성이 감지되지 않았습니다.';
break;
case 'audio-capture':
statusText.textContent = '마이크를 찾을 수 없습니다.';
break;
case 'not-allowed':
statusText.textContent = '마이크 권한이 거부되었습니다.';
break;
default:
statusText.textContent = `에러 발생: ${event.error}`;
}
};
// 일치 결과 없음
recognition.onnomatch = () => {
statusText.textContent = '인식할 수 없는 음성입니다.';
};
설명
이것이 하는 일: 이 코드는 음성 인식의 모든 결과를 받아서 중간 결과와 최종 결과를 구분하여 처리하고, 에러 상황에 적절히 대응합니다. 첫 번째로, onresult 이벤트 핸들러는 음성 인식 결과를 받습니다.
event.results는 인식된 모든 결과를 담고 있고, event.resultIndex는 새로 추가된 결과의 시작 인덱스입니다. 이전에 처리한 결과는 건너뛰고 새로운 결과만 처리하기 위해 resultIndex부터 순회합니다.
그 다음으로, isFinal 속성으로 중간 결과와 최종 결과를 구분합니다. 사용자가 말하는 동안에는 isFinal = false인 중간 결과가 계속 오고, 문장이 끝나면 isFinal = true인 최종 결과가 옵니다.
중간 결과는 UI 업데이트용으로만 사용하고, 최종 결과만 서버에 저장하거나 명령으로 해석하는 것이 일반적입니다. 세 번째로, event.results[i][0].transcript에서 실제 텍스트를 추출합니다.
[0]은 가장 확실한 결과를 의미하고, maxAlternatives를 설정했다면 [1], [2] 등으로 대체 결과도 접근할 수 있습니다. 각 결과의 신뢰도는 confidence 속성으로 확인할 수 있습니다.
네 번째로, onerror 이벤트 핸들러는 다양한 에러 상황을 처리합니다. 'no-speech'는 일정 시간 동안 음성이 없을 때, 'audio-capture'는 마이크 접근 실패, 'not-allowed'는 사용자가 권한을 거부했을 때 발생합니다.
각 에러에 맞는 안내 메시지를 표시하여 사용자가 문제를 해결할 수 있도록 돕습니다. 여러분이 이 코드를 사용하면 사용자가 말하는 동안 실시간으로 텍스트가 화면에 나타나고, 문장이 완성되면 최종 텍스트가 확정되며, 에러가 발생해도 적절한 안내를 받을 수 있습니다.
또한 중간 결과와 최종 결과를 분리하여 불필요한 서버 요청을 줄이고 성능을 최적화할 수 있습니다.
실전 팁
💡 event.results는 배열처럼 보이지만 실제로는 SpeechRecognitionResultList입니다. Array.from()으로 변환하여 map, filter 등을 사용하면 편리합니다.
💡 중간 결과는 매우 빠르게 업데이트됩니다. 디바운싱(debouncing)을 적용하여 UI 업데이트 빈도를 제한하면 성능이 향상됩니다.
💡 confidence 값이 0.8 이하면 인식 정확도가 낮은 것입니다. 사용자에게 다시 말해달라고 요청하거나 대체 결과를 선택지로 보여주세요.
💡 'not-allowed' 에러는 복구 불가능합니다. 사용자에게 브라우저 설정에서 마이크 권한을 허용하는 방법을 안내해야 합니다.
💡 finalTranscript에 결과를 계속 추가할 때는 메모리 관리에 주의하세요. 일정 길이를 초과하면 이전 내용을 잘라내거나 서버로 전송하고 초기화하는 것이 좋습니다.
5. 언어 설정과 다국어 지원
시작하며
여러분이 글로벌 서비스를 만든다면 음성 인식도 다국어를 지원해야 합니다. 한국 사용자는 한국어로, 미국 사용자는 영어로, 일본 사용자는 일본어로 음성을 인식받고 싶어 합니다.
많은 개발자들이 언어를 고정해두거나, 사용자가 직접 선택하게 만듭니다. 하지만 브라우저는 이미 사용자의 선호 언어를 알고 있고, 이를 활용하면 훨씬 더 나은 사용자 경험을 제공할 수 있습니다.
바로 이럴 때 필요한 것이 자동 언어 감지와 동적 언어 전환입니다. 사용자의 브라우저 언어를 감지하여 자동으로 설정하고, 필요시 실시간으로 언어를 변경할 수 있습니다.
개요
간단히 말해서, 음성 인식의 언어 설정은 lang 속성으로 제어되며, 'ko-KR'(한국어), 'en-US'(영어), 'ja-JP'(일본어) 등의 BCP 47 언어 태그를 사용합니다. 언어 설정이 필요한 이유는 같은 발음이라도 언어에 따라 완전히 다른 단어로 인식되기 때문입니다.
예를 들어, "나이키"라고 말했을 때 영어 모드에서는 "Nike"로, 한국어 모드에서는 "나이키"로 인식됩니다. 다국어 쇼핑몰에서 음성 검색 기능을 제공한다면, 사용자의 언어에 맞춰 정확하게 인식해야 올바른 검색 결과를 보여줄 수 있습니다.
기존에는 하나의 언어만 지원했다면, 이제는 브라우저 언어를 자동 감지하고, 사용자가 언어를 전환할 수 있으며, 심지어 다국어 혼용 음성도 처리할 수 있습니다. 핵심 기능은 첫째, navigator.language로 브라우저 언어 감지, 둘째, 동적 언어 전환 함수 구현, 셋째, 지원 언어 목록 관리, 넷째, 언어별 인식률 최적화입니다.
이러한 기능들이 글로벌 사용자에게 모국어로 서비스를 제공할 수 있게 합니다.
코드 예제
// 지원 언어 목록 정의
const supportedLanguages = {
'ko': 'ko-KR',
'en': 'en-US',
'ja': 'ja-JP',
'zh': 'zh-CN',
'es': 'es-ES'
};
// 브라우저 언어 자동 감지
function detectUserLanguage() {
const browserLang = navigator.language.split('-')[0];
return supportedLanguages[browserLang] || 'en-US';
}
// 언어 설정 함수
function setRecognitionLanguage(langCode) {
const wasRecognizing = isRecognizing;
// 실행 중이면 중지
if (wasRecognizing) {
recognition.stop();
}
// 언어 변경
recognition.lang = langCode;
console.log('언어 변경:', langCode);
// 다시 시작
if (wasRecognizing) {
setTimeout(() => recognition.start(), 100);
}
// UI 업데이트
document.getElementById('currentLang').textContent = langCode;
}
// 초기 언어 설정
const initialLang = detectUserLanguage();
recognition.lang = initialLang;
console.log('자동 감지된 언어:', initialLang);
// 언어 선택 드롭다운
document.getElementById('langSelect').addEventListener('change', (e) => {
setRecognitionLanguage(e.target.value);
});
설명
이것이 하는 일: 이 코드는 사용자의 브라우저 언어를 자동으로 감지하여 음성 인식 언어를 설정하고, 사용자가 수동으로 언어를 변경할 수 있는 기능을 제공합니다. 첫 번째로, supportedLanguages 객체는 서비스가 지원하는 언어 목록을 정의합니다.
키는 ISO 639-1 코드(2자리)이고, 값은 BCP 47 전체 언어 태그입니다. 이렇게 매핑 테이블을 만들면 브라우저 언어를 쉽게 음성 인식 언어로 변환할 수 있습니다.
그 다음으로, detectUserLanguage() 함수는 navigator.language에서 언어 코드만 추출합니다. 예를 들어, 'ko-KR'에서 'ko'만 가져와서 supportedLanguages에서 찾습니다.
지원하지 않는 언어면 기본값으로 'en-US'를 반환합니다. 이렇게 하면 사용자가 별도 설정 없이도 자신의 언어로 바로 서비스를 사용할 수 있습니다.
세 번째로, setRecognitionLanguage() 함수는 언어를 안전하게 변경합니다. 가장 중요한 부분은 인식이 실행 중이면 먼저 중지하고, 언어를 변경한 후, 다시 시작한다는 점입니다.
인식 중에 언어를 변경하면 적용되지 않기 때문에 이 과정이 필수입니다. setTimeout(..., 100)으로 약간의 지연을 두는 것은 중지가 완전히 완료될 시간을 주기 위함입니다.
마지막으로, 언어 선택 드롭다운의 change 이벤트를 감지하여 사용자가 언어를 수동으로 변경할 수 있게 합니다. 이는 자동 감지가 틀렸거나, 사용자가 다른 언어로 말하고 싶을 때 유용합니다.
여러분이 이 코드를 사용하면 한국에서 접속한 사용자는 자동으로 한국어 음성 인식이 활성화되고, 미국 사용자는 영어가 활성화됩니다. 또한 여행 중이거나 다국어 사용자는 언제든지 언어를 전환할 수 있어 훨씬 더 유연한 서비스를 제공할 수 있습니다.
실전 팁
💡 navigator.languages (복수형)를 사용하면 사용자의 언어 선호도 목록을 받을 수 있습니다. 첫 번째 지원 언어를 찾아서 설정하면 더 정확합니다.
💡 지역별 방언이 중요하다면 'en-US'와 'en-GB', 'zh-CN'과 'zh-TW'를 구분해서 지원하세요. 발음과 어휘가 다릅니다.
💡 언어 변경 시 사용자에게 "언어가 변경되었습니다. 다시 말씀해주세요."라고 안내하세요. 그렇지 않으면 사용자는 왜 인식이 안 되는지 모릅니다.
💡 특정 언어는 브라우저나 OS에 따라 지원되지 않을 수 있습니다. 지원 여부를 미리 테스트하고 지원되지 않는 언어는 목록에서 제외하세요.
💡 다국어 콘텐츠를 다루는 서비스라면 문장 단위로 언어를 자동 감지하는 것도 고려해보세요. 한 문장은 한국어, 다음 문장은 영어로 말할 수 있습니다.
6. 음성 명령 시스템 구현
시작하며
여러분이 음성으로 앱을 제어하는 기능을 만들고 싶다면, 단순히 음성을 텍스트로 변환하는 것만으로는 부족합니다. "다음 페이지", "스크롤 다운", "검색해줘" 같은 명령어를 인식하고 실행하는 시스템이 필요합니다.
많은 개발자들이 if (text === '다음') 같은 단순 비교만 사용하다가 "다음 페이지", "다음으로", "넥스트" 같은 유사 표현을 놓치게 됩니다. 또한 띄어쓰기나 문장부호 때문에 정확히 일치하지 않아 명령이 실행되지 않는 경우도 많습니다.
바로 이럴 때 필요한 것이 유연한 명령어 매칭 시스템입니다. 정규식과 패턴 매칭을 활용하면 다양한 표현을 하나의 명령으로 처리할 수 있습니다.
개요
간단히 말해서, 음성 명령 시스템은 인식된 텍스트를 분석하여 미리 정의된 명령어 패턴과 매칭하고, 해당하는 동작을 실행하는 구조입니다. 이것이 필요한 이유는 사람들이 같은 의미를 다양한 방식으로 표현하기 때문입니다.
"검색"이라는 명령을 "검색해", "검색해줘", "찾아줘", "서치", "search" 등 여러 방식으로 말할 수 있습니다. 예를 들어, 스마트 홈 앱에서 "불 켜줘", "조명 켜", "라이트 온" 모두 조명을 켜는 명령으로 인식해야 사용자가 편리하게 사용할 수 있습니다.
기존에는 정확히 일치하는 명령어만 인식했다면, 이제는 정규식, 유사도 매칭, 의도 분석 등을 통해 자연스러운 음성 명령을 처리할 수 있습니다. 핵심 구성 요소는 첫째, 명령어 패턴 정의 (정규식 또는 키워드 배열), 둘째, 텍스트 전처리 (띄어쓰기 제거, 소문자 변환 등), 셋째, 패턴 매칭 엔진, 넷째, 명령 실행 핸들러입니다.
이러한 요소들이 자연스러운 음성 인터페이스를 만들어줍니다.
코드 예제
// 명령어 패턴 정의
const commands = [
{
patterns: [/다음/i, /넥스트/i, /next/i],
action: () => {
window.scrollBy(0, 500);
showFeedback('다음 페이지로 이동');
}
},
{
patterns: [/이전/i, /뒤로/i, /back/i],
action: () => {
window.scrollBy(0, -500);
showFeedback('이전 페이지로 이동');
}
},
{
patterns: [/(검색|찾아|서치).*?(.+)/i],
action: (match) => {
const query = match[2].trim();
performSearch(query);
showFeedback(`"${query}" 검색 중...`);
}
}
];
// 텍스트 전처리
function preprocessText(text) {
return text
.toLowerCase()
.replace(/[.,!?]/g, '')
.trim();
}
// 명령어 매칭 및 실행
function executeCommand(text) {
const processed = preprocessText(text);
for (const command of commands) {
for (const pattern of command.patterns) {
const match = processed.match(pattern);
if (match) {
console.log('명령 실행:', pattern, match);
command.action(match);
return true;
}
}
}
console.log('일치하는 명령 없음:', text);
return false;
}
// 피드백 표시
function showFeedback(message) {
const feedback = document.getElementById('feedback');
feedback.textContent = message;
feedback.classList.add('show');
setTimeout(() => feedback.classList.remove('show'), 2000);
}
// 음성 인식 결과에서 명령 실행
recognition.onresult = (event) => {
for (let i = event.resultIndex; i < event.results.length; i++) {
if (event.results[i].isFinal) {
const text = event.results[i][0].transcript;
executeCommand(text);
}
}
};
설명
이것이 하는 일: 이 코드는 인식된 음성 텍스트를 전처리하고, 미리 정의된 명령어 패턴과 매칭하여, 해당하는 동작을 실행하는 완전한 음성 명령 시스템입니다. 첫 번째로, commands 배열은 각 명령의 패턴과 동작을 정의합니다.
각 명령은 여러 개의 정규식 패턴을 가질 수 있어서 "다음", "넥스트", "next" 같은 다양한 표현을 모두 인식할 수 있습니다. patterns 배열에 정규식을 나열하면 하나라도 매칭되면 명령이 실행됩니다.
그 다음으로, preprocessText() 함수는 인식된 텍스트를 정규화합니다. 소문자로 변환하여 대소문자 구분을 없애고, 문장부호를 제거하며, 앞뒤 공백을 제거합니다.
이렇게 전처리하면 "다음!", "다음.", "다음" 모두 "다음"으로 통일되어 매칭 성공률이 높아집니다. 세 번째로, executeCommand() 함수는 전처리된 텍스트를 모든 명령 패턴과 비교합니다.
이중 루프를 사용하여 각 명령의 모든 패턴을 확인하고, 첫 번째로 매칭되는 패턴을 찾으면 즉시 해당 동작을 실행하고 true를 반환합니다. 매칭되는 명령이 없으면 false를 반환하여 호출자가 대체 동작을 수행할 수 있게 합니다.
네 번째로, 정규식의 캡처 그룹을 활용하여 파라미터를 추출합니다. 예를 들어, "검색해줘 리액트 훅스"라고 말하면 /(검색|찾아).*?(.+)/ 패턴의 두 번째 그룹 (.+)가 "리액트 훅스"를 캡처하여 match[2]로 전달합니다.
이를 performSearch(query)에 넘겨 실제 검색을 수행합니다. 여러분이 이 시스템을 사용하면 사용자가 자연스러운 말투로 앱을 제어할 수 있고, 같은 명령을 다양한 방식으로 표현해도 모두 인식되며, 명령의 파라미터도 추출하여 동적인 동작을 수행할 수 있습니다.
또한 새로운 명령을 추가할 때도 commands 배열에 객체만 추가하면 되어 확장성이 뛰어납니다.
실전 팁
💡 명령어 우선순위가 중요합니다. 더 구체적인 패턴을 배열 앞쪽에 배치하세요. "검색 중지"는 "검색"보다 먼저 체크해야 합니다.
💡 한국어와 영어를 혼용하는 사용자를 위해 두 언어의 패턴을 모두 포함하세요. 한국인도 "next", "back" 같은 단어를 자주 사용합니다.
💡 명령어 매칭 실패 시 "죄송합니다. 이해하지 못했습니다. 사용 가능한 명령어: ..."처럼 도움말을 보여주세요.
💡 비슷한 명령어가 많다면 퍼지 매칭(fuzzy matching) 라이브러리(예: fuse.js)를 사용하여 오타나 발음 차이를 허용하세요.
💡 민감한 동작(삭제, 결제 등)은 음성 명령만으로 실행하지 말고 확인 다이얼로그를 보여주세요. 오인식으로 인한 사고를 방지할 수 있습니다.
7. 음성 인식 정확도 향상
시작하며
여러분이 음성 인식을 실제 프로덕션에 적용하다 보면 "왜 이렇게 자주 틀리지?", "주변 소음 때문에 엉뚱한 말이 인식돼"라는 문제를 겪게 됩니다. 기본 설정만으로는 인식 정확도가 충분하지 않을 때가 많습니다.
많은 개발자들이 음성 인식 엔진 자체의 한계라고 생각하고 포기합니다. 하지만 실제로는 적절한 설정 조정, 후처리, 사용자 피드백 수집 등을 통해 정확도를 크게 향상시킬 수 있습니다.
바로 이럴 때 필요한 것이 정확도 최적화 기법들입니다. 신뢰도 체크, 문맥 기반 필터링, 사용자 정정 학습 등을 활용하면 훨씬 더 실용적인 음성 인식 시스템을 만들 수 있습니다.
개요
간단히 말해서, 음성 인식 정확도 향상은 인식 결과의 신뢰도를 평가하고, 낮은 신뢰도 결과를 걸러내거나 사용자에게 확인받으며, 도메인 특화 용어를 보정하는 과정입니다. 이것이 필요한 이유는 음성 인식 엔진이 항상 완벽하지 않고, 특히 도메인 특화 용어나 고유명사, 전문 용어는 자주 틀리기 때문입니다.
의료 기록 앱에서 "gastroenterology"를 "gas enter all ology"로 인식하거나, 한국어로 "안녕하세요"를 "안녕 하세요"로 띄어쓰기를 잘못하는 경우가 많습니다. 예를 들어, 고객 이름을 음성으로 입력받는 CRM 앱에서는 낮은 신뢰도의 결과를 사용자에게 다시 확인받는 것이 필수입니다.
기존에는 인식 결과를 그대로 사용했다면, 이제는 신뢰도를 체크하고, 도메인 사전으로 보정하며, 사용자 정정 데이터를 수집하여 점진적으로 개선할 수 있습니다. 핵심 기법은 첫째, confidence 값으로 신뢰도 체크, 둘째, 대체 결과 (alternatives) 활용, 셋째, 도메인 용어 사전으로 후처리, 넷째, 사용자 정정 피드백 수집입니다.
이러한 기법들이 실무에서 사용 가능한 수준의 정확도를 달성하게 해줍니다.
코드 예제
// 최소 신뢰도 임계값
const MIN_CONFIDENCE = 0.7;
// 도메인 특화 용어 사전
const domainDictionary = {
'react hook': 'React Hooks',
'use state': 'useState',
'use effect': 'useEffect',
'next js': 'Next.js',
'type script': 'TypeScript'
};
// 신뢰도 기반 결과 처리
recognition.onresult = (event) => {
for (let i = event.resultIndex; i < event.results.length; i++) {
if (event.results[i].isFinal) {
const result = event.results[i][0];
const transcript = result.transcript;
const confidence = result.confidence;
console.log(`인식: "${transcript}" (신뢰도: ${confidence.toFixed(2)})`);
if (confidence < MIN_CONFIDENCE) {
// 신뢰도 낮음 - 대체 결과 확인
handleLowConfidence(event.results[i]);
} else {
// 신뢰도 높음 - 후처리 후 사용
const corrected = applyDomainCorrection(transcript);
displayResult(corrected, confidence);
}
}
}
};
// 낮은 신뢰도 처리
function handleLowConfidence(result) {
const alternatives = [];
for (let j = 0; j < result.length; j++) {
alternatives.push({
text: result[j].transcript,
confidence: result[j].confidence
});
}
console.log('대체 결과들:', alternatives);
// 사용자에게 선택지 제공
showAlternativesDialog(alternatives);
}
// 도메인 용어 보정
function applyDomainCorrection(text) {
let corrected = text.toLowerCase();
for (const [wrong, correct] of Object.entries(domainDictionary)) {
const regex = new RegExp(wrong, 'gi');
corrected = corrected.replace(regex, correct);
}
return corrected;
}
// 사용자 정정 피드백 수집
function collectUserCorrection(recognized, userCorrected) {
// 서버로 전송하여 학습 데이터로 활용
fetch('/api/feedback', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ recognized, userCorrected, timestamp: Date.now() })
});
}
설명
이것이 하는 일: 이 코드는 음성 인식 결과의 신뢰도를 평가하고, 낮은 신뢰도 결과는 사용자에게 확인받으며, 도메인 특화 용어를 자동으로 보정하여 정확도를 향상시킵니다. 첫 번째로, confidence 값을 체크하여 인식 결과의 신뢰도를 평가합니다.
이 값은 0.0에서 1.0 사이이며, 1.0에 가까울수록 엔진이 결과를 확신한다는 의미입니다. MIN_CONFIDENCE를 0.7로 설정하면 70% 미만의 신뢰도를 가진 결과는 별도 처리합니다.
이 임계값은 도메인과 요구사항에 따라 조정해야 합니다. 그 다음으로, 신뢰도가 낮을 때는 result.length를 확인하여 대체 결과들을 가져옵니다.
maxAlternatives를 5로 설정했다면 최대 5개의 대체 결과를 받을 수 있고, 각각의 신뢰도와 텍스트를 비교하여 사용자에게 "어느 것을 의도하셨나요?" 같은 선택 UI를 보여줄 수 있습니다. 세 번째로, applyDomainCorrection() 함수는 도메인 사전을 활용하여 자주 틀리는 용어를 자동으로 보정합니다.
음성 인식 엔진은 "useState"를 "use state"로, "Next.js"를 "next js"로 인식하는 경우가 많은데, 정규식 치환으로 이를 올바른 형태로 교정합니다. 개발 도구 앱이라면 프로그래밍 용어, 의료 앱이라면 의학 용어를 사전에 추가하면 됩니다.
마지막으로, collectUserCorrection() 함수는 사용자가 인식 결과를 수동으로 수정했을 때 그 데이터를 수집합니다. 예를 들어, "use effect"로 인식되었는데 사용자가 "useEffect"로 고쳤다면 이 정보를 서버로 전송하여 향후 도메인 사전을 업데이트하거나 커스텀 언어 모델을 학습시키는 데 활용할 수 있습니다.
여러분이 이 기법들을 사용하면 음성 인식 오류를 조기에 발견하여 사용자에게 확인받을 수 있고, 도메인 특화 용어의 인식률을 크게 높일 수 있으며, 시간이 지날수록 시스템이 더 똑똑해지는 자기 학습 효과를 얻을 수 있습니다. 또한 신뢰도가 낮은 결과를 서버에 저장하지 않음으로써 데이터 품질도 보장할 수 있습니다.
실전 팁
💡 confidence 값은 크롬과 사파리에서 다르게 동작할 수 있습니다. 브라우저별로 임계값을 다르게 설정하는 것을 고려하세요.
💡 도메인 사전은 사용 빈도가 높은 용어부터 추가하세요. 너무 많은 용어를 추가하면 오히려 오검출(false positive)이 증가할 수 있습니다.
💡 사용자 정정 데이터를 주기적으로 분석하여 자주 틀리는 패턴을 찾아내세요. 예를 들어, "리액트"와 "react"가 모두 사용된다면 둘 다 인정하도록 수정할 수 있습니다.
💡 중요한 정보(이름, 주소, 금액 등)는 신뢰도에 관계없이 항상 사용자에게 확인받는 것이 안전합니다.
💡 배경 소음이 많은 환경에서는 audioTrack 설정으로 노이즈 캔슬링을 활성화하거나, 사용자에게 "조용한 곳에서 다시 시도하세요"라고 안내하세요.
8. 모바일 음성 인식 대응
시작하며
여러분이 음성 인식 기능을 데스크톱에서 완벽하게 구현했다고 해도, 모바일에서는 전혀 다른 문제들을 겪게 됩니다. 터치 인터랙션, 화면 크기, 배터리 소모, 권한 요청 등 모바일만의 특수한 상황들이 있습니다.
많은 개발자들이 "데스크톱에서 되는데 왜 모바일에서는 안 되지?"라며 당황합니다. 특히 iOS Safari에서는 사용자 제스처 없이는 음성 인식을 시작할 수 없고, Android에서는 화면이 꺼지면 인식이 중단되는 등 플랫폼별 차이가 큽니다.
바로 이럴 때 필요한 것이 모바일 특화 대응 전략입니다. 터치 기반 UI, 권한 요청 최적화, 배터리 효율 관리 등을 구현하면 모바일에서도 훌륭한 음성 인식 경험을 제공할 수 있습니다.
개요
간단히 말해서, 모바일 음성 인식 대응은 터치 이벤트 활용, 권한 요청 타이밍 최적화, 배터리 소모 최소화, 화면 방향 및 키보드 대응 등 모바일 환경의 특수성을 고려한 구현입니다. 이것이 필요한 이유는 모바일 브라우저가 보안과 사용자 경험을 위해 데스크톱과 다른 제약을 가지고 있기 때문입니다.
iOS Safari는 autoplay 정책이 엄격하여 사용자 상호작용 없이는 음성 인식을 시작할 수 없고, Android는 배터리 최적화를 위해 백그라운드 작업을 제한합니다. 예를 들어, 음성 메모 앱을 모바일로 만든다면 사용자가 화면을 터치한 상태에서만 녹음되도록 하는 "푸시 투 토크(Push-to-Talk)" 방식이 직관적이고 효율적입니다.
기존에는 데스크톱과 동일한 코드를 사용했다면, 이제는 터치 이벤트 감지, 모바일 브라우저 특성 파악, 반응형 UI, 오프라인 대응 등을 추가로 구현해야 합니다. 핵심 기능은 첫째, 터치 이벤트(touchstart, touchend)로 음성 인식 제어, 둘째, 사용자 제스처 후 권한 요청, 셋째, 화면 잠금 방지 및 백그라운드 처리, 넷째, 햅틱 피드백과 음성 피드백입니다.
이러한 기능들이 모바일 사용자에게 자연스럽고 효율적인 경험을 제공합니다.
코드 예제
// 모바일 감지
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
// 푸시 투 토크 버튼
const recordButton = document.getElementById('recordBtn');
if (isMobile) {
// 모바일: 터치 이벤트 사용
recordButton.addEventListener('touchstart', (e) => {
e.preventDefault(); // 기본 동작 방지
startRecognition();
// 햅틱 피드백 (지원 브라우저)
if (navigator.vibrate) {
navigator.vibrate(50);
}
// 시각적 피드백
recordButton.classList.add('recording');
});
recordButton.addEventListener('touchend', (e) => {
e.preventDefault();
stopRecognition();
// 햅틱 피드백
if (navigator.vibrate) {
navigator.vibrate(30);
}
recordButton.classList.remove('recording');
});
// 터치 이동 시 취소
recordButton.addEventListener('touchmove', (e) => {
const touch = e.touches[0];
const rect = recordButton.getBoundingClientRect();
// 버튼 영역을 벗어나면 취소
if (touch.clientX < rect.left || touch.clientX > rect.right ||
touch.clientY < rect.top || touch.clientY > rect.bottom) {
stopRecognition();
recordButton.classList.remove('recording');
showFeedback('녹음 취소됨');
}
});
} else {
// 데스크톱: 클릭 이벤트 사용
recordButton.addEventListener('click', () => {
isRecognizing ? stopRecognition() : startRecognition();
});
}
// 화면 잠금 방지 (모바일)
let wakeLock = null;
async function preventScreenLock() {
if ('wakeLock' in navigator && isMobile) {
try {
wakeLock = await navigator.wakeLock.request('screen');
console.log('화면 잠금 방지 활성화');
} catch (err) {
console.error('화면 잠금 방지 실패:', err);
}
}
}
function releaseScreenLock() {
if (wakeLock) {
wakeLock.release();
wakeLock = null;
console.log('화면 잠금 방지 해제');
}
}
// 음성 인식 시작 시 화면 잠금 방지
recognition.onstart = () => {
if (isMobile) {
preventScreenLock();
}
};
recognition.onend = () => {
if (isMobile) {
releaseScreenLock();
}
};
설명
이것이 하는 일: 이 코드는 모바일과 데스크톱을 감지하여 각 플랫폼에 최적화된 음성 인식 인터페이스를 제공하고, 모바일에서는 터치 기반 푸시 투 토크와 화면 잠금 방지 기능을 구현합니다. 첫 번째로, User Agent 문자열로 모바일 기기를 감지합니다.
정확한 방법은 아니지만 대부분의 경우 충분히 작동합니다. 이를 통해 모바일에서는 터치 이벤트, 데스크톱에서는 클릭 이벤트를 사용하도록 분기합니다.
그 다음으로, 모바일에서는 touchstart와 touchend 이벤트로 푸시 투 토크 방식을 구현합니다. 사용자가 버튼을 누르고 있는 동안만 음성 인식이 활성화되고, 손을 떼면 자동으로 중지됩니다.
이는 메신저 앱의 음성 메시지 기능과 동일한 UX로, 모바일 사용자에게 매우 익숙한 패턴입니다. e.preventDefault()는 터치 이벤트의 기본 동작(스크롤, 확대 등)을 막아서 의도하지 않은 동작을 방지합니다.
세 번째로, navigator.vibrate()로 햅틱 피드백을 제공합니다. 녹음 시작 시 50ms, 종료 시 30ms의 진동으로 사용자에게 즉각적인 촉각 피드백을 줍니다.
이는 시각적 피드백만으로는 부족한 모바일 환경에서 매우 중요합니다. 특히 손으로 버튼을 가리고 있을 때도 피드백을 인지할 수 있습니다.
네 번째로, touchmove 이벤트로 취소 제스처를 구현합니다. 사용자가 버튼을 누른 채로 손가락을 밖으로 슬라이드하면 녹음이 취소됩니다.
이는 메신저의 음성 메시지 취소 제스처와 동일하여 직관적입니다. 다섯 번째로, Wake Lock API로 화면이 꺼지는 것을 방지합니다.
음성 인식 중에 화면이 자동으로 꺼지면 인식이 중단되므로, navigator.wakeLock.request('screen')으로 화면을 켜진 상태로 유지합니다. 인식이 끝나면 wakeLock.release()로 해제하여 불필요한 배터리 소모를 방지합니다.
여러분이 이 패턴을 사용하면 모바일 사용자가 한 손으로 쉽게 음성 입력을 할 수 있고, 햅틱과 시각적 피드백으로 명확한 상태를 인지할 수 있으며, 화면이 꺼지지 않아 안정적으로 음성 인식이 작동합니다. 또한 취소 제스처로 잘못 시작한 녹음을 쉽게 취소할 수 있어 사용자 경험이 크게 향상됩니다.
실전 팁
💡 iOS Safari는 사용자 제스처 컨텍스트가 매우 엄격합니다. touchstart 핸들러 안에서 직접 recognition.start()를 호출해야 하며, 비동기 함수로 감싸면 작동하지 않을 수 있습니다.
💡 Android Chrome은 백그라운드로 가면 음성 인식이 중지됩니다. visibilitychange 이벤트로 감지하여 사용자에게 "앱이 백그라운드로 이동하여 녹음이 중지되었습니다"라고 안내하세요.
💡 푸시 투 토크 버튼은 충분히 크게 만드세요 (최소 48x48px). 모바일에서 작은 버튼은 터치하기 어렵고, 특히 움직이는 상황에서는 더욱 그렇습니다.
💡 햅틱 피드백은 선택적으로 제공하고 설정에서 끌 수 있게 하세요. 일부 사용자는 진동을 선호하지 않습니다.
💡 모바일 데이터 사용을 고려하세요. 음성 인식은 네트워크를 사용하므로, WiFi가 아닌 경우 사용자에게 데이터 사용 경고를 표시하는 것이 좋습니다.