주요 포인트 한눈에 보기
※ 이 문서는 Firebase Functions 2nd gen(v2) 기준으로 설명합니다. Firebase Functions는 프론트엔드에서 처리하면 위험하거나 부적절한 로직을 서버에서 안전하게 실행하기 위한 서버리스(Serverless) 기능입니다. 이 문서는 개념과 역할 → 실행 구조 → 실제 도입 준비 → 실무 함정 순서로 흐름을 구성하여, 처음 접하는 경우에도 전체 구조를 자연스럽게 이해할 수 있도록 정리합니다.
[Firebase Functions 공식문서 참고하기]
개념과 역할: 왜 Functions인가
Firebase Functions란 무엇인가
Firebase Functions는 서버를 직접 구축하거나 관리하지 않아도, 특정 이벤트가 발생했을 때 자동으로 실행되는 서버 코드입니다. 외부 네트워크 호출, Secret 사용, 일부 트리거 기능은 결제 플랜(Blaze) 전환이 필요할 수 있습니다. 개발자는 인프라 설정 대신 비즈니스 로직 구현에 집중할 수 있으며, 서버 운영과 확장성은 Firebase가 담당합니다.
서버리스(Serverless)란 무엇인가
서버리스는 “서버가 없다”가 아니라, 서버 운영과 관리 책임을 개발자가 직접 지지 않는 구조를 의미합니다. 인프라 증설, 장애 대응, 스케일링은 플랫폼이 자동으로 처리하고, 개발자는 실행 로직만 작성합니다.
즉, 서버를 빌리고 붙잡고 있는 방식이 아니라 “필요할 때만 실행되는 코드”를 배포해 두고, 호출량에 맞춰 자동 확장되도록 맡기는 모델입니다. Firebase Functions(v2)은 이 구조를 Cloud Run 기반으로 제공하며, 트래픽이 늘어도 별도의 서버 증설 작업 없이 대응할 수 있습니다.
왜 프론트엔드에서 처리하면 안 되는가
API Key, Secret, 관리자 권한 판단, 외부 API 호출 같은 로직을 프론트엔드에서 처리하면 보안상 심각한 문제가 발생합니다. 프론트엔드 코드는 브라우저에서 내려받는 순간 노출되며, 사용자는 개발자 도구로 요청과 응답을 그대로 관찰하고 복제할 수 있습니다.
특히 “관리자 권한이 있는지 확인” 같은 검증을 클라이언트에서만 처리하면 공격자는 요청을 변조해 우회할 수 있습니다. 그래서 최종 권한 검증과 Secret 사용은 반드시 서버 영역(Functions)에서 확정해야 하고, 프론트엔드는 “요청을 보내는 역할”까지만 담당하는 구조가 안전합니다.
프론트엔드와 Functions의 역할 분리 기준
- 프론트엔드: 화면 렌더링, 사용자 입력 처리, 상태 관리, UX
- Functions: 권한 검증, Secret 사용, 외부 API 호출, 데이터 무결성 보장
실행 구조와 트리거 유형
Functions는 HTTP API처럼 프론트엔드에서 직접 호출할 수도 있고, 인증(Auth) 이벤트나 Firestore 데이터 변경 같은 이벤트에 의해 자동으로 실행될 수도 있습니다. 즉, 사용자의 요청에 반응하는 방식과 시스템 이벤트에 반응하는 방식 두 가지를 모두 지원합니다.
HTTP Functions를 배포하면 호출 가능한 URL이 함께 출력되며, 이 URL을 그대로 사용하면 됩니다. 해당 URL은 배포 시 터미널 로그 또는 Firebase 콘솔의 Functions 화면에서 확인할 수 있습니다.
HTTP 트리거는 클라이언트가 직접 호출하는 API 성격에 가깝고, Auth/Firestore 트리거는 시스템 이벤트에 의해 자동 실행되는 후처리 로직에 가깝습니다.
2nd gen(v2) Functions는 Cloud Run 기반으로 동작하기 때문에 run.app 형태의 URL이 제공될 수 있으며, 프로젝트 설정이나 배포 방식에 따라 URL 형태는 달라질 수 있습니다. Next.js를 사용하는 경우 API Route를 프록시로 두고 그 내부에서 Functions를 호출하는 구조도 자주 사용됩니다.
Firebase Functions의 실행 구조와 트리거
Firebase Functions는 이벤트 기반으로 실행됩니다. 대표적인 트리거로는 HTTP 요청, 인증(Auth) 이벤트, Firestore 데이터 변경이 있습니다.
import { onRequest } from 'firebase-functions/v2/https';
export const helloApi = onRequest((req, res) => {
res.json({ message: 'hello' });
});
import { onUserCreated } from 'firebase-functions/v2/auth';
export const onUserCreate = onUserCreated(event => {
console.log(event.data?.uid);
});
import { onDocumentCreated } from 'firebase-functions/v2/firestore';
export const onOrderCreated = onDocumentCreated(
{ document: 'orders/{orderId}' },
(event) => {
console.log('orderId:', event.params.orderId);
const doc = event.data?.data();
console.log('payload:', doc);
}
);
가장 많이 쓰이는 Functions 유형
| 유형 | 주요 용도 |
|---|---|
| HTTP Functions | API 서버 역할, 외부 요청 처리 |
| Auth Trigger | 회원가입·탈퇴 후 자동 처리 |
| Firestore Trigger | 주문 생성, 포인트 적립, 로그 기록 |
도입 준비: 초기화·로컬·배포·Secret
이 파트에서는 Firebase Functions를 실제 서비스에 적용하기 위해 반드시 거쳐야 하는 초기 설정과 개발·배포 워크플로우를 다룹니다. 코드를 작성하기 전에 어떤 준비가 필요한지, 그리고 어떤 순서로 진행되는지를 기준으로 살펴봅니다.
Firebase Functions 사용을 위한 기본 준비
Functions를 사용하려면 먼저 Firebase 프로젝트가 생성되어 있어야 하며, 로컬 개발 환경에서는 Firebase CLI를 통해 인증·초기화·배포 작업을 수행합니다. 이 단계는 실제 코드 작성 이전의 필수 준비 과정입니다.
npm install -g firebase-tools
firebase login
초기화와 폴더 구조
Functions를 초기화하면 프론트엔드 코드와 분리된 전용 서버 코드 영역이 생성됩니다. 이 분리를 통해 클라이언트 로직과 서버 로직의 책임 경계가 명확해집니다.
firebase init functions
project-root/
├─ app/
├─ functions/
│ ├─ src/
│ │ └─ index.ts
│ └─ package.json
└─ firebase.json
로컬 실행과 배포 흐름
로컬에서는 Emulator를 통해 Functions를 먼저 실행해 동작을 검증하고, 문제가 없을 경우 배포합니다. 일반적으로는 로컬에서 먼저 호출 테스트(Postman 또는 브라우저)를 진행한 뒤, 정상 동작을 확인하고 배포(deploy)하는 흐름을 사용합니다.
firebase emulators:start --only functions
firebase deploy --only functions
환경 변수와 Secret 관리
Secret은 코드에 직접 작성하지 않고 Firebase 환경에 안전하게 저장됩니다. v2 Functions에서는 Secret을 명시적으로 선언하고, 해당 Function에 주입해야만 사용할 수 있습니다. 또한 로컬 실행 환경에서는 배포 환경처럼 Secret이 항상 주입되는 형태가 아니므로, Emulator 설정이나 로컬 변수로 대체하거나 분기 처리하는 패턴을 사용합니다. 일반적으로는 로컬에서는 .env 또는 Emulator 설정으로 값을 주입하고, 배포 환경에서는 Firebase Secret으로 전환하는 방식으로 분리합니다.
firebase functions:secrets:set OPENAI_API_KEY
firebase deploy --only functions
import { onRequest } from 'firebase-functions/v2/https';
import { defineSecret } from 'firebase-functions/params';
const OPENAI_API_KEY = defineSecret('OPENAI_API_KEY');
export const callExternalApi = onRequest({ secrets: [OPENAI_API_KEY] }, (req, res) => {
const apiKey = OPENAI_API_KEY.value();
if (!apiKey) {
res.status(500).send('API Key not found');
return;
}
res.json({ status: 'success' });
});
실무에서 자주 터지는 함정
- CORS / Preflight: 브라우저에서 HTTP Functions를 직접 호출하면 CORS로 막히는 경우가 많으며, CORS 처리 또는 Next.js 프록시(API Route)로 우회하는 방식이 자주 사용됩니다.
- Cold Start: 서버리스 특성상 첫 호출은 느릴 수 있으며, 지연을 감안한 UX 설계나 캐시 전략이 필요합니다.
- Region 선택: 한국 사용자 서비스라면 주 사용자 지역을 기준으로 리전을 선택하는 것이 좋습니다.
- timeout / memory / concurrency: 외부 API 호출 기준으로 여유 있게 상향 설정하는 것이 안전합니다.
- 인증·권한 검증: 최종 권한 검증은 반드시 서버(Functions)에서 확정해야 합니다.
결론
Firebase Functions는 서버 인프라를 직접 운영하지 않으면서도 보안이 필요한 백엔드 로직을 안정적으로 처리할 수 있는 서버리스 도구입니다.
FAQ
Q. 언제 도입하는 것이 좋을까요?
Secret 보호, 권한 검증이 필요한 시점부터가 적절합니다.
Q. 프론트엔드만으로 처리하면 안 되는 이유는?
프론트엔드 코드는 사용자에게 그대로 노출되기 때문에, Secret이나 권한 검증 로직을 안전하게 보호할 수 없습니다. 이러한 검증은 반드시 서버 영역에서 최종 확정해야 데이터 무결성과 보안을 유지할 수 있습니다.
Q. 서버 개발 지식이 없어도 되나요?
직접 서버를 구축하거나 운영할 필요는 없지만, 인증·권한·에러 처리 같은 서버 관점의 사고는 필요합니다. Functions는 인프라를 추상화해 줄 뿐, 백엔드 책임까지 대신해 주지는 않습니다.