[Android] SpeechRecognizer 별다른 이유 없이 미작동할 때 확인해봐야할 것

ideal dev·4일 전
0

✅ Velog 포스트 구성

🧠 SpeechRecognizer 이유없는 오류

안드로이드로 Speech To Text(STT) 기능을 개발하고 있었다.
안드로이드의 내장 SpeechRecognizer 를 이용해서 음성을 텍스트로 변환하는 STT 기능이었고,
별다른 오류 없이 잘 동작하고 있었다.

그런데 어느 날 갑자기 STT가 작동하지 않았다.
로그에도 별다른 에러가 없고, 앱이 죽는 것도 아니었다.
심지어 태블릿 내의 다른 앱에서는 음성 인식이 잘 작동하였다.
“왜 안 되지…?”
이게 시작이었다.


🧩 문제 상황

당시 프로젝트 STT 기능은
Overlay로 띄어진 서비스(Service) 안에서 돌아가도록 설계되어 있었다.

문제가 생긴 시점에 바뀐 건 단 두 가지였다.

  1. 코드 수정
  2. 새로운 안드로이드 태블릿 기기

당연히 내 잘못이라고 생각했다.
코드가 계속 바뀌고 있었으니까.

그래서 반나절 동안 코드를 뒤집어가며 삽질했다.
하지만 이상하게도, 아무리 수정해도 STT는 시작되지 않았다.


🧪 디버깅 과정

일단 SpeechRecongnizer의 callback 함수들에 로그 찍어봤다.

I/SpeechRecognizer: onReadyForSpeech
I/SpeechRecognizer: onRmsChanged: -2.0
I/SpeechRecognizer: onError: 7

onError만 계속 반환한다.

흠...
SpeechRecoginzer의 Intent도 수정해보고
delay도 넣어보는 등
코드를 이리 저리 수정해도 안되어,

동일한 코드를 Android 에뮬레이터 태블릿에서 실행해봤다.
그랬더니 정상적으로 되는 것이다...

두 기기의 다른 환경은 바로바로, Android 버전이었다.

기기Android 버전STT 결과
태블릿 AAndroid 7.0✅ 정상 동작
태블릿 BAndroid 8.0❌ STT 안 됨

어머나. Android 8.0 버전에서 뭔가 달라진 게 있는 거다.


🔍 원인 분석

문제의 코드는 대략 이런 구조였다.

class SttService : Service() {

    private lateinit var speechRecognizer: SpeechRecognizer

    override fun onCreate() {
        super.onCreate()
        speechRecognizer = SpeechRecognizer.createSpeechRecognizer(this)
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val recognizerIntent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH).apply {
            putExtra(RecognizerIntent.EXTRA_LANGUAGE, "ko-KR")
        }
        speechRecognizer.startListening(recognizerIntent)
        return START_STICKY
    }
}

Android 7.0에서는 문제없이 동작하지만,
8.0에서는 이 코드가 조용히 작동하지 않는다.

왜냐하면 Android 8.0 (API 26)부터는 백그라운드에서 실행되는 Service의 정책이 바뀌었기 때문이다.
Foreground Service로 전환하지 않으면, STT 동작 중간에 서비스가 바로 종료되어버린다.

즉, STT가 실행조차 되지 않고 조용히 사라지는 것처럼 보이는 현상이 발생한다.

심지어 이 정보를 알고 있었고
다른 프로젝트에서 활용했던 지식인데,
오류를 만나니까 연관지을 생각을 전혀 못했다.


⚙️ 해결 방법

Service를 단순 실행하는 대신, Foreground Service로 실행해야 한다.
즉, Notification을 포함한 실행 컨텍스트를 만들어야 한다.

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val notificationChannelId = "stt_channel"
        val channel = NotificationChannel(
            notificationChannelId,
            "STT Service Channel",
            NotificationManager.IMPORTANCE_LOW
        )
        val manager = getSystemService(NotificationManager::class.java)
        manager.createNotificationChannel(channel)

        val notification = Notification.Builder(this, notificationChannelId)
            .setContentTitle("음성 인식 중...")
            .setSmallIcon(android.R.drawable.ic_btn_speak_now)
            .build()

        startForeground(1, notification)
    }

    val recognizerIntent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH).apply {
        putExtra(RecognizerIntent.EXTRA_LANGUAGE, "ko-KR")
    }
    speechRecognizer.startListening(recognizerIntent)

    return START_STICKY
}

이렇게 바꾸고 나니, Android 8.0에서도 STT가 정상 동작했다.


🧾 해결 후 로그 변화

문제가 있었을 때(Foreground 설정이 없을 때) 로그는 아래처럼
잠깐 실행 후 바로 중단되는 형태였다.

I/SpeechRecognizer: onReadyForSpeech
I/SpeechRecognizer: onRmsChanged: -2.0
I/SpeechRecognizer: onError: 7

Foreground Service로 수정 후에는 아래처럼 정상적인 순서로 로그가 찍혔다.

I/SpeechRecognizer: startListening called
I/SpeechRecognizer: onReadyForSpeech
I/SpeechRecognizer: onBeginningOfSpeech
I/SpeechRecognizer: onResults
I/SpeechRecognizer: destroy

🧰 추가 - 권한 관련 이슈 정리

STT를 사용할 때, 권한 설정이 올바르지 않아도 비슷한 증상이 나타날 수 있다.
아래 두 가지는 반드시 체크해야 한다.

1️⃣ RECORD_AUDIO 권한

Manifest에 다음을 선언해야 한다.

<uses-permission android:name="android.permission.RECORD_AUDIO" />

그리고 Android 6.0 (Marshmallow) 이상에서는 런타임 권한 요청도 필요하다.

if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
    != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(
        this,
        arrayOf(Manifest.permission.RECORD_AUDIO),
        100
    )
}

2️⃣ 네트워크 권한 (온라인 인식 시)

SpeechRecognizer는 내부적으로 Google 서버를 호출하므로,
다음 권한도 반드시 포함되어야 한다.

<uses-permission android:name="android.permission.INTERNET" />

3️⃣ 서비스 실행 시 주의점

  • Android 8.0 이상에서는 startForegroundService()로 실행해야 함
  • Notification을 만들어야 함
  • 그렇지 않으면 Context.startForegroundService() did not then call Service.startForeground() 오류 발생 가능

🧩 요약

항목내용
문제SpeechRecognizer가 Android 8.0에서 작동하지 않음
원인Foreground Service 설정 누락
해결Notification과 함께 Foreground로 실행
추가RECORD_AUDIO / INTERNET 권한 필수

💭 마무리하며

이번 문제를 겪으면서 다시 한 번 느꼈다.
“코드가 틀렸을 거야”라는 생각이 얼마나 큰 함정인지.

다음엔 비슷한 문제가 생기면,
또 놓치고 삽질할까봐 정리해두고 싶었다.

“혹시 안드로이드 정책이 바뀐 건 아닐까?” 도 고려해야겠다.

삽질은 늘 힘들지만,
해결했을 때의 즐거움과
그 과정에서의 성장은 짜릿하다.

도파민 팡팡.

0개의 댓글