Use Cases

Claude Code × GCP Cloud Functions 완전 가이드 | 서버리스 함수 초고속 개발

Claude Code로 GCP Cloud Functions를 효율화. HTTP/Pub/Sub/Firestore 트리거 구현부터 로컬 테스트·배포 자동화까지, Masa의 실무 경험을 토대로 실제 코드로 해설.

Cloud Run은 너무 과했다 — 그래서 Cloud Functions를 선택했다

저는 claudecode-lab.com을 운영하는 Masa입니다.

GCP 서버리스를 사용하기 시작했을 때 Cloud Run을 고려했습니다. 유연하고 컨테이너 기반이지만 “단순히 웹훅 하나 받는 것” 이나 “밤에 한 번 배치 실행” 같은 작업에는 분명히 오버스펙이었습니다. 그래서 Cloud Functions Gen2 로 전환했습니다.

Claude Code 덕분에 Cloud Functions 관련 코드의 90% 이상이 자동 생성됩니다.


Step 1: HTTP 트리거 함수 구현

import { http, HttpFunction } from "@google-cloud/functions-framework";
import { Request, Response } from "express";
import { Firestore } from "@google-cloud/firestore";

const db = new Firestore();
const VALID_TOKEN = process.env.API_SECRET_TOKEN;

interface ActionRequest {
  userId: string;
  action: string;
}

const handleAction: HttpFunction = async (req: Request, res: Response) => {
  // CORS 헤더 설정
  res.set("Access-Control-Allow-Origin", "*");
  if (req.method === "OPTIONS") {
    res.set("Access-Control-Allow-Methods", "POST");
    res.set("Access-Control-Allow-Headers", "Authorization, Content-Type");
    res.status(204).send("");
    return;
  }

  // POST만 허용
  if (req.method !== "POST") {
    res.status(405).json({ success: false, message: "Method Not Allowed" });
    return;
  }

  // Bearer 토큰 검증
  const authHeader = req.headers.authorization ?? "";
  if (!authHeader.startsWith("Bearer ") || authHeader.slice(7) !== VALID_TOKEN) {
    res.status(401).json({ success: false, message: "인증되지 않음" });
    return;
  }

  const body = req.body as Partial<ActionRequest>;
  if (!body.userId || !body.action) {
    res.status(400).json({ success: false, message: "userId와 action이 필요합니다" });
    return;
  }

  try {
    const logRef = await db.collection("action_logs").add({
      userId: body.userId,
      action: body.action,
      timestamp: new Date(),
      ip: req.ip,
    });
    res.status(200).json({ success: true, message: "액션 기록됨", logId: logRef.id });
  } catch (err) {
    console.error("Firestore 쓰기 오류:", err);
    res.status(500).json({ success: false, message: "내부 서버 오류" });
  }
};

http("handleAction", handleAction);

Step 2: Pub/Sub 트리거 함수

import { cloudEvent, CloudEvent } from "@google-cloud/functions-framework";
import { MessagePublishedData } from "@google/events/cloud/pubsub/v1/MessagePublishedData";
import { Storage } from "@google-cloud/storage";

const storage = new Storage();

interface ImageUploadedMessage {
  bucketName: string;
  filePath: string;
}

cloudEvent<MessagePublishedData>("handleImageUploaded", async (event: CloudEvent<MessagePublishedData>) => {
  const base64Data = event.data?.message?.data;
  if (!base64Data) {
    console.warn("메시지 데이터 없음, 건너뜀");
    return;
  }

  const rawJson = Buffer.from(base64Data, "base64").toString("utf-8");
  let payload: ImageUploadedMessage;
  try {
    payload = JSON.parse(rawJson) as ImageUploadedMessage;
  } catch {
    console.error("Pub/Sub 메시지의 잘못된 JSON:", rawJson);
    return;
  }

  const { bucketName, filePath } = payload;
  try {
    const [metadata] = await storage.bucket(bucketName).file(filePath).getMetadata();
    console.log("파일 메타데이터:", { 이름: metadata.name, 크기: metadata.size });
  } catch (err) {
    // 예외를 던지면 Pub/Sub이 메시지를 재시도
    console.error(`${filePath} 처리 실패:`, err);
    throw err;
  }
});

Step 3: 로컬 테스트 및 배포

# 로컬 시작
npm run build
npx @google-cloud/functions-framework --target=handleAction --port=8080

# 테스트 요청 전송
curl -X POST http://localhost:8080 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer test-token" \
  -d '{"userId": "user-123", "action": "login"}'

# GCP에 배포
gcloud functions deploy handleAction \
  --gen2 \
  --runtime=nodejs22 \
  --region=asia-northeast3 \
  --source=. \
  --entry-point=handleAction \
  --trigger-http \
  --allow-unauthenticated \
  --memory=512Mi \
  --timeout=60s \
  --set-secrets="API_SECRET_TOKEN=api-secret-token:latest"

4가지 함정

함정문제해결책
콜드 스타트비활성 인스턴스에서 2~5초 지연중요 함수에 --min-instances=1 설정
타임아웃 제한최대 9분 (540초)Pub/Sub으로 대형 작업 분할 처리
Secret Manager 권한런타임에서 Permission deniedsecretmanager.secretAccessor 역할 부여
메모리 부족무거운 처리에서 OOM--memory=2Gi --cpu=2 설정

요약

트리거사용 사례핵심 고려사항
HTTP웹훅, API 엔드포인트인증, CORS
Pub/Sub비동기 이벤트 처리base64 디코딩, 재시도 설계
Firestore데이터 변경에 반응무한 루프 주의
Cloud Scheduler정기 배치 작업OIDC 검증, 타임존 관리

관련 기사

#claude-code #gcp #cloud-functions #typescript #serverless #pubsub
무료 제공

무료 PDF: 5분 완성 Claude Code 치트시트

이메일 주소만 등록하시면 A4 한 장짜리 치트시트 PDF를 즉시 보내드립니다.

개인정보는 엄격하게 관리하며 스팸은 보내지 않습니다.

Claude Code 워크플로우를 한 단계 업그레이드하세요

지금 바로 Claude Code에 복사해 쓸 수 있는 검증된 프롬프트 템플릿 50선.

Masa

이 글을 작성한 사람

Masa

Claude Code를 적극 활용하는 엔지니어. 10개 언어, 2,000페이지 이상의 테크 미디어 claudecode-lab.com을 운영 중.