본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 28. 00:38

실제 앱에 Gemini Nano를 온디바이스로 배포하기: 아무도 경고하지 않은 제약 사항들

요약

Gemini Nano를 활용하여 Android 앱에 온디바이스 AI를 구현할 때 직면하는 실질적인 기술적 제약 사항을 다룹니다. 백그라운드 실행 불가, 싱글 스레드 방식의 리소스 관리, 모델 버전별 출력 가변성 등 프로덕션 환경에서의 아키텍처 설계 주의점을 설명합니다.

핵심 포인트

  • 추론은 반드시 포그라운드 상태에서만 실행 가능함
  • AICore는 싱글 스레드로 동작하므로 단일 추론 큐를 통한 직렬화 필요
  • 모델 버전 변경에 따른 출력 불일치 가능성을 고려한 설계 필요
  • 배터리 사용량 제한 및 BUSY 오류에 대한 예외 처리 필수

저는 클라우드, 프롬프트 또는 출력이 기기를 전혀 떠나지 않고, 오직 휴대폰에서만 실행되는 Gemini Nano에 의해 일일 인사이트가 작성되는 Android 앱을 출시했습니다. 문서상으로는 통합 과정이 사소해 보였습니다. 하지만 실제 프로덕션 환경에서는 제 아키텍처를 재설계하게 만든 세 가지 까다로운 경계가 있었습니다. 이것은 제가 시작하기 전에 알았더라면 좋았을 기록입니다.

간략한 배경: Tawen은 Health Connect에서 수면, HRV(심박 변이도), 활동 데이터를 읽어와 기기에서 준비도 점수(0–100)를 계산한 다음, ML Kit GenAI Prompt API(Android의 온디바이스 파운데이션 모델용 시스템 서비스인 AICore를 기반으로 함)를 사용하여 해당 점수를 쉬운 영어로 설명합니다. 건강 데이터, 프롬프트 및 출력은 모두 기기에 머뭅니다.

실제로 문제가 되었던 부분은 다음과 같습니다.

1. 추론(Inference)은 포그라운드(Foreground)에서만 가능하며, 이는 강제됩니다

저의 첫 번째 설계는 사용자가 앱을 열었을 때 즉시 나타날 수 있도록 WorkManager 작업에서 그날의 내러티브를 미리 생성하는 방식이었습니다. 깔끔하고 관용적인 방식이었지만 완전히 틀렸습니다. GenAI API는 가시적인 UI 없이 호출하는 즉시(포그라운드 서비스에서 호출하는 경우를 포함하여) ErrorCode.BACKGROUND_USE_BLOCKED를 반환합니다. AICore는 앱이 최상위 포그라운드 애플리케이션이 아닌 한 의도적으로 추론을 거부합니다.

이것은 요청을 통해 우회할 수 있는 할당량(Quota)이 아니라 설계상의 제약 사항입니다. 따라서 아키텍처가 반전됩니다. 추론은 사용자의 눈앞에서 발생하며, 필요한 화면에 의해 트리거됩니다. 모델 없이 미리 계산할 수 있는 모든 것(아래에서 더 자세히 다룰 점수 자체 등)은 미리 계산해 두되, _내러티브(Narration)_는 관련 화면이 포그라운드로 올라올 때 실시간으로 생성됩니다. 만약 백그라운드에서 온디바이스 LLM을 "예열(warm up)"할 계획이라면, 작동하지 않을 것이라고 예상하십시오.

2. AICore는 사실상 싱글 스레드(Single-threaded)입니다 — 동시 실행 시 BUSY 반환

기기 내의 공유된 Gemini Nano 모델은 단일 리소스이며, AICore는 이에 대한 접근을 직렬화(Serialize)합니다. 거의 동시에 두 개의 추론(Inference) 호출을 실행하면 — 예를 들어, 각각 서사(Narrative)를 원하는 두 개의 Composable이 있다면 — 두 번째 호출은 ErrorCode.BUSY를 반환합니다. 또한 장기적인 과다 사용에 대해서는 PER_APP_BATTERY_USE_QUOTA_EXCEEDED 오류가 발생합니다.

이를 안정적으로 만든 해결책은 UI가 모델을 직접 호출하게 두지 않는 것이었습니다. 모든 Nano 요청은 **단일 소유자 추론 큐(Single-owner inference queue)**를 거칩니다. 하나의 코루틴(Coroutine)이 모델을 소유하고, 요청은 직렬화되며, 각 요청에는 엄격한 타임아웃(Timeout)이 설정됩니다. 호출자는 리소스를 차지하기 위해 경쟁하는 대신 결과를 기다립니다. 온디바이스 모델을 병렬로 확장할 수 있는 상태 비저장(Stateless) 클라우드 엔드포인트가 아니라, 하나의 직렬 장치(Single serial device)처럼 취급하십시오 (실제로 그러하기 때문입니다).

3. Nano 버전마다 출력이 다르므로, 안정성이 보장되어야 하는 항목을 모델에 맡기지 마세요

문서에는 다음과 같이 명시되어 있습니다: Gemini Nano의 버전이 다르면 동일한 프롬프트에 대해 서로 다른 출력을 반환할 수 있습니다. _서사(Narrative)_의 경우에는 산문(Prose)이므로 괜찮습니다. 하지만 이는 사용자가 매일 비교할 수 있는 어떤 것에 대해서도 모델이 신뢰할 수 있는 원천(Source of truth)이 될 수 없음을 의미합니다.

이 점이 앱의 핵심적인 아키텍처 결정으로 이어졌습니다: 점수는 결정론적(Deterministic)이어야 하며, 모델은 단지 그것을 설명(Narrate)할 뿐입니다. 단순한 규칙 기반 엔진(Rule-based engine)이 다섯 가지 가중치 신호로부터 준비도 점수(Readiness score)를 계산합니다. Gemini Nano는 그 점수_에 대한 설명을 작성할 뿐, 점수를 직접 계산하지는 않습니다. 이 방식의 이점은 다음과 같이 누적됩니다:

  • Nano를 사용할 수 있든 없든 숫자는 동일합니다.
  • Nano가 없는 기기(최신 하드웨어가 필요함)에서는 결정론적인 규칙 기반 설명이 그 자리를 대신하며 점수는 변하지 않습니다.
  • "AI가 투명한 계산 과정을 설명한다"는 방식은 "AI가 검사할 수 없는 숫자를 내뱉는다"는 방식보다 더 정직하고 디버깅하기 쉬운 형태입니다. 저는 Nano가 실제로 작성했을 때만 출력을 AI가 작성한 것으로 표시합니다. 규칙 기반 폴백(Fallback)은 결코 "AI"라고 부르지 않습니다. 이러한 정직함은 AI 자체보다 사용자들에게 더 중요한 요소임이 밝혀졌습니다.

과거의 나에게 해주고 싶은 말

  • 에러 코드를 먼저 읽고, 설계를 나중에 하세요. BACKGROUND_USE_BLOCKED, BUSY, 그리고 버전 변동성(version-variance)에 관한 노트는 예외적인 상황(edge cases)이 아닙니다. 그것이 바로 플랫폼의 형태입니다. 처음부터 이를 고려하여 설계했다면 코드를 다시 작성하는 수고를 덜었을 것입니다.
  • 결정론적 핵심(deterministic core)을 유지하세요. 모델은 부드럽고 모호한 언어적 부분만 담당하게 하세요. 안정적이고, 재현 가능하며, 비교 가능해야 하는 모든 것은 당신이 제어할 수 있는 코드 내에 있어야 합니다. 기본적으로 제공되는 폴백 경로(fallback path)만으로도 이러한 규율을 지킬 가치는 충분합니다.
  • 온디바이스 AI의 진정한 제품적 승리 요인은 마법이 아니라 개인정보 보호입니다. 이 작업을 수행하는 이유는 Nano가 클라우드 모델보다 똑똑하기 때문이 아닙니다(그렇지 않습니다). 누군가의 수면이나 심박수 데이터에 관한 문장이 해당 데이터가 휴대폰을 떠나지 않고도 생성될 수 있다는 점 때문입니다. 이를 중심으로 구축하면 아키텍처는 대부분 스스로 설계됩니다.

만약 ML Kit GenAI / Gemini Nano를 통합하고 있다면, 특히 포그라운드 전용(foreground-only) 및 단일 소유자 큐(single-owner-queue) 부분에 대해 함께 의견을 나누고 싶습니다. 공식 문서는 여기에서 확인할 수 있습니다. 위에서 언급한 모든 내용은 공식 문서가 당신을 충분히 대비시켜 주지 않는 부분들입니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0