1세대 Node.js 함수를 2세대로 업그레이드

현재 1세대 함수를 사용하는 앱은 이 가이드의 안내에 따라 2세대로의 마이그레이션을 고려해야 합니다. 2세대 함수는 Cloud Run을 사용하여 더 우수한 성능, 구성, 모니터링 등을 제공합니다.

이 페이지의 예시에서는 CommonJS 모듈(require 스타일 가져오기)과 함께 JavaScript를 사용한다고 가정하지만, ESM(import … from 스타일 가져오기) 및 TypeScript와 함께 JavaScript를 사용할 때도 동일한 원칙이 적용됩니다.

마이그레이션 프로세스

1세대 함수와 2세대 함수가 동일한 파일에 나란히 공존할 수 있습니다. 이렇게 하면 준비가 되었을 때 단계별로 쉽게 마이그레이션할 수 있습니다. 계속 진행하기 전에 한 번에 하나의 함수를 마이그레이션하고 테스트 및 확인을 수행하는 것이 좋습니다.

Firebase CLI 및 firebase-function 버전 확인

Firebase CLI 버전 12.00firebase-functions 버전 4.3.0을 사용하고 있는지 확인합니다. 모든 최신 버전은 1세대뿐 아니라 2세대도 지원합니다.

가져오기 업데이트

2세대 함수는 firebase-functions SDK의 v2 하위 패키지에서 가져옵니다. 이 다른 가져오기 경로가 있으면 Firebase CLI가 함수 코드를 1세대 함수로 배포할지 아니면 2세대 함수로 배포할지 결정할 수 있습니다.

v2 하위 패키지는 모듈식이므로 필요한 특정 모듈만 가져오는 것이 좋습니다.

이전: 1세대

const functions = require("firebase-functions/v1");

이후: 2세대

// explicitly import each trigger
const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");

트리거 정의 업데이트

2세대 SDK는 모듈식 가져오기를 선호하므로 트리거 정의를 업데이트하여 이전 단계에서 변경한 가져오기를 반영합니다.

일부 트리거의 콜백에 전달되는 인수가 변경되었습니다. 이 예시에서는 onDocumentCreated 콜백의 인수가 단일 event 객체로 통합되었습니다. 또한 일부 트리거에는 onRequest 트리거의 cors 옵션과 같은 편리한 새 구성 기능이 있습니다.

이전: 1세대

const functions = require("firebase-functions/v1");

exports.date = functions.https.onRequest((req, res) => {
  // ...
});

exports.uppercase = functions.firestore
  .document("my-collection/{docId}")
  .onCreate((change, context) => {
    // ...
  });

이후: 2세대

const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");

exports.date = onRequest({cors: true}, (req, res) => {
  // ...
});

exports.uppercase = onDocumentCreated("my-collection/{docId}", (event) => {
  /* ... */
});

매개변수화된 구성 사용

2세대 함수에서는 코드베이스 내에서 구성 매개변수를 선언적으로 정의하는 데 도움이 되는 보다 안전한 인터페이스를 위해 functions.config 지원을 중단합니다. 새로운 params 모듈을 사용하면 모든 매개변수에 유효한 값이 있는 경우 외에는 CLI에서 배포를 차단하므로 함수가 누락된 구성으로 배포되지 않습니다.

params 하위 패키지로 마이그레이션

functions.config로 환경 구성을 사용하고 있다면 기존 구성을 매개변수화된 구성으로 마이그레이션할 수 있습니다.

이전: 1세대

const functions = require("firebase-functions/v1");

exports.date = functions.https.onRequest((req, res) => {
  const date = new Date();
  const formattedDate =
date.toLocaleDateString(functions.config().dateformat);

  // ...
});

이후: 2세대

const {onRequest} = require("firebase-functions/v2/https");
const {defineString} = require("firebase-functions/params");

const dateFormat = defineString("DATE_FORMAT");

exports.date = onRequest((req, res) => {
  const date = new Date();
  const formattedDate = date.toLocaleDateString(dateFormat.value());

  // ...
});

매개변수 값 설정

처음 배포할 때 Firebase CLI가 모든 매개변수 값을 묻는 메시지를 표시하고 이 값을 dotenv 파일에 저장합니다. functions.config 값을 내보내려면 firebase functions:config:export를 실행합니다.

더 안전하게 하려면 매개변수 유형검증 규칙을 지정할 수도 있습니다.

특수한 경우: API 키

params 모듈은 API 키와 같은 민감한 값에 대해 세분화된 액세스 제어가 가능한 Cloud Secret Manager와 통합됩니다. 자세한 내용은 보안 비밀 매개변수를 참조하세요.

이전: 1세대

const functions = require("firebase-functions/v1");

exports.getQuote = functions.https.onRequest(async (req, res) => {
  const quote = await fetchMotivationalQuote(functions.config().apiKey);
  // ...
});

이후: 2세대

const {onRequest} = require("firebase-functions/v2/https");
const {defineSecret} = require("firebase-functions/params");

// Define the secret parameter
const apiKey = defineSecret("API_KEY");

exports.getQuote = onRequest(
  // make the secret available to this function
  { secrets: [apiKey] },
  async (req, res) => {
    // retrieve the value of the secret
    const quote = await fetchMotivationalQuote(apiKey.value());
    // ...
  }
);

런타임 옵션 설정

1세대와 2세대 간에 런타임 옵션 구성이 변경되었습니다. 2세대에는 모든 함수의 옵션을 설정하는 새로운 기능도 추가되었습니다.

이전: 1세대

const functions = require("firebase-functions/v1");

exports.date = functions
  .runWith({
    // Keep 5 instances warm for this latency-critical function
    minInstances: 5,
  })
  // locate function closest to users
  .region("asia-northeast1")
  .https.onRequest((req, res) => {
    // ...
  });

exports.uppercase = functions
  // locate function closest to users and database
  .region("asia-northeast1")
  .firestore.document("my-collection/{docId}")
  .onCreate((change, context) => {
    // ...
  });

이후: 2세대

const {onRequest} = require("firebase-functions/v2/https");
const {onDocumentCreated} = require("firebase-functions/v2/firestore");
const {setGlobalOptions} = require("firebase-functions/v2");

// locate all functions closest to users
setGlobalOptions({ region: "asia-northeast1" });

exports.date = onRequest({
    // Keep 5 instances warm for this latency-critical function
    minInstances: 5,
  }, (req, res) => {
  // ...
});

exports.uppercase = onDocumentCreated("my-collection/{docId}", (event) => {
  /* ... */
});

동시 실행 사용

2세대 함수의 중요한 이점은 단일 함수 인스턴스가 한 번에 둘 이상의 요청을 처리할 수 있다는 점입니다. 결과적으로 최종 사용자가 경험하는 콜드 스타트 횟수를 크게 줄일 수 있습니다. 동시 실행의 기본값은 80이지만 1~1,000 사이의 값으로 설정할 수 있습니다.

const {onRequest} = require("firebase-functions/v2/https");

exports.date = onRequest({
    // set concurrency value
    concurrency: 500
  },
  (req, res) => {
    // ...
});

동시 실행을 조정하면 성능을 개선하고 함수 비용을 절감할 수 있습니다. 동시 요청 허용에서 동시 실행에 대해 자세히 알아보세요.

전역 변수 사용 감사

동시 실행을 염두에 두지 않고 작성된 1세대 함수는 각 요청에서 설정되고 읽히는 전역 변수를 사용할 수 있습니다. 동시 실행이 사용 설정되어 있고 단일 인스턴스가 여러 요청을 한 번에 처리하기 시작하면 동시 요청이 전역 변수를 설정하고 읽기 시작할 때 함수에 버그가 발생할 수 있습니다.

업그레이드 중에 함수의 CPU를 gcf_gen1로 설정하고 concurrency를 1로 설정하여 1세대 동작을 복원할 수 있습니다.

const {onRequest} = require("firebase-functions/v2/https");

exports.date = onRequest({
    // TEMPORARY FIX: remove concurrency
    cpu: "gcf_gen1",
    concurrency: 1
  },
  (req, res) => {
    // ...
});

하지만 2세대 함수의 성능상 이점을 포기해야 하므로 이 방법은 장기적인 해결책으로 권장하지 않습니다. 대신 함수에서 전역 변수 사용을 감사하고 준비가 되면 이러한 임시 설정을 삭제합니다.

새로운 2세대 함수로 트래픽 마이그레이션

함수의 리전 또는 트리거 유형을 변경할 때와 마찬가지로 2세대 함수에 새 이름을 지정하고 트래픽을 서서히 마이그레이션해야 합니다.

이름이 같은 함수를 1세대에서 2세대로 업그레이드하고 firebase deploy를 실행할 수는 없습니다. 그렇게 하면 다음과 같은 오류가 발생합니다.

Upgrading from GCFv1 to GCFv2 is not yet supported. Please delete your old function or wait for this feature to be ready.

변경 과정에서 새 버전 및 구 버전의 함수가 동시에 실행되므로 이 단계를 수행하기 전에 먼저 멱등 함수인지 확인해야 합니다. 예를 들어 Firestore의 쓰기 이벤트에 응답하는 1세대 함수가 있는 경우, 이러한 이벤트에 대한 응답으로 쓰기에 두 번 응답(한 번은 1세대 함수에서, 한 번은 2세대 함수에서)하면 앱이 일관된 상태가 됩니다.

  1. 함수 코드에서 함수 이름을 바꿉니다. 예를 들어 resizeImageresizeImageSecondGen으로 바꿉니다.
  2. 원래의 1세대 함수와 2세대 함수가 모두 실행되도록 함수를 배포합니다.
    1. 호출 가능, 태스크 큐, HTTP 트리거의 경우 클라이언트 코드를 2세대 함수의 이름 또는 URL로 업데이트하여 모든 클라이언트가 2세대 함수를 가리키도록 합니다.
    2. 백그라운드 트리거를 사용하면 1세대 및 2세대 함수 모두 배포 즉시 모든 이벤트에 응답합니다.
  3. 전체 트래픽이 마이그레이션되면 Firebase CLI의 firebase functions:delete 명령어를 사용하여 1세대 함수를 삭제합니다.
    1. 필요한 경우 1세대 함수의 이름과 일치하도록 2세대 함수의 이름을 바꿉니다.