Firebase Functions v2의 callable·Firestore 트리거와 Secret 연결은 어떻게 구성하나요?
Firebase Functions의 callable과 Firestore 트리거는 호출 방식이 다릅니다. Secret은 선언 후 함수에 연결해 실행 중 읽으세요. 권한·중복 이벤트·배포를 나눠 확인하며 Secret 존재를 외부 API 성공으로 해석하지 않습니다.
적용 조건·범위: 로컬에서는 Firebase CLI로 Functions와 Firestore Emulator를 함께 시작하고 개발 환경의 Secret 대체값을 연결합니다. 주문 한 개를 넣어 orderEvents에 대응 문서가 생기는지 확인합니다. callable은 클라이언트 SDK httpsCallable로 호출하며 함수 리전을 맞춥니다. 비로그인·일반 사용자 호출은 permission-denied가 기대 결과입니다. 운영 배포에는 프로젝트 결제/권한/런타임 조건 확인이 필요하며 배포 후 로그와 실패율을 확인하세요.
본문 근거: firebase.google.com/docs/functions/config-env · firebase.google.com/docs/functions/firestore-events · firebase.google.com/docs/functions/callable
학습 목표와 사전 지식
목표: v2 callable·Firestore 트리거와 Secret 바인딩의 차이를 설명하고 배포 점검 순서를 정합니다.
사전 지식: Node.js, Promise, Firebase Auth·Firestore, 서버와 클라이언트 경계
Functions는 신뢰해야 하는 코드를 실행하는 서버 환경입니다. 클라이언트에서 숨긴 버튼은 권한 검사가 아니며, 서버에서도 요청자의 인증·역할·입력을 확인해야 합니다. 서버리스여도 비용·권한·재시도 정책은 애플리케이션이 설계합니다.
| 유형 | 핵심 계약 |
|---|---|
| onRequest | 일반 HTTP; 인증 토큰 검증과 CORS를 직접 설계 |
| onCall | Firebase callable 프로토콜; request.auth 검사 필요 |
| onDocumentCreated | 이벤트 중복 전달 가능; 멱등 처리 필요 |
| Auth 가입/삭제 후 트리거 | 일반 onCreate/onDelete는 1세대 API와 구분 |
부분 서버 예제로 흐름 읽기
아래 functions/index.js는 CommonJS 모듈 전체입니다. firebase init functions에서 JavaScript를 선택한 생성 프로젝트에 적용하는 예이며 완성 프론트엔드나 외부 API 연동은 포함하지 않습니다. npm install firebase-functions firebase-admin으로 서버 의존성을 설치하고 지원 Node 런타임을 package.json engines에 지정합니다. 공식 문서에서 Node 20·22 지원과 Node 18 폐기 상태를 확인하세요.
functions/index.js · 전체 서버 모듈; Firebase CLI 초기화 프로젝트에 적용
const { onCall, HttpsError } = require('firebase-functions/v2/https');
const { onDocumentCreated } = require('firebase-functions/v2/firestore');
const { defineSecret } = require('firebase-functions/params');
const { initializeApp } = require('firebase-admin/app');
const { getFirestore } = require('firebase-admin/firestore');
initializeApp();
const serviceKey = defineSecret('SERVICE_KEY');
exports.checkSecret = onCall(
{ region: 'us-central1', secrets: [serviceKey] },
(request) => {
if (request.auth?.token.admin !== true) {
throw new HttpsError('permission-denied', '관리자 권한이 필요합니다.');
}
if (!serviceKey.value()) {
throw new HttpsError('failed-precondition', '서버 설정을 확인하세요.');
}
return { configured: true };
}
);
exports.markOrderCreated = onDocumentCreated(
{ document: 'orders/{orderId}', region: 'us-central1' },
async (event) => {
if (!event.data) return;
await getFirestore().doc(`orderEvents/${event.params.orderId}`).set({
orderId: event.params.orderId,
sourceEventId: event.id,
sourceCreatedAt: event.data.createTime
});
}
);

Secret과 응답의 의미
defineSecret은 선언이고 secrets 옵션은 해당 함수에 값을 연결하는 바인딩입니다. .value()는 핸들러 실행 중 읽습니다. checkSecret의 configured: true는 Secret 값 존재만 확인하며 외부 서비스 호출 성공을 뜻하지 않습니다. 값이나 길이·일부 문자열도 응답·로그에 넣지 않습니다. 설정은 firebase functions:secrets:set SERVICE_KEY를 사용하고 Secret 변경 후 사용하는 함수를 다시 배포합니다.
이벤트를 한 번만 받는다고 가정하지 않기
markOrderCreated는 같은 주문 ID의 결과 문서를 동일 값으로 덮어써 재전달 시 문서가 늘어나지 않게 합니다. 단순 increment나 결제 요청을 그대로 넣으면 중복 부작용이 생길 수 있습니다. sourceEventId를 저장하는 것만으로 외부 결제가 멱등해지는 것은 아닙니다. 외부 서비스 idempotency key 또는 트랜잭션 기반 처리 기록이 필요합니다. 원본 orders에 다시 쓰지 않아 같은 트리거의 반복 실행도 피합니다.

실행과 배포를 구분해 확인하기
로컬에서는 Firebase CLI로 Functions와 Firestore Emulator를 함께 시작하고 개발 환경의 Secret 대체값을 연결합니다. 주문 한 개를 넣어 orderEvents에 대응 문서가 생기는지 확인합니다. callable은 클라이언트 SDK httpsCallable로 호출하며 함수 리전을 맞춥니다. 비로그인·일반 사용자 호출은 permission-denied가 기대 결과입니다. 운영 배포에는 프로젝트 결제/권한/런타임 조건 확인이 필요하며 배포 후 로그와 실패율을 확인하세요.
호출 결과를 먼저 예측하고 확인하기
functions/index.js를 저장했다면 다음 읽기 전용 명령으로 JavaScript 문법을 확인할 수 있습니다. 이 명령은 firebase-functions 패키지 설치나 함수 배포·호출을 대신하지 않습니다.
node --check functions/index.js
| 요청·이벤트 | 예상 결과 | 실제 확인 위치 |
|---|---|---|
| 비로그인 checkSecret 호출 | permission-denied | callable 클라이언트의 오류 코드 |
| 일반 로그인 사용자 checkSecret 호출 | permission-denied | 사용자 토큰의 admin claim과 함수 응답 |
| admin claim이 true이고 Secret 값을 읽을 수 있음 | { configured: true } | 서버에 바인딩된 Secret 존재만 확인; 값은 반환하지 않음 |
| 같은 orders/{orderId} 생성 이벤트의 재전달 | orderEvents/{orderId} 한 경로에 다시 set | 목적 문서 ID와 내용, 외부 부작용이 없는지 |
Secret 값을 바꾸면 해당 함수를 다시 배포해야 새 버전을 읽습니다. 관리자 claim을 막 설정했다면 클라이언트 ID 토큰 갱신도 별도로 확인하세요. Firestore 이벤트는 적어도 한 번 전달되고 순서가 보장되지 않을 수 있으므로 결과 문서 한 개라는 사실만으로 결제 등 외부 작업의 중복 방지를 증명하지 못합니다. Admin SDK의 Firestore 쓰기는 클라이언트 Rules 대신 서버 IAM의 영향을 받습니다. 이 편집에서는 Node 문법 검사만 실제 통과했고 callable·트리거·Secret·배포는 실행하지 않았습니다.
직접 확인할 결과
서버 모듈 Node 문법 검사
중복 이벤트 동일 목적 문서 확인 절차
클라우드 배포·Secret·Auth 실제 호출 미실행
검증 범위와 기준일
공식 문서 확인일: 2026-09-13. 웹 SDK 예제는 모듈형 API 기준입니다. 이 글의 코드·구조 정적 검토와 실제 클라우드 인증·저장·배포 검증을 구분합니다. 본인 개발 Firebase 프로젝트의 실제 인증·권한·배포 요청은 이 편집 환경에서 실행하지 않았습니다.
이어서 학습하기
공식 문서
이 글이 도움이 되었나요?
읽음 기록
읽음 — / 전체 20개 · 필수 14개
새 글 받아보기
RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.