반응형
3편까지 개발 환경에서 Gemini를 붙였습니다. 4편은 실제 사용자에게 배포하기 전 해야 하는 것들입니다. App Check 없이 공개하면 누구나 내 API 할당량을 소진할 수 있습니다. Remote Config 없이 배포하면 모델 deprecation 때마다 앱 업데이트가 강제됩니다.
[4편 핵심 요약]
→ App Check: Gemini API를 인가된 앱만 사용하도록 보호 — 공개 배포 전 필수
→ 프로바이더: Android=Play Integrity / iOS=App Attest / Web=reCAPTCHA Enterprise
→ Vertex AI 전환: GoogleAIBackend() → VertexAIBackend() 한 줄 교체 — Blaze 플랜 필요
→ Remote Config: 모델명·파라미터·프롬프트를 앱 업데이트 없이 원격 변경
→ 프로덕션 모델명: auto-updated alias(gemini-3.1-flash-lite) 금지 → 고정 버전 사용
→ AI 모니터링: Firebase 콘솔 AI Logic 탭 → 요청수/지연/에러/토큰 대시보드
→ 비용 알림: Blaze 플랜 전환 후 Budget Alert 반드시 설정
→ 개발/스테이징/프로덕션: 별도 Firebase 프로젝트 필수
실전 1 — Firebase App Check 설정
공개 앱에 App Check 없으면 누구나 앱을 리버스 엔지니어링해서 내 Gemini 할당량을 소진할 수 있습니다. 공식 문서가 "가능한 빨리, 개발 중에도" 설정을 강권하는 이유입니다.
Android — Play Integrity
// app/build.gradle.kts
dependencies {
implementation(platform("com.google.firebase:firebase-bom:34.13.0"))
implementation("com.google.firebase:firebase-ai")
implementation("com.google.firebase:firebase-appcheck-playintegrity") // 추가
}
// MyApplication.kt — Application 클래스에서 초기화
import com.google.firebase.Firebase
import com.google.firebase.appcheck.appCheck
import com.google.firebase.appcheck.playintegrity.PlayIntegrityAppCheckProviderFactory
import com.google.firebase.initialize
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// Firebase 초기화
Firebase.initialize(this)
// App Check 초기화 — Play Integrity (프로덕션)
Firebase.appCheck.installAppCheckProviderFactory(
PlayIntegrityAppCheckProviderFactory.getInstance()
)
}
}
// 개발 환경에서는 DebugAppCheckProviderFactory 사용
// AndroidManifest.xml에 Application 등록 필수
// <application android:name=".MyApplication" ...>
// 개발 중 디버그 토큰 활성화 (BuildConfig 활용)
if (BuildConfig.DEBUG) {
Firebase.appCheck.installAppCheckProviderFactory(
DebugAppCheckProviderFactory.getInstance()
)
} else {
Firebase.appCheck.installAppCheckProviderFactory(
PlayIntegrityAppCheckProviderFactory.getInstance()
)
}
// ⚠️ 주의: Play Integrity는 Play Store에 등록된 앱만 동작
// → 개발 중에는 디버그 토큰 필수
웹 — reCAPTCHA Enterprise
// lib/firebase.ts — reCAPTCHA Enterprise 추가
import { initializeApp } from "firebase/app";
import { initializeAppCheck, ReCaptchaEnterpriseProvider } from "firebase/app-check";
import { getAI, getGenerativeModel, GoogleAIBackend } from "firebase/ai";
const app = initializeApp(firebaseConfig);
// App Check 초기화 — reCAPTCHA Enterprise
// Firebase 콘솔에서 reCAPTCHA Enterprise 키 발급 필요
const appCheck = initializeAppCheck(app, {
provider: new ReCaptchaEnterpriseProvider(
process.env.NEXT_PUBLIC_RECAPTCHA_ENTERPRISE_SITE_KEY!
),
isTokenAutoRefreshEnabled: true // 토큰 자동 갱신
});
// AI Logic 초기화 (App Check 자동 연동)
const ai = getAI(app, { backend: new GoogleAIBackend() });
export const model = getGenerativeModel(ai, { model: "gemini-3.1-flash-lite" });
// 개발 환경 디버그 토큰 설정 (브라우저 콘솔에서 토큰 확인)
// next.config.ts
export default {
env: {
// 개발 환경에서만 활성화
NEXT_PUBLIC_FIREBASE_APPCHECK_DEBUG_TOKEN:
process.env.NODE_ENV === "development"
? process.env.APPCHECK_DEBUG_TOKEN
: undefined,
}
}
// lib/firebase.ts — 환경별 분기
if (process.env.NODE_ENV === "development") {
// @ts-ignore — 전역 디버그 토큰 설정
self.FIREBASE_APPCHECK_DEBUG_TOKEN =
process.env.NEXT_PUBLIC_FIREBASE_APPCHECK_DEBUG_TOKEN || true;
}
Firebase 콘솔에서 App Check 강제 적용
[App Check 활성화 순서]
1. Firebase 콘솔 → App Check → 앱 등록
2. 프로바이더 선택 (Play Integrity / App Attest / reCAPTCHA)
3. 디버그 토큰 생성 (개발용)
4. "강제 적용" 전에 반드시 모니터링 기간 운영
강제 적용 전 모니터링:
→ App Check 탭에서 "검증됨" vs "검증 안 됨" 비율 확인
→ 정상 사용자 요청이 검증됨으로 나올 때까지 대기
→ 검증 안 됨이 일정 비율 이상 남아있으면 원인 분석 후 강제 적용
⚠️ 강제 적용 실수 시: 정상 사용자 요청도 차단될 수 있음
→ 모니터링 → 확인 → 강제 적용 순서 엄수
실전 2 — Vertex AI 백엔드 전환
유저가 늘어나거나 엔터프라이즈 SLA가 필요하면 Vertex AI로 전환합니다. 코드 변경은 최소화됩니다.
Android 전환
// 전환 전 (Gemini Developer API — 무료 티어)
val model = Firebase.ai(backend = GenerativeBackend.googleAI())
.generativeModel("gemini-3.1-flash-lite")
// 전환 후 (Vertex AI — Blaze 플랜 필요)
val model = Firebase.ai(backend = GenerativeBackend.vertexAI())
.generativeModel("gemini-3.1-flash-lite")
// ↑ 이 한 줄만 바꾸면 됨
// 위치(리전) 지정 — Vertex AI에서만 필요
val model = Firebase.ai(
backend = GenerativeBackend.vertexAI(),
location = "us-central1" // 기본값. asia-northeast3(서울) 등 지정 가능
).generativeModel("gemini-3.1-flash-lite")
// Remote Config로 리전 동적 설정 (다음 실전에서 자세히)
val location = remoteConfig.getString("vertex_location") // "us-central1"
val model = Firebase.ai(
backend = GenerativeBackend.vertexAI(),
location = location
).generativeModel(remoteConfig.getString("gemini_model"))
웹 전환
// 전환 전 (Gemini Developer API)
import { GoogleAIBackend } from "firebase/ai";
const ai = getAI(app, { backend: new GoogleAIBackend() });
// 전환 후 (Vertex AI)
import { VertexAIBackend } from "firebase/ai";
const ai = getAI(app, { backend: new VertexAIBackend() });
// ↑ import + 생성자 이름만 바꾸면 됨
[Gemini Developer API vs Vertex AI 선택 기준]
Gemini Developer API (무료 → Spark/Blaze):
→ 무료 티어로 시작 (신용카드 불필요)
→ 개인 프로젝트, 초기 스타트업
→ 빠른 프로토타이핑
→ 제한: 특정 모델만 지원, 데이터 위치 제한 없음
→ 무료 한도: gemini-3.1-flash-lite 1,500 req/일, 32,000 TPM
Vertex AI (Blaze 플랜 필요):
→ GCP 청구 계정 연결
→ 데이터 위치 요구사항 있는 경우 (EU, APAC 등)
→ 엔터프라이즈 SLA 필요
→ GCP 다른 서비스(BigQuery, Cloud Run 등)와 통합
→ 더 많은 모델 선택지
→ $300 GCP 신규 크레딧 사용 가능 (Gemini Developer API 무관)
실전 3 — Remote Config로 모델 동적 관리
앱 업데이트 없이 모델명, 파라미터, 프롬프트를 원격으로 변경합니다. 공식 문서가 "강력히 권장"하는 패턴입니다.
Android — Remote Config + Gemini 통합
// app/build.gradle.kts
dependencies {
implementation(platform("com.google.firebase:firebase-bom:34.13.0"))
implementation("com.google.firebase:firebase-ai")
implementation("com.google.firebase:firebase-config") // Remote Config 추가
}
// GeminiRepository.kt
import com.google.firebase.Firebase
import com.google.firebase.ai.ai
import com.google.firebase.ai.type.GenerativeBackend
import com.google.firebase.ai.type.generationConfig
import com.google.firebase.remoteconfig.remoteConfig
import com.google.firebase.remoteconfig.remoteConfigSettings
class GeminiRepository {
private val remoteConfig = Firebase.remoteConfig
init {
// Remote Config 기본값 설정 (오프라인/초기값 보장)
remoteConfig.setDefaultsAsync(
mapOf(
"gemini_model" to "gemini-3.1-flash-001", // 안정 버전 고정
"gemini_temperature" to 0.7,
"gemini_max_tokens" to 1024,
"gemini_system_prompt" to "당신은 친절한 AI 어시스턴트입니다.",
"vertex_location" to "us-central1",
)
)
// 최소 갱신 주기 설정 (기본 12시간)
val settings = remoteConfigSettings {
minimumFetchIntervalInSeconds = 3600 // 1시간
}
remoteConfig.setConfigSettingsAsync(settings)
}
// Remote Config 값 fetch + activate
suspend fun fetchConfig() {
remoteConfig.fetchAndActivate().await()
}
// Remote Config 값으로 모델 생성
fun buildModel() = Firebase.ai(backend = GenerativeBackend.googleAI())
.generativeModel(
modelName = remoteConfig.getString("gemini_model"),
generationConfig = generationConfig {
temperature = remoteConfig.getDouble("gemini_temperature").toFloat()
maxOutputTokens = remoteConfig.getLong("gemini_max_tokens").toInt()
},
systemInstruction = content {
text(remoteConfig.getString("gemini_system_prompt"))
}
)
}
// ViewModel에서 사용
class ChatViewModel : ViewModel() {
private val repository = GeminiRepository()
private lateinit var model: GenerativeModel
init {
viewModelScope.launch {
repository.fetchConfig() // 앱 시작 시 최신 config 가져오기
model = repository.buildModel()
}
}
}
Firebase 콘솔에서 Remote Config 설정
[콘솔에서 파라미터 설정]
Firebase 콘솔 → Remote Config → 파라미터 추가
파라미터 이름: gemini_model
기본값: gemini-3.1-flash-001 ← 안정 버전
조건 추가 예시:
→ 베타 테스터 그룹: gemini-3.1-pro-preview (최신 모델 먼저 테스트)
→ 국가 = KR: gemini-3.1-flash-001 (지역별 모델 최적화)
→ 앱 버전 < 2.0: gemini-2.5-flash (구버전 호환)
변경 후 "변경사항 게시" → 즉시 적용
→ 앱 업데이트 없이 모든 사용자에게 반영
// 웹 — Remote Config
import { getRemoteConfig, getValue, fetchAndActivate } from "firebase/remote-config";
const remoteConfig = getRemoteConfig(app);
remoteConfig.settings.minimumFetchIntervalMillis = 3600000; // 1시간
// 기본값 설정
remoteConfig.defaultConfig = {
gemini_model: "gemini-3.1-flash-001",
gemini_temperature: "0.7",
gemini_max_tokens: "1024",
};
// fetch + activate
await fetchAndActivate(remoteConfig);
// 값으로 모델 초기화
const modelName = getValue(remoteConfig, "gemini_model").asString();
const temperature = parseFloat(getValue(remoteConfig, "gemini_temperature").asString());
const model = getGenerativeModel(ai, {
model: modelName,
generationConfig: { temperature, maxOutputTokens: 1024 }
});
[Remote Config 활용 시나리오]
1. 모델 deprecation 대응:
gemini-2.5-flash 종료 예고 →
Remote Config에서 gemini-3.1-flash-lite로 변경 →
앱 업데이트 없이 즉시 모든 사용자 마이그레이션
2. 비용 최적화:
트래픽 급증 시 gemini-3.1-pro → gemini-3.1-flash-lite로 다운그레이드
비용 안정화 후 복원
3. A/B 테스트:
temperature 0.5 vs 0.9 → 사용자 반응 측정
Google Analytics 연동으로 지표 추적
4. 단계적 롤아웃:
새 모델을 10% 사용자에게 먼저 배포
이슈 없으면 100%로 확대
실전 4 — AI 모니터링 대시보드
[AI 모니터링 활성화 — Firebase 콘솔]
1. Firebase 콘솔 → AI Services → AI Logic
2. "AI monitoring" 탭 클릭
3. "Enable AI monitoring" 활성화
4. 최소 SDK 버전 요구사항 확인:
- iOS: v11.13.0+
- Android: v16.0.0+ (BoM: v33.14.0+)
- Web: v11.8.0+
- Flutter: v2.0.0+
활성화 후 5분 이내 데이터 수집 시작
별도 코드 변경 불필요
[AI 모니터링 대시보드에서 볼 수 있는 것]
요청 지표:
→ 시간별 요청 수 (정상/오류)
→ 응답 코드별 분포
→ API 메서드별 지연 시간
→ 전체 지연 시간 P50/P95/P99
토큰 지표:
→ 입력 토큰 수 (시간별 합계)
→ 출력 토큰 수 (시간별 합계)
→ 사용자별 평균 토큰 소비
→ 이상 토큰 소비 감지
개별 추적 (Trace):
→ 각 요청의 전체 생명주기
→ 입력 프롬프트 (샘플링)
→ 출력 응답
→ 토큰 수, 지연, 모델 버전
샘플링 설정:
→ 대용량 트래픽: 100% 수집 시 비용 발생
→ Google Cloud Observability Suite 사용량 기준
→ 권장: 10~30% 샘플링
// Android — AI 모니터링 데이터 수집 동의 확인
// 기본값: opt-in (enabled)
// 사용자 동의 기반 앱이라면 명시적 제어 가능
// 데이터 수집 비활성화 (GDPR 등 규제 환경)
Firebase.ai.dataCollectionEnabled = false
실전 5 — 비용 최적화 전략
[프리 티어 한도 (Gemini Developer API, 2026년 5월 기준)]
gemini-3.1-flash-lite:
→ 1,500 requests/일
→ 32,000 TPM (Tokens Per Minute)
→ 1,000,000 TPD (Tokens Per Day)
gemini-2.5-flash:
→ 500 requests/일
→ 250,000 TPM
→ 1,000,000 TPD
→ 무료 한도 초과 시: 429 오류 또는 Blaze 플랜 필요
// 비용 최적화 패턴 1: 토큰 예산 게이트
class TokenBudgetGuard(
private val dailyBudget: Int = 500_000 // 일일 토큰 예산
) {
private var usedToday = 0
private var lastResetDate = ""
suspend fun checkAndRecord(
model: GenerativeModel,
prompt: String
): Boolean {
// 날짜 변경 시 카운터 리셋
val today = LocalDate.now().toString()
if (today != lastResetDate) {
usedToday = 0
lastResetDate = today
}
// 예상 토큰 수 확인 (실제 요청 전)
val tokenCount = model.countTokens(prompt).totalTokens
return if (usedToday + tokenCount > dailyBudget) {
println("⚠️ 일일 토큰 예산 초과: $usedToday/$dailyBudget")
false
} else {
usedToday += tokenCount
true
}
}
}
// 비용 최적화 패턴 2: 모델 라우팅
// 태스크 복잡도에 따라 적합한 모델 선택
class ModelRouter {
// 저비용 모델 (단순 태스크)
private val flashLiteModel = Firebase.ai(backend = GenerativeBackend.googleAI())
.generativeModel("gemini-3.1-flash-lite")
// 고성능 모델 (복잡 태스크)
private val flashModel = Firebase.ai(backend = GenerativeBackend.googleAI())
.generativeModel("gemini-2.5-flash")
fun selectModel(task: TaskType): GenerativeModel {
return when (task) {
TaskType.SIMPLE_QA,
TaskType.CLASSIFICATION,
TaskType.SHORT_SUMMARY -> flashLiteModel // 저비용
TaskType.COMPLEX_REASONING,
TaskType.CODE_GENERATION,
TaskType.LONG_DOCUMENT -> flashModel // 고성능
}
}
}
enum class TaskType {
SIMPLE_QA, CLASSIFICATION, SHORT_SUMMARY,
COMPLEX_REASONING, CODE_GENERATION, LONG_DOCUMENT
}
// 비용 최적화 패턴 3: 응답 캐싱
// 동일 프롬프트 반복 요청 방지
import java.util.concurrent.ConcurrentHashMap
class GeminiResponseCache(
private val model: GenerativeModel,
private val maxCacheSize: Int = 100
) {
private val cache = ConcurrentHashMap<String, String>()
suspend fun generateWithCache(prompt: String): String {
// 캐시 히트
cache[prompt]?.let { return it }
// 캐시 미스 → 실제 API 호출
val response = model.generateContent(prompt).text ?: ""
// 캐시 크기 제한
if (cache.size >= maxCacheSize) {
cache.remove(cache.keys.first())
}
cache[prompt] = response
return response
}
}
[Blaze 플랜 비용 관리 체크리스트]
☐ Budget Alert 설정 (Firebase 콘솔 → 사용량 및 청구)
→ 월 예산 설정
→ 50%, 90%, 100% 도달 시 이메일 알림
→ "하드 한도" 없음 — 알림만 전송됨
☐ 쿼터 설정 (Google Cloud 콘솔 → APIs & Services)
→ Gemini API 쿼터 설정
→ 사용자별 RPM 제한 (기본 100 RPM)
→ 과도한 사용 자동 차단
☐ AI 모니터링 활성화 (Firebase 콘솔)
→ 이상 토큰 소비 감지
→ 비정상 요청 패턴 조기 발견
☐ 프로덕션 모델명 고정 (auto-updated alias 금지)
→ gemini-3.1-flash-lite ❌ (alias, 자동 변경됨)
→ gemini-3.1-flash-lite-001 ✅ (고정 버전, 예측 가능)
실전 6 — 프로덕션 배포 전 최종 체크리스트
공식 문서 프로덕션 체크리스트 기반입니다.
[Firebase AI Logic 프로덕션 체크리스트]
보안:
☐ Firebase App Check 활성화 + 강제 적용
☐ API 키 앱 코드에 없음 확인 (.env 파일 git 제외)
☐ Firebase Security Rules 검토
☐ Cloud Storage 파일 접근 권한 제한 (멀티모달 사용 시)
모델 관리:
☐ 모델명 고정 버전 사용 (alias 금지)
☐ Remote Config로 모델명 관리 (앱 업데이트 없이 교체 가능)
☐ gemini-2.0-flash 사용 중이면 6/1 전 마이그레이션 완료
☐ Imagen 모델 사용 중이면 6/24 전 Nano Banana로 전환
프로젝트 분리:
☐ 개발용 Firebase 프로젝트 (dev)
☐ 스테이징용 Firebase 프로젝트 (staging)
☐ 프로덕션용 Firebase 프로젝트 (prod)
☐ 각 환경별 google-services.json / GoogleService-Info.plist 분리
비용 관리 (Blaze 플랜):
☐ Budget Alert 설정
☐ 사용자별 RPM 쿼터 설정
☐ AI 모니터링 활성화
☐ 샘플링 비율 설정
운영:
☐ 에러 처리 — 429(할당량 초과), 500(서버 오류) 사용자 안내
☐ 오프라인 대응 — 요청 실패 시 재시도 로직
☐ 긴 응답 타임아웃 설정
☐ 안전 설정(Safety Settings) 검토
// 에러 처리 — 프로덕션 필수
import com.google.firebase.ai.type.FirebaseAIException
import com.google.firebase.ai.type.FinishReason
fun handleGeminiError(error: Exception): String {
return when (error) {
is FirebaseAIException -> when {
error.message?.contains("429") == true ->
"요청이 너무 많습니다. 잠시 후 다시 시도해주세요."
error.message?.contains("500") == true ->
"서비스 오류가 발생했습니다. 잠시 후 다시 시도해주세요."
error.message?.contains("SAFETY") == true ->
"안전 정책에 의해 응답이 차단되었습니다."
else -> "오류가 발생했습니다: ${error.message}"
}
else -> "알 수 없는 오류가 발생했습니다."
}
}
// 응답 완료 이유 확인
fun checkFinishReason(response: GenerateContentResponse) {
val finishReason = response.candidates.firstOrNull()?.finishReason
when (finishReason) {
FinishReason.STOP -> println("정상 완료")
FinishReason.MAX_TOKENS -> println("토큰 한도 초과 — maxOutputTokens 증가 필요")
FinishReason.SAFETY -> println("안전 필터 차단 — Safety Settings 검토")
FinishReason.RECITATION -> println("저작권 필터 차단")
else -> println("기타 종료: $finishReason")
}
}
마무리 — 4편 시리즈 완결
[전체 시리즈 정리]
1편: Firebase 프로젝트 설정 + 첫 API 호출 (Android/웹 공통)
2편: Android 실전 — 스트리밍, 채팅, 멀티모달, 구조화 출력
3편: 웹 실전 — TypeScript/React 커스텀 훅, Next.js App Router
4편: 프로덕션 — App Check, Vertex AI 전환, Remote Config, 모니터링
[권장 도입 순서]
1. Gemini Developer API 무료 티어로 시작
2. 개발 중부터 App Check 설정
3. Remote Config로 모델명 관리
4. 트래픽 증가 시 Blaze 플랜 전환
5. AI 모니터링 활성화 + Budget Alert
6. 필요 시 Vertex AI로 백엔드 전환 (코드 한 줄)
✅ 프로덕션 배포 최소 요건
→ App Check 강제 적용
→ 모델명 고정 버전 사용
→ Remote Config로 모델 관리
→ 개발/프로덕션 Firebase 프로젝트 분리
→ 에러 처리 구현
관련 글
반응형
'AI 개발' 카테고리의 다른 글
| OpenRouter 완전 가이드 2편 — 폴백 라우팅, 로드밸런싱, 프로바이더 제어, 비용 최적화 실전 (0) | 2026.05.19 |
|---|---|
| OpenRouter 완전 가이드 1편 — 300개 AI 모델을 API 키 하나로 쓰는 법 (0) | 2026.05.19 |
| Firebase AI Logic + Gemini 실전 가이드 3편 — 웹(JavaScript/TypeScript) + Next.js 실전 (0) | 2026.05.19 |
| Firebase AI Logic + Gemini 실전 가이드 2편 — 스트리밍, 멀티턴 채팅, 멀티모달, 구조화 출력 (0) | 2026.05.19 |
| Firebase AI Logic + Gemini 실전 가이드 1편 — 개요, Firebase 세팅, 첫 API 호출까지 (0) | 2026.05.19 |