Firebase cloud function

Create folder

// e.g. 
mkdir recommand_youtube

Firebase ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ on Firebase console

๋กœ๊ทธ์ธ - Google ๊ณ„์ •

Firebase CLI ์„ค์น˜ ๋ฐ ์ดˆ๊ธฐํ™”

npm install -g firebase-tools

Firebase ํ”„๋กœ์ ํŠธ ์ดˆ๊ธฐํ™” ์„ค์ •

// login
firebase login

// login ๋ณ€๊ฒฝ
firebase logout
firebase login

// check project list
firebase projects:list

// result
โœ” Preparing the list of your Firebase projects
Project Display Name: recommand-youtube, Project ID: recommand-5e127, Project Number: 155670260747, Resource Location ID: [Not specified]

Project Display Name: refacter, Project ID: refacter, Project Number: 192701994501, Resource Location ID: [Not specified]

// initialize functions 
firebase init functions

// Python์€ ๋กœ์ปฌ์—์„œ ํ…Œ์ŠคํŠธํ•˜๊ธฐ๊ฐ€ ์–ด๋ ค์›€ 
// ๊ทธ๋ž˜์„œ nodejs๋Š” ํ™œ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ง€์†์ ์œผ๋กœ ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ํŽธํ•  ๊ฒƒ์œผ๋กœ ์ƒ๊ฐ ๋จ

functions์„ ์ดˆ๊ธฐํ™” ํ•˜๋ฉด ํ”„๋กœ์ ํŠธ๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ํ•„์š”ํ•˜๋‹ค๋ฉด ํ”„๋กœ์ ํŠธ ์„ ํƒ

// select the project
firebase use <ํ”„๋กœ์ ํŠธID>

(Optional)Google Cloud Platform(GCP) API ํ™œ์„ฑํ™”

  1. Google Cloud Console (https://console.cloud.google.com) ์ ‘์†
  2. ํ•ด๋‹น ํ”„๋กœ์ ํŠธ ์„ ํƒ
  3. ์ขŒ์ธก ๋ฉ”๋‰ด์—์„œ โ€œAPI ๋ฐ ์„œ๋น„์Šคโ€ > โ€œYoutube Data API v3โ€ ํ™œ์„ฑํ™” > โ€œ์‚ฌ์šฉ์ž ์ธ์ฆ ์ •๋ณดโ€ (Credentials) ์ด๋™
  4. โ€œAPI ํ‚คโ€ ์„น์…˜์— ์ƒ์„ฑ๋œ API ํ‚ค๊ฐ€ ํ‘œ์‹œ๋จ
// GCP secret manager์— key ์ €์žฅ
firebase functions:secrets:set YOUR_SECRET_NAME

// result
firebase functions:secrets:set youtube_key
โš   By convention, secret key must be in UPPER_SNAKE_CASE.
? Would you like to use YOUTUBE_KEY as key instead? Yes
? Enter a value for YOUTUBE_KEY [hidden]
โœ”  Created a new secret version projects/155670260747/secrets/YOUTUBE_KEY/versions/1
  1. Firebase functions์— ์ ‘๊ทผํ•˜๋Š” PROJECT_ID@appspot.gserviceaccount.com ์— ๊ถŒํ•œ ์„ค์ •

(Optional) OAuth 2.0 ์„ค์ •

  1. OAuth ๋™์˜ ํ™”๋ฉด
  2. ํด๋ผ์ด์–ธํŠธ ๋งŒ๋“ค๊ธฐ
    1. redirect-uri
        • OAuth Playground์šฉ: https://developers.google.com/oauthplayground
      1. ์ž์ฒด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์šฉ: ๊ท€ํ•˜์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝœ๋ฐฑ URL
  • Id, ๋ณดํ˜ธ ๋น„๋ฐ€๋ฒˆํ˜ธ ์ €์žฅ
  1. ๋Œ€์ƒ > ํ…Œ์ŠคํŠธ ์‚ฌ์šฉ์ž ๋“ฑ๋ก
  2. RefreshToken ๊ฐ€์ ธ์˜ค๊ธฐ OAuth 2.0 Playground ์—์„œ RefreshToken ๊ฐ€์ ธ์˜ค๊ธฐ

Google OAuth 2.0 ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ ํš๋“ ์™„๋ฒฝ ๊ฐ€์ด๋“œ 2024 - ์ตœ์‹  UI ์—…๋ฐ์ดํŠธ ๋ฐ˜์˜

IDE ํ™˜๊ฒฝ ๊ตฌ์„ฑ

Nodejs ํ™˜๊ฒฝ์ผ ๋•Œ๋งŒ ์œ ํšจ

Run and Debug ์„ค์ •

9229 ํฌํŠธ ์„ค์ •

๋ณ„๋„๋กœ ํฌํŠธ๋ฅผ ์ง€์ •ํ•˜๋ ค๋ฉด Node.js inspect ์˜ต์…˜์ธย --inspect=ํฌํŠธ๋ฒˆํ˜ธย ์‚ฌ์šฉ๋ฒ•์„ ์ฐธ๊ณ ํ•ด์•ผ ํ•˜์ง€๋งŒ ํ˜„์žฌ Firebase CLI๋Š” ๊ณ ์ •์œผ๋กœ 9229 ํฌํŠธ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. by GPT

"configurations": [
	// ...
	{
		"type": "node",
		"request": "attach",
		"name": "Attach to Functions Emulator",
		"port": 9229,
		"restart": true,
	}
]

Break point ์„ค์ •

์„ค์ • ํ›„ ํ™•์ธ

์—๋ฎฌ๋ ˆ์ดํ„ฐ ์‹คํ–‰ & VS Code ์‹คํ–‰

// Emulator
firebase emulators:start --inspect-functions

// VS Code
Run "Attach to Functions Emulator"

Firebase hosting

ํ˜ธ์ŠคํŒ… ์ค‘๋‹จ

firebase hosting:disable

Google cloud

// login
gcloud auth login

// ํ˜„์žฌ ๋กœ๊ทธ์ธ ๊ณ„์ • ๋ฆฌ์ŠคํŠธ
gcloud auth list

// ํ”„๋กœ์ ํŠธ ์„ค์ •
gcloud config set project PROJECT_ID

Vertex AI RAG

  1. ์ฝ”ํผ์Šค ์ƒ์„ฑ
    1. ์ฝ”ํผ์Šค๋ž€? ๋ง๋ญ‰์น˜, ์ž์—ฐ์–ธ์–ด ์—ฐ๊ตฌ๋ฅผ ์œ„ํ•ด ํŠน์ •ํ•œ ๋ชฉ์ ์„ ๊ฐ€์ง€๊ณ  ์–ธ์–ด์˜ ํ‘œ๋ณธ์„ ์ถ”์ถœํ•œ ์ง‘ํ•ฉ
  2. ๋ฐ์ดํ„ฐ ์„ ํƒ ํ›„ ์ƒ์„ฑ
    1. ๋กœ์ปฌ ํŒŒ์ผ ์—…๋กœ๋“œ: ๋กœ์ปฌ ํŒŒ์ผ์„ ํ†ตํ•ด RAG ์„ค์ •, ์ดํ›„ ๋กœ์ปฌ์—์„œ ํŒŒ์ผ ์‚ญ์ œ๋˜๋„ ๋ฌด๋ฐฉ
    2. Google Drive ํŒŒ์ผ ์„ ํƒ: ๊ณ„์† ์—…๋ฐ์ดํŠธ ๊ฐ€๋Šฅํ•  ๊ฒƒ
  3. ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ, ์„ค์ •
    1. service-<ํ”„๋กœ์ ํŠธ ๋„˜๋ฒ„>@gcp-sa-vertex-rag.iam.gserviceaccount.com
  4. Vertex AI Studio์—์„œ ํ…Œ์ŠคํŠธ - 3๋ฒˆ ์ฒซ๋ฒˆ์งธ ๊ทธ๋ฆผ์— ์žˆ๋Š” ๋ฒ„ํŠผ

Cloud function with RAG

  1. Google Cloud ํ”„๋กœ์ ํŠธ ์„ค์ •
# ํ”„๋กœ์ ํŠธ ์„ค์ •
PROJECT_ID=carenote-ai-474805
LOCATION=europe-west4
gcloud config set project ${PROJECT_ID}
  1. ํ•„์š”ํ•œ API ํ™œ์„ฑํ™” Vertex AI, Cloud Functions, Cloud Build ๋“ฑ ํ•„์š”ํ•œ API ํ™œ์„ฑํ™”
gcloud services enable \
    aiplatform.googleapis.com \
    cloudfunctions.googleapis.com \
    cloudbuild.googleapis.com \
    artifactregistry.googleapis.com \
    run.googleapis.com
  1. Vertex AI RAG Corpus ์„ค์ •
# RAG Corpus๋Š” ์ด๋ฏธ ์ƒ์„ฑ๋˜์–ด ์žˆ์Œ
  RAG_CORPUS_ID=2305843009213693952
  RAG_CORPUS_RESOURCE_NAME="projects/${PROJECT_ID}/locations/${LOCATION}/ragCorpora/${RAG_CORPUS_ID}"
  1. ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์„ค์ • (.env ํŒŒ์ผ)
PROJECT_ID=carenote-ai-474805
PROJECT_NUMBER=16801581551
LOCATION=europe-west4
RAG_CORPUS_ID=2305843009213693952
MODEL_ID=gemini-2.5-flash-lite
  1. Node.js ํ”„๋กœ์ ํŠธ ๊ตฌ์„ฑ
package.json:
  {
    "name": "carenote-ai",
    "version": "1.0.0",
    "main": "index.js",
    "type": "module",  // ES6 ๋ชจ๋“ˆ ์‚ฌ์šฉ
    "dependencies": {
      "@google-cloud/functions-framework": "^4.0.0",
      "@google-cloud/vertexai": "^1.10.0",
      "dotenv": "^17.2.3"
    }
  }

๊ธฐ๋ณธ ์ฝ”๋“œ ๊ตฌ์กฐ: // index.js - Cloud Functions ์—”ํŠธ๋ฆฌํฌ์ธํŠธ import functions from โ€˜@google-cloud/functions-frameworkโ€™;

// generateDiary ํ•จ์ˆ˜ ์ •์˜ functions.http(โ€˜generateDiaryโ€™, async (req, res) โ‡’ { // CORS, ์ธ์ฆ, ๊ฒ€์ฆ ์ฒ˜๋ฆฌ // RAG ํ˜ธ์ถœ ๋ฐ ์‘๋‹ต });

// ragQuery ํ•จ์ˆ˜ ์ •์˜
functions.http(โ€˜ragQueryโ€™, async (req, res) โ‡’ { // ์ผ๋ฐ˜ RAG ์ฟผ๋ฆฌ ์ฒ˜๋ฆฌ });

Vertex AI RAG ํ˜ธ์ถœ ํ•ต์‹ฌ ์ฝ”๋“œ: import { VertexAI } from โ€˜@google-cloud/vertexaiโ€™;

const vertexAI = new VertexAI({ project: PROJECT_ID, location: LOCATION, });

const generativeModel = vertexAI.preview.getGenerativeModel({ model: MODEL_ID, generationConfig: { responseMimeType: โ€˜application/jsonโ€™ } });

// RAG ์š”์ฒญ const ragRequest = { contents: [{ role: โ€˜userโ€™, parts: [{ text: query }] }], tools: [{ retrieval: { vertexRagStore: { ragResources: [{ ragCorpus: RAG_CORPUS_RESOURCE_NAME }], similarityTopK: 5, vectorDistanceThreshold: 0.8 } } }] };

  1. Cloud Functions ๋ฐฐํฌ

generateDiary ํ•จ์ˆ˜ ๋ฐฐํฌ

gcloud functions deploy generateDiary
โ€”gen2
โ€”runtime nodejs20
โ€”region europe-west4
โ€”source .
โ€”entry-point generateDiary
โ€”trigger-http
โ€”timeout 300s
โ€”memory 512MB
โ€”min-instances 0
โ€”max-instances 10
โ€”set-env-vars PROJECT_ID={LOCATION},RAG_CORPUS_ID=${RAG_CORPUS_ID},MODEL_ID=gemini-2.5-flash-lite
โ€”no-allow-unauthenticated

gcloud functions deploy generate-diary \
  --gen2 \
  --runtime nodejs20 \
  --region europe-west4 \
  --source . \
  --entry-point generateDiary \
  --trigger-http \
  --timeout 300s \
  --memory 512MB \
  --min-instances 0 \
  --max-instances 10 \
  --set-env-vars PROJECT_ID=carenote-ai-474805,LOCATION=europe-west4,RAG_CORPUS_ID=2305843009213693952,MODEL_ID=gemini-2.5-flash-lite \
  --no-allow-unauthenticated

ragQuery ํ•จ์ˆ˜๋„ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ๋ฐฐํฌ ๊ฐ€๋Šฅ

  1. ๋ฐฐํฌ ํ›„ ์„ค์ •

IAM ๊ถŒํ•œ ์„ค์ • (Cloud Functions Invoker)

gcloud functions add-invoker-policy-binding generateDiary
โ€”member=โ€œuser:email@domain.comโ€
โ€”region=europe-west4

๋ฐฐํฌ๋œ ํ•จ์ˆ˜ URL

FUNCTION_URL=โ€œhttps://europe-west4-${PROJECT_ID}.cloudfunctions.net/generateDiaryโ€

  1. ํ•„์ˆ˜ ํŒŒ์ผ ๊ตฌ์„ฑ

project/ โ”œโ”€โ”€ index.js # Cloud Functions ์—”ํŠธ๋ฆฌํฌ์ธํŠธ โ”œโ”€โ”€ package.json # ์˜์กด์„ฑ ์ •์˜ โ”œโ”€โ”€ .env # ํ™˜๊ฒฝ๋ณ€์ˆ˜ (๋กœ์ปฌ ํ…Œ์ŠคํŠธ์šฉ) โ”œโ”€โ”€ .gcloudignore # ๋ฐฐํฌ ์ œ์™ธ ํŒŒ์ผ โ””โ”€โ”€ src/ โ”œโ”€โ”€ config/ # Vertex AI ์„ค์ • โ”œโ”€โ”€ services/ # RAG ํ˜ธ์ถœ ๋กœ์ง โ””โ”€โ”€ controllers/ # HTTP ์š”์ฒญ ์ฒ˜๋ฆฌ

  1. ์ฃผ์š” ํ™˜๊ฒฝ ์„ค์ • ์š”์•ฝ
  • Region: europe-west4 (RAG Corpus์™€ ๋™์ผ ์ง€์—ญ)
  • Runtime: nodejs20
  • Memory: 512MB
  • Timeout: 300s (RAG ์‘๋‹ต ์‹œ๊ฐ„ ๊ณ ๋ ค)
  • Authentication: IAM ๊ธฐ๋ฐ˜ (no-allow-unauthenticated)

์ด ์„ค์ •์œผ๋กœ Vertex AI RAG๋ฅผ Cloud Functions๋ฅผ ํ†ตํ•ด ์„œ๋น„์Šคํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฃผ๊ธฐ์ ์ธ ํ† ํฐ ์—…๋ฐ์ดํŠธ ํ•„์š” gcloud auth print-identity-token