diff --git a/com.twin.app.shoptime/src/hooks/useWebSpeech.js b/com.twin.app.shoptime/src/hooks/useWebSpeech.js
index b7f73395..f608ad15 100644
--- a/com.twin.app.shoptime/src/hooks/useWebSpeech.js
+++ b/com.twin.app.shoptime/src/hooks/useWebSpeech.js
@@ -63,10 +63,12 @@ export const useWebSpeech = (isActive, onSTTText, config = {}) => {
dispatch(stopWebSpeech());
}, [dispatch]);
- // WebSpeech API 지원 여부 체크
+ // WebSpeech API 브라우저 지원 여부 체크
+ // - 브라우저가 SpeechRecognition API를 지원하는지만 확인
+ // - 마이크 접근 불가 등의 에러는 error 필드로 별도 처리
const isSupported =
- !webSpeech.error ||
- (typeof webSpeech.error === 'string' && !webSpeech.error.includes('not supported'));
+ typeof window !== 'undefined' &&
+ !!(window.SpeechRecognition || window.webkitSpeechRecognition);
return {
isInitialized: webSpeech.isInitialized,
diff --git a/com.twin.app.shoptime/src/views/SearchPanel/VoiceInputOverlay/VoiceInputOverlay.v2.jsx b/com.twin.app.shoptime/src/views/SearchPanel/VoiceInputOverlay/VoiceInputOverlay.v2.jsx
index 4ecb8ee6..6776d575 100644
--- a/com.twin.app.shoptime/src/views/SearchPanel/VoiceInputOverlay/VoiceInputOverlay.v2.jsx
+++ b/com.twin.app.shoptime/src/views/SearchPanel/VoiceInputOverlay/VoiceInputOverlay.v2.jsx
@@ -45,8 +45,14 @@ export const VOICE_MODES = {
MODE_4: 'mode4', // 추후 추가
};
-// NOINIT 모드 에러 메시지
-const NOINIT_ERROR_MESSAGE = 'Voice recognition is not supported on this device.';
+// NOINIT 모드 에러 메시지 - 에러 타입별로 구분
+const NOINIT_ERROR_MESSAGES = {
+ 'not-supported': 'Voice recognition is not supported on this browser.',
+ 'audio-capture': 'No microphone detected. Please connect a microphone.',
+ 'not-allowed': 'Microphone access denied. Please allow microphone permissions.',
+ 'service-not-allowed': 'Voice recognition service is not available.',
+ default: 'Voice recognition is not available.',
+};
// 음성인식 입력 모드 (VUI vs WebSpeech)
export const VOICE_INPUT_MODE = {
@@ -127,6 +133,8 @@ const VoiceInputOverlay = ({
const [sttResponseText, setSttResponseText] = useState('');
// Voice Version (어떤 음성 시스템을 사용할지 결정)
const [voiceVersion, setVoiceVersion] = useState(VOICE_VERSION.WEB_SPEECH);
+ // NOINIT 모드 에러 메시지
+ const [noInitErrorMessage, setNoInitErrorMessage] = useState(NOINIT_ERROR_MESSAGES.default);
// 🔊 Beep 소리 재생 함수 - zero dependencies
const playBeep = useCallback(() => {
@@ -338,15 +346,37 @@ const VoiceInputOverlay = ({
}
}, [interimText, currentMode, handleWakeWordDetected]);
- // WebSpeech가 지원되지 않을 때 NOINIT 모드로 전환
+ // WebSpeech가 지원되지 않거나 마이크 에러 발생 시 NOINIT 모드로 전환
useEffect(() => {
- if (isVisible && voiceVersion === VOICE_VERSION.WEB_SPEECH && isSupported === false) {
+ if (!isVisible || voiceVersion !== VOICE_VERSION.WEB_SPEECH) return;
+
+ // 브라우저가 WebSpeech를 지원하지 않는 경우
+ if (isSupported === false) {
if (DEBUG_MODE) {
console.log('⚠️ [VoiceInputOverlay.v2] WebSpeech not supported, switching to NOINIT mode');
}
+ setNoInitErrorMessage(NOINIT_ERROR_MESSAGES['not-supported']);
setCurrentMode(VOICE_MODES.NOINIT);
+ return;
}
- }, [isVisible, voiceVersion, isSupported]);
+
+ // 마이크 관련 에러 발생 시
+ if (error && typeof error === 'string') {
+ if (DEBUG_MODE) {
+ console.log('⚠️ [VoiceInputOverlay.v2] WebSpeech error detected:', error);
+ }
+
+ // 에러 타입 파싱 (error는 'audio-capture' 같은 문자열 또는 객체일 수 있음)
+ const errorType = typeof error === 'string' ? error : error.error || error.message || '';
+
+ // 마이크 접근 불가 에러들
+ if (['audio-capture', 'not-allowed', 'service-not-allowed'].some(e => errorType.includes(e))) {
+ const matchedError = ['audio-capture', 'not-allowed', 'service-not-allowed'].find(e => errorType.includes(e));
+ setNoInitErrorMessage(NOINIT_ERROR_MESSAGES[matchedError] || NOINIT_ERROR_MESSAGES.default);
+ setCurrentMode(VOICE_MODES.NOINIT);
+ }
+ }
+ }, [isVisible, voiceVersion, isSupported, error]);
// ⛔ 독립 테스트: WebSpeech API 호출 비활성화
// WebSpeech 모드로 전환되면 자동으로 음성 인식 시작
@@ -582,9 +612,9 @@ const VoiceInputOverlay = ({
return