본문 바로가기

Capacitor 앱에서 Gemini Nano 사용하는 방법

Android 온디바이스 AI를 위해 Capacitor 앱에서 Gemini Nano를 사용해 보세요: 기기 지원, AICore 체크, @capgo/capacitor-llm 스트리밍 및 Gemma 폴백 기능 포함.

Capacitor 앱에서 Gemini Nano 사용하는 방법

Capacitor 앱에서 Gemini Nano를 사용하려면 다음을 설치하세요: @capgo/capacitor-llm, 호출 setModel({ path: 'Gemini Nano' }) on Android, then create a chat and stream the reply through the textFromAi 이 플러그인은 Google ML Kit GenAI Prompt API를 통해 Gemini Nano와 통신하며, 모델이 Android AICore 시스템 서비스 내에서 실행되므로 텍스트가 기기 외부로 유출되지 않습니다.

이 가이드에서는 Gemini Nano의 작동 방식, 지원 기기, 정확한 플러그인 호출, 스트리밍, 할당량 및 제한 사항, 미지원 기기를 위한 Gemma 폴백, 그리고 동일한 code가 iOS의 Apple Intelligence에 도달하는 방법을 다룹니다.

Android에서 Gemini Nano가 작동하는 방식

Gemini Nano는 모바일용으로 설계된 Google의 가장 작은 Gemini 모델입니다. 앱에 직접 번들링되지 않으며, Android의 AICore 서비스가 모델을 다운로드, 업데이트 및 추론을 수행하며, 앱은 다음을 통해 이에 접근합니다: ML Kit GenAI API:

  • API 프롬프트: 자유 형식의 텍스트 프롬프트, 이 플러그인이 사용하는 API (com.google.mlkit:genai-prompt).
  • 사용 가능한 API: 요약, 교정, 재작성, 이미지 설명과 같은 고정된 작업.

Google이 제공하는 Prompt API는 SLA나 지원 종료 정책이 없는 베타 버전입니다. 릴리스마다 동작이 변경될 수 있으므로, 자체적인 래퍼(wrapper)를 만들어 통합하는 것을 권장합니다.

추론이 로컬에서 이루어지므로 클라우드 모델과는 다른 세 가지 장점이 있습니다. 오프라인 작동, 토큰당 비용 없음, 그리고 사용자 텍스트의 기기 내 유지입니다. 단점은 컨텍스트와 출력 길이가 짧은 소형 모델이라는 점이므로, 문장 재작성, 노트 요약, 메시지 분류, 필드 추출, 짧은 답장 초안 작성 등 적합한 작업에 활용하세요.

지원 기기 및 요구 사항

Google은 ML Kit GenAI 사이트에 Gemini Nano 버전(nano-v2, nano-v3, nano-v4)별로 API 지원 목록을 게시합니다. Prompt API의 경우 Pixel 9 이후 모델, 최신 Samsung Galaxy S 및 Z 플래그십, 그리고 OnePlus, OPPO, Xiaomi, vivo, Honor, realme, Motorola 등의 최신 플래그십을 지원합니다. 특정 기기에 기능을 약속하기 전에 반드시 최신 목록을 확인하세요.

지원되는 모든 기기에는 다음 사항도 필요합니다:

요구 사항 상세 정보
Android 버전 API 레벨 26 이상 (플러그인에서 확인)
부트로더 잠금 상태. Google은 언락된 부트로더에서는 GenAI API를 지원하지 않습니다.
AICore 준비 및 설정 중. 기기 설정이나 초기화 직후에는 완료까지 시간이 걸릴 수 있습니다.
앱 상태 추론 중에는 앱이 반드시 최상단 포그라운드 상태여야 합니다.

Nano 버전마다 동일한 prompt에 대해 다른 결과가 나올 수 있습니다. 사용하려는 각 버전별로 최소 한 대의 기기에서 prompt를 테스트하세요.

플러그인 설치

bun add @capgo/capacitor-llm
bunx cap sync

Capacitor의 메이저 버전을 따릅니다: Capacitor 8 버전에는 8.x를 사용하세요. Gemini Nano를 위해 Android manifest를 수정할 필요는 없습니다. 전체 레퍼런스: 플러그인 문서 및 플러그인 페이지.

1단계: Gemini Nano 로드 및 준비 상태 확인

setModel 전용 경로를 사용하여 'Gemini Nano' ML Kit에 모델 상태를 요청합니다. 상태를 확인할 수 있으면 resolve되고, 그렇지 않으면 상태 정보가 포함된 메시지와 함께 reject됩니다:

AICore 상태 플러그인 동작
사용 가능 setModel resolve됨, readinessChange emit함 ready
다운로드 가능 거부됨: “Gemini Nano는 다운로드 가능하지만 이 기기에는 다운로드되어 있지 않습니다”
다운로드 중 downloading 상태로 거부됨
사용 불가 거부됨: “이 기기에서는 Gemini Nano를 사용할 수 없습니다”
import { Capacitor } from '@capacitor/core';
import { CapgoLLM } from '@capgo/capacitor-llm';

export async function loadGeminiNano(): Promise<boolean> {
  if (Capacitor.getPlatform() !== 'android') return false;

  try {
    await CapgoLLM.setModel({
      path: 'Gemini Nano',
      temperature: 0.3,
      topk: 16,
    });
  } catch (error) {
    console.warn('Gemini Nano not ready:', error);
    return false;
  }

  const { readiness } = await CapgoLLM.getReadiness();
  return readiness === 'ready';
}

플러그인 자체가 AICore 모델 다운로드를 시작하지는 않습니다. 기기에서 다운로드 가능 상태를 보고하면 보통 다른 앱이나 시스템이 요청한 뒤에 모델이 도착하므로, 기능을 영구히 숨기지 말고 다음 실행 때 다시 확인하세요.

2단계: 채팅 생성 및 답변 스트리밍

채팅은 대화 내역을 유지합니다. Android에서는 sendMessage 생성이 시작되자마자 resolve되며, 이벤트로 텍스트가 전달됩니다. 다음 시점에 resolve되는 promise로 감싸세요: aiFinished:

import { CapgoLLM } from '@capgo/capacitor-llm';

// Android sends deltas. iOS Apple Intelligence sends the full text so far.
// This merge handles both.
function mergeChunk(current: string, chunk: string) {
  return chunk.startsWith(current) ? chunk : current + chunk;
}

export async function ask(
  chatId: string,
  message: string,
  onText: (textSoFar: string) => void,
): Promise<string> {
  let text = '';
  let finish!: (value: string) => void;
  let fail!: (error: Error) => void;
  const done = new Promise<string>((resolve, reject) => {
    finish = resolve;
    fail = reject;
  });

  const handles = await Promise.all([
    CapgoLLM.addListener('textFromAi', (event) => {
      if (event.chatId !== chatId) return;
      text = mergeChunk(text, event.text);
      onText(text);
    }),
    CapgoLLM.addListener('aiFinished', (event) => {
      if (event.chatId === chatId) finish(text);
    }),
    CapgoLLM.addListener('generationError', (event) => {
      if (!event.chatId || event.chatId === chatId) fail(new Error(event.error));
    }),
  ]);

  // If the app is backgrounded mid-answer, neither event may arrive
  const timeout = setTimeout(() => fail(new Error('Generation timed out')), 60_000);

  try {
    await CapgoLLM.sendMessage({ chatId, message });
    return await done;
  } finally {
    clearTimeout(timeout);
    await Promise.all(handles.map((h) => h.remove()));
  }
}

실제 기능에 적용해 보세요. 예를 들어 고객 리뷰에 대한 답장 초안 작성 기능:

const INSTRUCTIONS =
  'You write short, polite replies to customer reviews for a small shop. ' +
  'Answer each point, stay under 60 words, never promise refunds.';

export async function draftReply(review: string, render: (t: string) => void) {
  const { id } = await CapgoLLM.createChat();
  return ask(id, `${INSTRUCTIONS}\n\nReview:\n${review}\n\nReply:`, render);
}

createChat() 별도의 옵션이 없으므로 첫 메시지 시작 부분에 지침을 넣으세요. 작업마다 새 채팅을 생성하세요. Gemini Nano 방식에서는 플러그인이 메모리에 내역을 보관하고 매 메시지마다 함께 전송하므로, 긴 채팅은 입력 토큰을 빠르게 소모합니다.

설계 시 제한 사항

제한 값 조치 사항
입력 요청당 약 4,000 토큰 미만 (Google) 채팅은 짧게 유지하고, 긴 문서는 요약하세요
출력 이 플러그인은 Gemini Nano 응답을 256 토큰으로 제한합니다 짧은 답변을 요청하거나 긴 작업은 나누어 처리하세요
포그라운드 앱이 활성화된 상태에서만 추론을 수행합니다 백그라운드 작업에서 실행하지 마세요
할당량 앱별 할당량 및 BUSY 배터리 할당량 오류 디바운스(Debounce)를 사용하여 매 키 입력마다 생성되지 않도록 하세요
동시성 채팅당 한 번의 생성 스트리밍 중에는 전송 버튼 비활성화
속도 문단당 몇 초 소요 UI로 항상 스트리밍

할당량 오류를 일시적인 것으로 처리합니다. 에러 다이얼로그 대신 재시도 옵션을 보여주세요.

Fallback: Gemini Nano가 없을 때 Gemma 4 사용

현재 사용되는 대부분의 Android 폰에는 Gemini Nano가 없습니다. 동일한 플러그인으로 LiteRT-LM을 통해 오픈 Gemma 모델을 실행할 수 있으므로, API 하나로 유지할 수 있습니다. 모델 크기가 보통 1GB 이상으로 크기 때문에, 사용자가 동의한 후, 가급적 Wi-Fi 환경에서 download 하도록 하세요.

import { Preferences } from '@capacitor/preferences';
import { CapgoLLM } from '@capgo/capacitor-llm';

const GEMMA_URL =
  'https://huggingface.co/litert-community/gemma-4-E2B-it-litert-lm/resolve/main/gemma-4-E2B-it.litertlm?download=true';

export async function loadGemma(onProgress: (pct: number) => void) {
  const saved = await Preferences.get({ key: 'gemmaPath' });
  if (saved.value) {
    try {
      await CapgoLLM.setModel({ path: saved.value, modelType: 'litertlm', maxTokens: 4096 });
      return;
    } catch {
      // File missing or corrupt, download again
    }
  }

  const sub = await CapgoLLM.addListener('downloadProgress', (e) => onProgress(e.progress));
  try {
    const { path } = await CapgoLLM.downloadModel({
      url: GEMMA_URL,
      filename: 'gemma-4-E2B-it.litertlm',
    });
    await Preferences.set({ key: 'gemmaPath', value: path });
    await CapgoLLM.setModel({ path, modelType: 'litertlm', maxTokens: 4096 });
  } finally {
    await sub.remove();
  }
}

그 다음 시작 시 엔진을 선택합니다:

export async function initOnDeviceAI(userAcceptedDownload: boolean, onProgress: (p: number) => void) {
  if (await loadGeminiNano()) return 'gemini-nano';
  if (userAcceptedDownload) {
    await loadGemma(onProgress);
    return 'gemma';
  }
  return 'none';
}

둘 다 불가능한 경우, 해당 기능을 숨기거나 클라우드 모델을 호출하세요. 로컬 AI 때문에 핵심 플로우가 막히지 않도록 해야 합니다.

iOS에서도 동일한 code

iOS에서 플러그인은 Foundation Models framework를 통해 Apple Intelligence를 사용합니다. 코드 한 줄만 수정하세요:

await CapgoLLM.setModel({ path: 'Apple Intelligence' });

createChat, sendMessage, 이벤트는 동일하게 유지되며, mergeChunk 위의 헬퍼가 이미 iOS 스트리밍 형식을 처리합니다. 기기 요구 사항은 다릅니다(iOS 18 이상 및 Apple Intelligence 지원 기기). 관련 내용은 Capacitor 앱에서 Apple Intelligence를 사용하는 가이드를 참고하세요.

테스트

  • 지원되는 실제 기기를 사용하세요. 에뮬레이터는 Gemini Nano를 위한 AICore를 제공하지 않습니다.
  • 다음 내용을 setModel 거절 메시지를 로그로 남기세요. AICore가 보고하는 정확한 상태를 알려줍니다.
  • 새로 설정하거나 초기화한 기기라면, 온라인 상태에서 AICore의 초기 설정이 완료될 때까지 기다린 후 다시 시도하세요.
  • 지원하는 가장 낮은 Nano 버전에서 프롬프트를 테스트하세요. 짧고 명확한 프롬프트가 버전 간에 더 안정적입니다.

문제 해결

“이 기기에서는 Gemini Nano를 사용할 수 없습니다.” 기기가 Google 목록에 없거나, 부트로더가 언락되었거나, 아직 AICore 설정이 완료되지 않은 경우입니다.

“Gemini Nano는 다운로드 가능하지만 아직 다운로드되지 않았습니다.” 기기는 지원하지만 모델이 없습니다. Gemma 폴백을 제공하거나 나중에 다시 시도하세요.

“채팅 세션을 찾을 수 없습니다.” 호출했습니다: sendMessage 새로운 것이 생성되기 전에 만든 채팅과 함께 setModel. 모델을 로드하면 기존 채팅이 지워지므로 새로 만드세요.

“모델이 준비되지 않음” setModel 실패했거나 호출되지 않았습니다 createChat.

출력이 문장 중간에 끊깁니다. 출력 제한에 도달했습니다. 더 짧은 답변을 요청하거나 작업을 나누세요.

앱이 백그라운드로 전환되면 오류가 발생합니다. 추론은 포그라운드에서만 가능합니다. 일시 중지 시 UI 상태를 취소하고 사용자가 다시 시도할 수 있게 하세요.

개인정보 보호 및 스토어 심사

Gemini Nano를 사용하면 프롬프트와 응답이 기기에 머물기 때문에 데이터 안전성 양식 작성이 간편해집니다. 다만 Google ML Kit 약관에는 여전히 메트릭 처리 관련 내용이 포함되어 있으므로, 개인정보 처리방침에 온디바이스 AI 사용에 대해 명시해야 합니다. Gemma 폴백(fallback)을 추가하는 경우 모델 다운로드가 Hugging Face를 통해 이루어지므로 해당 네트워크 요청도 함께 언급해 주세요.

AI 기능의 프롬프트와 UI 문구는 자주 변경됩니다. 출시 후 이를 조정해야 할 때, Capgo Live Update 사용하기 스토어 심사를 기다릴 필요 없이 새로운 웹 code를 배포할 수 있습니다. 하이브리드 앱의 AI 활용에 대한 더 자세한 내용은 다음을 참고하세요. 왜 Capacitor가 AI 모바일 앱에 적합한가.

마치며

Gemini Nano는 지원되는 Android 기기에서 Capacitor 앱에 무료이며 프라이빗한 오프라인 텍스트 생성 기능을 제공합니다. Capacitor를 사용하여 setModel({ path: 'Gemini Nano' }), 를 통해 스트리밍하고 textFromAi, 프롬프트와 출력값을 짧게 유지하며, 포그라운드 및 할당량 규칙을 준수하세요. 또한 해당 기능이 없는 기기를 위해 Gemma 또는 클라우드 폴백을 준비해 두는 것이 좋습니다.

Capacitor 앱을 위한 Live Updates

웹 레이어 버그가 라이브 상태일 때, 앱 스토어 승인을 며칠씩 기다리지 말고 Capgo를 통해 수정 사항을 배포하세요. 네이티브 변경 사항은 정상적인 리뷰 절차를 따르는 동안, 사용자는 백그라운드에서 업데이트를 받습니다.

블로그 최신 글

Capgo는 진정한 프로급 모바일 앱을 만드는 데 필요한 최고의 인사이트를 제공합니다.