최근 같이 공부하는 스터디팀에서 영어 단어 앱을 만들어 배포했는데요. Next.js로 웹을 구현하고 Flutter의 웹뷰를 이용해 제작/배포한 앱입니다.
영어 단어를 읽어주는 기능을 윈도우 함수 중 speechSynthesis로 구현했는데, 이 함수는 안드로이드 웹뷰에서는 지원되지 않는다고 합니다. iOS에서만 테스트를 하다보니 이런 부분을 체크하지 못한 것 같네요.
기존 코드를 보면 영어 단어 클릭시 아래와 같이 speechSynthesis를 이용해서 음성으로 표시되고 있었습니다.
window.speechSynthesis.cancel();
const speechMsg = new SpeechSynthesisUtterance();
speechMsg.rate = 1;
speechMsg.pitch = 1.2;
speechMsg.lang = "en-US";
speechMsg.text = itemWord.word;
window.speechSynthesis.speak(speechMsg);
애뮬레이터의 로그를 보면 이런 로그가 찍혀있습니다.
"Uncaught TypeError: Cannot read property 'cancel' of undefined",
이 문제를 해결하기 위해 Flutter의 TTS(Text To Speech) 패키지를 사용하기로 했습니다. TTS 패키지는 텍스트를 소리로 표현합니다.
이 패키지를 통해 iOS 에서는 기존 윈도우 함수를 사용하고 Android에서는 tts 함수를 사용하도록 처리했습니다.
먼저 패키지를 추가합니다.
flutter pub add flutter_tts
웹뷰 위젯안에 FlutterTts를 초기화하고, 접속 디바이스에 따라 채널명을 다르게 설정했습니다.
final FlutterTts tts = FlutterTts();
final String _webviewChannel =
Platform.isAndroid ? 'androidChannel' : 'iosChannel';
initState 메소드가 실행될 때 초기화된 Flutter tts의 언어와 스피칭 속도를 지정합니다.
한글을 읽고 싶은 경우는 setLanguage를 "ko-KR"로 변경하면 됩니다.
tts.setLanguage('en');
tts.setSpeechRate(0.4);
디바이스에 따라 설정된 채널명으로 자바스크립트 채널을 설정합니다. 그리고 메세지를 통해 받아온 값을 그대로 읽도록 했습니다.
..addJavaScriptChannel(_webviewChannel,
onMessageReceived: (JavaScriptMessage javaScriptMessage) {
tts.speak(javaScriptMessage.message);
})
웹 소스에서는 아래와 같이 분기처리가 되어있기 때문에 androidChannel이 있는 경우에만 위의 코드가 실행됩니다.
const { androidChannel } = window;
if (androidChannel) {
androidChannel.postMessage(itemWord.word);
} else {
window.speechSynthesis.cancel();
const speechMsg = new SpeechSynthesisUtterance();
speechMsg.rate = 1;
speechMsg.pitch = 1.2;
speechMsg.lang = "en-US";
speechMsg.text = itemWord.word;
window.speechSynthesis.speak(speechMsg);
}
참고자료