Firebase Cloud Function 로컬 디버깅의 주의점
Firebase Cloud Functions를 Flutter 앱과 함께 로컬에서 테스트하는 환경은 매우 빠른 개발 피드백 루프를 제공합니다.
하지만 몇 가지 환경 설정을 빠뜨리면 함수 호출이 실패하거나, 클라우드와 로컬 간 동작 차이가 발생할 수 있습니다.
이 글에서는 Functions만 로컬에서 테스트할 때 필수적으로 확인해야 할 설정과 주의점을 정리합니다.
1. Firebase Functions + 에뮬레이터 초기화
Cloud Functions만 로컬 테스트할 경우에도 Firebase 프로젝트 초기화 시 함께 Firebase Emulators 설정을 추가해 두는 것이 좋습니다.
1단계: Firebase 프로젝트 초기화
firebase init functions
초기화 중 다음 항목을 선택합니다:
- 언어: TypeScript
- ESLint: Yes (권장)
- 종속성 설치: Yes
기본 디렉터리 구조가 생성되며, functions/src/index.ts에서 다음과 같이 작성할 수 있습니다:
import { onRequest } from 'firebase-functions/v2/https';
export const hello = onRequest((req, res) => {
res.send('Hello from Firebase!');
});
2단계: 에뮬레이터 추가 설정
Firebase 초기화 시 Emulators 를 같이 선택해주면 Functions 에뮬레이터를 활성화할 수 있습니다.
만일 이미 Firebase 프로젝트를 초기화 한 상태라면 별도로 emulators 만 초기화 해 줍니다.
firebase init emulators
에뮬레이터 설정까지 마쳤다면, 다음 명령으로 Functions만 로컬에서 실행할 수 있습니다:
firebase emulators:start --only functions
3단계: ESLint 설정 완화
기본 생성된 .eslintrc.js는 다소 엄격하므로 아래와 같이 일부 규칙을 완화해두면 개발 중 불필요한 스트레스를 줄일 수 있습니다:
rules: {
"indent": "off",
"max-len": "off",
"camelcase": "off",
"@typescript-eslint/no-unused-vars": "warn"
}
indent : 들여쓰기,
max-len : 한줄 길이 제한
camelcase : 카멜케이스 체크
no-unsed-vars : 사용하지 않은 변수
위 내용들이 off 나 warn 으로 변경하지 않으면 모두 다 에러를 내는 상황이라 처음 격게 되면 굉장히 당황스럽습니다.
저는 너무 빡빡해서 저렇게 완화 시켜서 사용하고 있지만 그래도 여전히 빡빡한 상태라고 생각합니다.
2. Flutter + 안드로이드 에뮬레이터 연결 시 주의할 점
Flutter 앱에서 로컬 Cloud Functions를 호출할 때는 Android 에뮬레이터 특성과 Firebase Functions SDK 사용법 모두에 주의해야 합니다.
AndroidManifest.xml: cleartext 설정
안드로이드 앱은 기본적으로 http 요청을 차단합니다. 따라서 다음 설정을 AndroidManifest.xml에 추가해야 합니다:
<application
android:usesCleartextTraffic="true"
... >
사실 이 부분때문에 굉장히 많은 고생을 했습니다.
대부분의 설정이 다 처리 되었는데 에뮬레이터에 붙을 수 없어서 이것저것 많이 찾아보다가 마지막에 찾은 항목입니다.
안드로이드 에뮬레이터 → Firebase Functions 에뮬레이터로 붙을때는 http 연결을 사용하니 꼭 추가해 주어야 합니다.
그렇지 않으면 “UNAVAILABLE” 에러가 나오게 됩니다.
useFunctionsEmulator 설정
Flutter에서 Firebase Functions 인스턴스를 로컬 Functions와 연결하려면 useFunctionsEmulator()를 호출해야 합니다:
FirebaseFunctions functions = FirebaseFunctions.instance;
functions.useFunctionsEmulator('10.0.2.2', 5001);
Android 에뮬레이터에서 localhost 대신 10.0.2.2를 사용해야 한다는 점도 꼭 기억해야 합니다.
사용자 입장에서 Android 에뮬레이터와 Firebase Functions 에뮬레이터가 같은 PC일수 있으나 Android 에뮬레이터에서는 다른 머신이기 때문에 에뮬레이터라면 “10.0.2.2” 이고 실제 기기라면 “localhost” 입니다.
3. 리전 설정은 배포와 로컬에서 다르게 동작합니다
Cloud Functions는 기본 리전이 us-central1입니다. 클라우드에 배포할 때는 아래처럼 region을 명시하면 해당 리전으로 배치됩니다.
export const hello = onRequest(
{ region: 'asia-northeast3' },
(req, res) => {
res.send('Hello from Seoul!');
}
);
이렇게 배포하면 호출 URL도 /asia-northeast3/hello 형식이 됩니다.
로컬 디버깅 시 리전 무시됨
하지만 로컬에서는 Functions의 region 설정이 무시되며, 항상 us-central1 경로로만 호출됩니다.
예를 들어, 위 함수도 로컬에서는 다음 주소로 호출해야 합니다:
<http://localhost:5001/your-project-id/us-central1/hello>
Flutter에서 Functions 리전 지정
Flutter에서 Functions 인스턴스를 특정 리전으로 고정하려면 다음처럼 instanceFor()를 사용할 수 있습니다:
FirebaseFunctions functions = FirebaseFunctions.instanceFor(region: 'asia-northeast3');
단, 로컬 테스트 중에는 이 설정이 무시되므로, useFunctionsEmulator()를 반드시 사용해야 합니다.
4. 로그는 functions.logger를 사용
로컬에서도 console.log()는 동작하지만, 배포 후 콘솔 로그를 일관되게 보기 위해서는 functions.logger 사용이 권장됩니다:
import { logger } from 'firebase-functions';
logger.info('디버깅 로그');
logger.warn('경고 발생');
logger.error('에러 발생', new Error('상세 정보'));
Cloud 콘솔에서도 구조화된 형태로 로그를 확인할 수 있어 유지 보수 시 유용합니다.
5. 포트 충돌 방지
Functions 에뮬레이터는 기본적으로 5001 포트를 사용합니다. 만약 다른 dev 서버(React, Node 등)가 해당 포트를 사용 중이면 충돌이 발생합니다.
다음처럼 포트를 변경하여 충돌을 방지할 수 있습니다:
"emulators": {
"functions": { "port": 5002 }
}
실행 시 명시적으로 포트를 지정할 수도 있습니다:
Cloud Functions만 로컬에서 테스트하는 환경은 구성만 제대로 해두면 매우 강력하고 빠른 개발 루프를 제공합니다. 특히 Flutter와 함께 사용할 경우, 앱과 서버를 동시에 로컬에서 테스트할 수 있어 디버깅 생산성을 크게 높일 수 있습니다.
'Firebase' 카테고리의 다른 글
[Firebase]Cloud Functions 의 캐싱 전략 (0) | 2025.05.10 |
---|---|
[Firebase] Functions v1과 v2 (0) | 2025.05.09 |