Firebase Cloud Functions란
Firebase에서 제공하는 서버리스(Serverless) 백엔드 서비스로, 개발자가 서버를 직접 관리하지 않고 클라우드에서 자동으로 확장 가능한 코드를 실행할 수 있도록 도와준다.
✏️ Firebase Cloud Functions 트리거
Firebase Cloud Functions에서는 두 가지 트리거 방식을 제공한다.
■ onCall()
- 매개변수: Handler function
- 형식:
async (data, context) => {}
- data: 클라이언트에서 전달한 데이터
- context: 인증 정보, 호출 메타데이터 등 포함
정의 방법 - 백엔드
1
2
3
4
5
6
7
8
9
| export const getBillingKey = regionalFunctions.https.onCall(
async (data: { authKey: string; customerKey: string }, context) => {
const { authKey, customerKey } = data;
...
return { success: true, billingKey };
}
);
|
사용 방법 - 프론트엔드
1
2
3
4
5
6
| const getBillingKey = httpsCallable<
{ authKey: string; customerKey: string },
{ billingKey: string }
>(functions, 'getBillingKey');
const result = await getBillingKey({ authKey, customerKey });
|
■ onRequest()
- 매개변수: Handler function
- 형식:
async (req, res) => {}
- req: 요청 객체 (Express와 동일)
- res: 응답 객체 (Express와 동일)
정의 방법 - 백엔드
1
2
3
4
5
| const functions = require("firebase-functions");
exports.helloWorld = functions.https.onRequest((req, res) => {
res.send("Hello, World!");
});
|
사용 방법 - 프론트엔드
1
2
3
4
5
6
| import { getFunctions, httpsCallable } from "firebase/functions";
const functions = getFunctions();
const addMessage = httpsCallable(functions, 'helloWorld');
await helloWorld();
|
■ onCall과 onRequest의 차이점
| 구분 |
onCall |
onRequest |
| 호출 방식 |
클라이언트의 firebase.functions()를 통해 호출 |
표준 HTTPS 클라이언트 (fetch, Axios 등) 사용 |
| 보안 처리 |
Firebase 인증 및 보안 규칙 자동 처리 |
⚠️ CORS 설정 필요 |
| 사용 목적 |
Firebase와의 간편한 통합 |
일반적인 HTTP 엔드포인트 |
⚠️ CORS 문제
- onCall: CORS 설정 불필요 (Firebase가 자동 처리)
- onRequest: Express와 유사하게 별도의 CORS 설정 필요
✏️ Firebase Emulator
Firebase Function은 Firebase Emulator를 통해 테스트해볼 수 있다.
아래는 Firebase Emulator로 Function을 테스트 하는 과정이다.
1. Firebase CLI 설정
- Firebase CLI 설치:
1
| npm install -g firebase-tools
|
- Firebase 로그인:
- Firebase 프로젝트 초기화:
- Functions 선택
- TypeScript 사용 권장
- Functions Emulator 활성화
2. Cloud Function 작성
HTTP 함수 예제
1
2
3
4
5
| import * as functions from "firebase-functions";
export const helloWorld = functions.https.onRequest((req, res) => {
res.send("Hello, World!");
});
|
Callable 함수 예제
1
2
3
4
5
| import * as functions from "firebase-functions";
export const addMessage = functions.https.onCall((data, context) => {
return { text: `Message received: ${data.text || 'Hello'}` };
});
|
3. TypeScript 빌드
- functions 폴더로 이동:
- 컴파일 실행:
functions/src 코드가 functions/lib로 변환됨
4. Firebase 설정 파일 확인 (firebase.json)
1
2
3
4
5
6
7
8
9
10
| {
"functions": {
"source": "functions"
},
"emulators": {
"functions": {
"port": 5001
}
}
}
|
5. Firebase Emulator 실행
1
| firebase emulators:start
|
- HTTP 함수 테스트 URL:
1
| http://127.0.0.1:5001/{프로젝트ID}/{지역}/{함수이름}
|
✏️ Scheduling with Firebase Cloud Functions
Firebase Cloud Functions는 Google Cloud의 Pub/Sub와 통합되어 스케줄링 작업을 수행할 수 있다.
■ 주요 개념
- Pub/Sub:
- Publisher (발행자):
- 메시지를 생성하고 특정 주제(Topic)에 발행.
- 예: 사용자 이벤트, 로그 데이터, 상태 변경 등을 메시지로 발행.
- Subscriber (구독자):
- 특정 주제를 구독하여 발행된 메시지를 수신.
- 예: 데이터 처리, 알림 전송, 추가 작업 수행.
■ 예제 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // 매일 자정에 실행되는 스케줄링 함수
exports.accountcleanup = onSchedule("every day 00:00", async (event) => {
// 비활성 사용자 조회
const inactiveUsers = await getInactiveUsers();
// 비활성 사용자 삭제
const promisePool = new PromisePool(
() => deleteInactiveUser(inactiveUsers),
MAX_CONCURRENT
);
await promisePool.start();
logger.log("User cleanup finished");
});
|
✏️ Next.js + Cloud Function에 Swagger 적용
Next.Js를 사용해서 프로젝트를 진행하던 중 프론트엔드 개발자와 협업이 필요해서, Cloud Function을 API Document로 작성할 수 있는지 찾아보았다.
필자는 다음과 같은 방법으로 Cloud Function을 문서화했다.
function/src/**.js : Cloud Function에 JSDoc 주석 작성
scripts/swagger-config.js : swagger-jsdoc 설정 (OpenAPI 정보, apis 경로 등)
scripts/generate-swagger.js : 위 설정을 사용해 swagger.json 생성하는 스크립트 작성
package.json에 스크립트 추가
npm run generate:swagger로 public/swagger.json 파일 생성
src/app/swagger/page.tsx : Swagger UI 페이지 작성
- 브라우저에서 확인
■ 프로젝트 구조
아래와 같은 프로젝트 구조를 적용해서 Swagger를 적용했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| project/
│
├─ firebase.json // Firebase 프로젝트 설정 (emulator, hosting, functions 등)
│
├─ next.config.js // Next.js 설정
│
├─ public/
│ └─ swagger.json // JSDoc → Swagger 변환 후 생성되는 최종 문서 (배포 시 포함)
│
├─ scripts/
│ ├─ swagger-config.js // swagger-jsdoc 설정 (OpenAPI 정보, apis 경로 등)
│ └─ generate-swagger.js // 위 설정을 사용해 swagger.json 생성하는 스크립트
│
├─ src/
│ └─ app/ // Next.js 13+ App Router
│ ├─ api-doc/
│ │ └─ page.tsx // Swagger UI 페이지 (use client 또는 dynamic import)
│ └─ page.tsx // 예시: 메인 페이지
│
└─ functions/
├─ package.json // Firebase Functions 전용 package.json
├─ tsconfig.json // Functions TS 설정
└─ src/
├─ index.ts // Firebase Functions entry
├─ (여러 API 모듈).ts
└─ ... // 기타 파일들
|
■ 주요 설정 및 코드
1. API 주석 작성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| /**
* @swagger
* /addMessage:
* post:
* summary: "메시지 추가 API"
* description: "Firebase Database에 메시지를 추가합니다."
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* text:
* type: string
* description: "추가할 메시지"
* responses:
* 200:
* description: "메시지 추가 성공"
*/
export const addMessage = functions.https.onCall((data, context) => {
const text = data.text || "Default message";
return { text: `Message received: ${text}` };
});
|
2. Swagger 설정 파일 (swagger-config.js)
1
2
3
4
5
6
7
8
9
10
11
12
13
| // scripts/swagger-config.js
module.exports = {
// OpenAPI 기본 설정
definition: {
openapi: '3.0.0',
info: {
title: 'My Firebase Functions API',
version: '1.0.0',
},
},
// API 정의 경로
apis: ['functions/src/**/*.{ts,js}'],
};
|
3. Swagger JSON 생성 스크립트
1
2
3
4
5
6
7
8
9
10
11
12
| // scripts/generate-swagger.js
const fs = require('fs');
const swaggerJsdoc = require('swagger-jsdoc');
const config = require('./swagger-config.js');
const swaggerSpec = swaggerJsdoc(config);
// Next.js 앱의 public 폴더에 swagger.json을 생성
const outputPath = './public/swagger.json';
fs.writeFileSync(outputPath, JSON.stringify(swaggerSpec, null, 2));
console.log(`✅ Swagger JSON 생성 완료: ${outputPath}`);
|
4. Scripts 추가
프로젝트 루트의 package.json에 아래 스크립트를 추가:
1
2
3
4
5
6
| {
"scripts": {
"generate:swagger": "node scripts/generate-swagger.js",
"dev": "next dev"
}
}
|
5. Swagger UI 페이지 설정 (Next.js)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // src/app/swagger/page.tsx
'use client';
import React from 'react';
import SwaggerUI from 'swagger-ui-react';
import 'swagger-ui-react/swagger-ui.css';
export default function ApiDocPage() {
return (
<div style={{ minHeight: '100vh' }}>
{/* public 폴더의 swagger.json은 /swagger.json으로 접근합니다 */}
<SwaggerUI url="/swagger.json" />
</div>
);
}
|
6. Swagger JSON 생성
1
| npm run generate:swagger
|
7. Swagger UI 확인
1
2
| firebase emulators:start
브라우저에서 `http://localhost:{포트}/swagger`로 접속하여 API 문서를 확인.
|
◼︎ 인증을 추가하는 경우
firebase authentication을 적용한 firebase function을 swagger로 테스트하고 싶다면 다음과 같이 swagger-config.ts를 수정해주면 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| module.exports = {
// OpenAPI 기본 설정
definition: {
openapi: '3.0.0',
info: {
title: 'My Firebase Functions API',
version: '1.0.0',
},
servers: [
{
url: 'http://localhost:5003/wedeoio/asia-northeast3', // 실제 local function url
},
],
// 추가: 보안 스키마 설정
components: {
securitySchemes: {
bearerAuth: {
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT', // Firebase ID 토큰도 결국 JWT 구조
},
},
},
// 추가: 전역 보안 적용 (모든 엔드포인트에 bearerAuth 요구)
security: [
{
bearerAuth: [],
},
],
},
apis: ['functions/src/**/*.{ts,js}'],
};
|
- 이 때 firebase ID 토큰은 로그인 한 뒤 프론트 코드에서 받을 수 있다.
```typescript
// 프론트 코드
const userCredential = await _auth.signInWithEmailAndPassword(auth, email, password);
// 로그인 성공 시, userCredential.user에서 ID 토큰 가져오기
const token = await userCredential.user.getIdToken(/* forceRefresh */ true);
```