이 글의 성격은 무엇인가요?
질문 / 문제 해결
내용을 설명해주세요
안녕하세요.
Apps in Toss WebView에서 speechSynthesis 테스트를 진행했습니다.
결과는 아래와 같습니다.
window exists: true
speechSynthesis: false
즉 window 객체는 존재하지만 speechSynthesis API 자체가 없는 것으로 확인됩니다.
Chrome 브라우저에서는 정상 동작하지만 Apps in Toss 내에서는 동작하지 않습니다.
현재 Apps in Toss WebView에서 speechSynthesis가 공식적으로 지원되는지 확인 부탁드립니다.
Dylan
2
안녕하세요 
웹 표준 api 사용이 가능합니다.
아래 테스트한 코드 공유드립니다.
import { useCallback, useEffect, useState } from 'react';
export function SpeechSynthesisExample() {
const [text, setText] = useState('안녕하세요, TTS 테스트입니다.');
const [status, setStatus] = useState('대기');
const [voices, setVoices] = useState<SpeechSynthesisVoice[]>([]);
const isSupported = typeof window !== 'undefined' && 'speechSynthesis' in window;
const loadVoices = useCallback(() => {
if (!isSupported) return;
setVoices(window.speechSynthesis.getVoices());
}, [isSupported]);
useEffect(() => {
if (!isSupported) {
setStatus('미지원 (speechSynthesis 없음)');
return;
}
loadVoices();
window.speechSynthesis.addEventListener('voiceschanged', loadVoices);
return () => {
window.speechSynthesis.removeEventListener('voiceschanged', loadVoices);
window.speechSynthesis.cancel();
};
}, [isSupported, loadVoices]);
const handleSpeak = () => {
if (!isSupported) return;
window.speechSynthesis.cancel();
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = 'ko-KR';
const koVoice = voices.find((v) => v.lang.startsWith('ko'));
if (koVoice) {
utterance.voice = koVoice;
}
utterance.onstart = () => setStatus('재생 중');
utterance.onend = () => setStatus('완료');
utterance.onerror = (e) => setStatus(`에러: ${e.error}`);
window.speechSynthesis.speak(utterance);
};
const handleStop = () => {
if (!isSupported) return;
window.speechSynthesis.cancel();
setStatus('중지됨');
};
return (
<div style={{ padding: 16 }}>
<h2 style={{ fontSize: 16, margin: '0 0 8px' }}>speechSynthesis (TTS)</h2>
<p style={{ fontSize: 13, color: '#666', margin: '0 0 12px' }}>
상태: <strong>{status}</strong> · 한국어 voice:{' '}
{voices.filter((v) => v.lang.startsWith('ko')).length}개
</p>
<textarea
value={text}
onChange={(e) => setText(e.target.value)}
rows={2}
style={{
width: '100%',
boxSizing: 'border-box',
padding: 8,
fontSize: 13,
borderRadius: 8,
border: '1px solid #ddd',
marginBottom: 8,
}}
/>
<div style={{ display: 'flex', gap: 8 }}>
<button
type="button"
onClick={handleSpeak}
disabled={!isSupported}
style={{ flex: 2, padding: '10px 0', fontSize: 14, fontWeight: 'bold' }}
>
말하기
</button>
<button
type="button"
onClick={handleStop}
disabled={!isSupported}
style={{ flex: 1, padding: '10px 0', fontSize: 13 }}
>
중지
</button>
</div>
</div>
);
}
안녕하세요.
안내주신 대로 확인해보았습니다.
현재 Apps in Toss 테스트 환경에서 아래 코드로 확인 시
‘speechSynthesis’ in window
결과가 false로 확인됩니다.
실제 행보카다의 speak 함수는 아래처럼 구현되어 있으며,
if (typeof window === ‘undefined’ || !(‘speechSynthesis’ in window)) {
return;
}
speechSynthesis 자체가 존재하지 않아 음성 재생 로직까지 진입하지 못하고 있습니다.
디버그 결과:
window exists: true
speechSynthesis: false
speechSynthesis API가 지원된다고 안내해주셨는데,
현재 Apps in Toss WebView에서는 window.speechSynthesis 자체가 노출되지 않고 있습니다.
확인 부탁드립니다.
Dylan
4
혹시 사용하신 코드를 공유해주실 수 있을까요 ?
const speak = (text: string, lang: string = ‘en-US’) => {
if (typeof window === ‘undefined’ || !(‘speechSynthesis’ in window)) {
return;
}
const cleaned = text.replace(/~/g, ‘’);
const doSpeak = () => {
const voices = speechSynthesis.getVoices();
const utterance = new SpeechSynthesisUtterance(cleaned);
utterance.lang = lang;
utterance.rate = lang === 'en-US' ? 0.8 : 0.9;
const match =
voices.find(v => v.lang === lang) ||
voices.find(v => v.lang.startsWith(lang.split('-')[0]));
if (match) {
utterance.voice = match;
}
speechSynthesis.speak(utterance);
};
if (speechSynthesis.getVoices().length === 0) {
speechSynthesis.onvoiceschanged = () => {
doSpeak();
};
} else {
doSpeak();
}
};
현재 행보카다에서 사용 중인 TTS 코드입니다.
추가로 Apps in Toss 테스트 환경에서 아래 결과를 확인했습니다.
window exists: true
speechSynthesis: false
즉,
‘speechSynthesis’ in window
결과가 false로 확인되어 현재 speak 함수가 return 처리되고 있습니다.
코드상 문제인지, Apps in Toss WebView 환경 차이인지 확인 부탁드립니다.
Dylan
6
android에서 해당 현상 재현되네요 조금 더 확인해보겠습니다!
Dylan
7
@sodongzagka 님, 확인해보니 안드로이드는 웹뷰 자체에서 해당 API 지원을 하지 않는 것 같아요 
참고한 문서 공유드려요.