본문 바로가기

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

Android 온디바이스 AI를 위한 Capacitor 앱에서 Gemini Nano 사용하기: 기기 지원, AICore 확인, @capgo/capacitor-llm 스트리밍, Gemma 폴백.

Capacitor 모바일 앱에서 실행되는 AI 기능 일러스트레이션

Capacitor 앱에서 Gemini Nano를 사용하려면 다음을 설치하세요 @capgo/capacitor-llm, 호출 setModel({ path: 'Gemini Nano' }) Android에서 실행한 후, 채팅을 생성하고 응답을 스트리밍하려면 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나 비공식화 정책 없이 베타로 제공하므로 릴리스 간 동작 변경을 예상하고 자체 래퍼 뒤에 통합을 유지하세요.

추론이 로컬에서 수행되므로 클라우드 모델로는 제공할 수 없는 세 가지 이점을 얻습니다: 오프라인 작동, 토큰당 비용 없음, 사용자 텍스트가 기기에 유지됩니다. 단, 컨텍스트와 출력이 짧은 작은 모델이라는 트레이드오프가 있으므로 재작성, 노트 요약, 메시지 분류, 필드 추출, 짧은 답변 초안 작성과 같이 적합한 작업을 선택하세요.

지원되는 기기 및 요구 사항

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 준비 및 설정 중. 기기 설정이나 초기화 직후에는 완료까지 시간이 걸릴 수 있습니다.
앱 상태 inference 중에는 앱이 최상단 포그라운드 앱이어야 합니다.

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는 다운로드 가능하지만 이 기기에서는 다운로드되지 않음”
다운로드 중 다운로드 중 상태로 거부
사용 불가 거부: “이 기기에서는 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 모델 다운로드를 직접 시작하지 않습니다. 기기가 downloadable 상태라고 보고되면, 다른 앱이나 시스템이 요청한 후 모델이 설치되는 경우가 많으므로 기능을 영구적으로 숨기기보다 다음 실행 시 다시 확인하세요.

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 적용, 키 입력마다 생성하지 않기
동시성 채팅당 1회 생성 스트리밍 중 전송 버튼 비활성화
속도 단락 하나에 수 초 소요 항상 UI에 스트리밍

쿼터 오류는 일시적 오류로 취급하세요. 에러 다이얼로그 대신 재시도 옵션을 표시하세요.

폴백: Gemini Nano가 없을 때 Gemma 4 사용

현재 사용 중인 대부분의 Android 폰에는 Gemini Nano가 없습니다. 같은 플러그인으로 LiteRT-LM을 통해 오픈 Gemma 모델을 실행할 수 있어 하나의 API를 유지할 수 있습니다. 모델 크기가 1GB를 넘는 경우가 많으므로, 사용자가 동의한 후에만 다운로드하고, 가능하면 Wi-Fi 환경에서 진행하세요.

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 프레임워크를 통해 Apple Intelligence를 사용합니다. 한 줄만 변경하세요:

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

createChat, sendMessage, 이벤트는 그대로 유지되고, mergeChunk 위의 헬퍼가 이미 iOS 스트리밍 형식을 처리합니다. 기기 요구 사항은 다릅니다(iOS 26 및 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 폴백을 추가하면 모델을 Hugging Face에서 다운로드하므로 해당 네트워크 요청도 언급하세요.

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

마무리

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

Capacitor 앱용 Live updates

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

블로그 최신 글

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