앱에서 함수 호출

Firebase용 Cloud Functions 클라이언트 SDK를 사용하면 Firebase 앱에서 직접 함수를 호출할 수 있습니다. 이 방식으로 앱에서 함수를 호출하려면 Cloud Functions에서 HTTPS 호출 가능 함수를 작성 및 배포한 후 앱에서 함수를 호출하는 클라이언트 로직을 추가합니다.

HTTPS 호출 가능 함수는 HTTP 함수와 비슷하지만 동일하지 않다는 점에 주의해야 합니다. 또한 1세대 및 2세대 함수 간에 콜백 서명이 변경되었습니다.

// Adds two numbers to each other.
exports.addnumbers = onCall((request) => {
  // Numbers passed from the client.
  const firstNumber = request.data.firstNumber;
  const secondNumber = request.data.secondNumber;

  // Checking that attributes are present and are numbers.
  if (!Number.isFinite(firstNumber) || !Number.isFinite(secondNumber)) {
    // Throwing an HttpsError so that the client gets the error details.
    throw new HttpsError("invalid-argument", "The function must be called " +
            "with two arguments \"firstNumber\" and \"secondNumber\" which " +
            "must both be numbers.");
  }

  // returning result.
  return {
    firstNumber: firstNumber,
    secondNumber: secondNumber,
    operator: "+",
    operationResult: firstNumber + secondNumber,
  };
});

호출 가능 함수와 HTTP 함수의 주요 차이점은 다음과 같습니다.

  • 호출 가능 함수를 사용하면 Firebase 인증 토큰, FCM 토큰, 앱 체크 토큰이 있는 경우 자동으로 요청에 포함됩니다.
  • functions.https.onCall 트리거가 자동으로 요청 본문을 역직렬화하고 인증 토큰의 유효성을 검사합니다.

Cloud Functions(2세대) 이상용 Firebase SDK는 HTTPS 호출 가능 함수를 지원하기 위해 다음과 같은 Firebase 클라이언트 SDK 최소 버전과 연동됩니다.

  • Apple 플랫폼용 Firebase SDK 10.9.0
  • Android용 Firebase SDK 20.3.0
  • Firebase Modular Web SDK v. 9.7.0

지원되지 않는 플랫폼에서 빌드한 앱에 비슷한 기능을 추가하려면 https.onCall의 프로토콜 사양을 참조하세요. 이 가이드의 나머지 부분에서는 Apple 플랫폼, Android, 웹, C++, Unity용 HTTPS 호출 가능 함수를 작성, 배포, 호출하는 방법을 안내합니다.

호출 가능 함수 작성 및 배포

functions/v2/https 하위 패키지의 onCall 메서드를 사용하여 HTTP 호출 가능 함수를 만듭니다. 이 메서드는 data, auth, app, instanceToken 속성이 있는 event 매개변수를 사용합니다.

// Saves a message to the Firebase Realtime Database but sanitizes the
// text by removing swearwords.
exports.addmessage = onCall((request) => {
  // ...
});

실시간 데이터베이스에 SMS를 저장하는 호출 가능 함수의 경우 예를 들어 dataauth의 인증 정보와 함께 메시지 텍스트가 포함될 수 있습니다.

// Message text passed from the client.
const text = request.data.text;
// Authentication / user information is automatically added to the request.
const uid = request.auth.uid;
const name = request.auth.token.name || null;
const picture = request.auth.token.picture || null;
const email = request.auth.token.email || null;

호출 가능 함수의 위치와 호출 중인 클라이언트의 위치 간의 거리로 인해 네트워크 지연 시간이 발생할 수 있습니다. 성능을 최적화하려면 함수 위치를 지정하고(해당하는 경우) 호출 가능 함수의 위치를 클라이언트 측에서 SDK를 초기화할 때 설정한 위치와 맞추세요.

필요한 경우 앱 체크 증명을 연결하여 백엔드 리소스 악용(예: 결제 사기 또는 피싱)을 방지할 수 있습니다. Cloud Functions에 대한 앱 체크 적용 사용 설정을 참조하세요.

결과 반환

데이터를 클라이언트에 다시 전송하려면 JSON으로 인코딩될 수 있는 데이터를 반환합니다. 예를 들어 추가 작업의 결과를 반환하는 방법은 다음과 같습니다.

// returning result.
return {
  firstNumber: firstNumber,
  secondNumber: secondNumber,
  operator: "+",
  operationResult: firstNumber + secondNumber,
};

비동기 작업 후에 데이터를 반환하려면 프로미스를 반환합니다. 프로미스로 반환한 데이터가 클라이언트로 다시 전송됩니다. 예를 들어 호출 가능 함수가 실시간 데이터베이스에 쓰기 처리한 정리된 텍스트를 반환할 수 있습니다.

// Saving the new message to the Realtime Database.
const sanitizedMessage = sanitizer.sanitizeText(text); // Sanitize message.

return getDatabase().ref("/messages").push({
  text: sanitizedMessage,
  author: {uid, name, picture, email},
}).then(() => {
  logger.info("New Message written");
  // Returning the sanitized message to the client.
  return {text: sanitizedMessage};
})

오류 처리

클라이언트에서 유용한 오류 세부정보를 가져오려면 functions.https.HttpsError 인스턴스를 발생시키거나 거부된 프로미스를 반환하여 호출 가능 함수에서 오류를 반환합니다. 오류에는 code 속성이 있으며 이 속성은 functions.https.HttpsError에 나열된 값 중 하나일 수 있습니다. 또한 오류에 message 문자열도 있으며 기본값은 비어 있는 문자열입니다. 임의의 값이 있는 details 필드(선택사항)도 있을 수 있습니다. 함수에서 HttpsError 외의 오류가 발생하면 클라이언트에서 INTERNAL 메시지와 internal 코드가 포함된 오류를 수신합니다.

예를 들어 함수에서 오류 메시지와 함께 데이터 유효성 검사 및 인증 오류를 발생시켜 호출 중인 클라이언트에 반환할 수 있습니다.

// Checking attribute.
if (!(typeof text === "string") || text.length === 0) {
  // Throwing an HttpsError so that the client gets the error details.
  throw new HttpsError("invalid-argument", "The function must be called " +
          "with one arguments \"text\" containing the message text to add.");
}
// Checking that the user is authenticated.
if (!request.auth) {
  // Throwing an HttpsError so that the client gets the error details.
  throw new HttpsError("failed-precondition", "The function must be " +
          "called while authenticated.");
}

호출 가능 함수 배포

완성된 호출 가능 함수를 index.js에 저장한 후 firebase deploy를 실행하면 다른 모든 함수와 함께 배포할 수 있습니다. 호출 가능 함수만 배포하려면 다음과 같이 --only 인수를 사용하여 부분 배포를 수행합니다.

firebase deploy --only functions:addMessage

함수를 배포할 때 권한 오류가 발생하면 배포 명령어를 실행하는 사용자에게 적절한 IAM 역할이 할당되었는지 확인합니다.

클라이언트 개발 환경 설정

기본 요건을 충족하는지 확인하고 필요한 종속 항목 및 클라이언트 라이브러리를 앱에 추가합니다.

iOS+

안내에 따라 Firebase를 Apple 앱에 추가합니다.

Swift Package Manager를 사용해 Firebase 종속 항목을 설치하고 관리하세요.

  1. 앱 프로젝트를 연 상태로 Xcode에서 File(파일) > Add Packages(패키지 추가)로 이동합니다.
  2. 메시지가 표시되면 Firebase Apple 플랫폼 SDK 저장소를 추가합니다.
  3.   https://github.com/firebase/firebase-ios-sdk
  4. Cloud Functions 라이브러리를 선택합니다.
  5. 완료되면 Xcode가 백그라운드에서 자동으로 종속 항목을 확인하고 다운로드하기 시작합니다.

Web version 9

  1. 안내에 따라 Firebase를 웹 앱에 추가합니다. 터미널에서 다음 명령어를 실행해야 합니다.
    npm install firebase@9.21.0 --save
    
  2. Firebase core 및 Cloud Functions를 수동으로 요청합니다.

     import { initializeApp } from 'firebase/app';
     import { getFunctions } from 'firebase/functions';
    
     const app = initializeApp({
         projectId: '### CLOUD FUNCTIONS PROJECT ID ###',
         apiKey: '### FIREBASE API KEY ###',
         authDomain: '### FIREBASE AUTH DOMAIN ###',
       });
     const functions = getFunctions(app);
    

Kotlin+KTX

  1. 안내에 따라 Firebase를 Android 앱에 추가합니다.

  2. 모듈(앱 수준) Gradle 파일(일반적으로 <project>/<app-module>/build.gradle)에서 Cloud Functions Android 라이브러리의 종속 항목을 추가합니다. 라이브러리 버전 관리 제어에는 Firebase Android BoM을 사용하는 것이 좋습니다.

    dependencies {
        // Import the BoM for the Firebase platform
        implementation platform('com.google.firebase:firebase-bom:32.0.0')
    
        // Add the dependency for the Cloud Functions library
        // When using the BoM, you don't specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-functions-ktx'
    }
    

    Firebase Android BoM을 사용하면 앱에서 항상 호환되는 Firebase Android 라이브러리 버전만 사용합니다.

    (대안) BoM을 사용하지 않고 Firebase 라이브러리 종속 항목을 추가합니다.

    Firebase BoM을 사용하지 않도록 선택한 경우에는 종속 항목 줄에 각 Firebase 라이브러리 버전을 지정해야 합니다.

    앱에서 여러 Firebase 라이브러리를 사용하는 경우 모든 버전이 호환되도록 BoM을 사용하여 라이브러리 버전을 관리하는 것이 좋습니다.

    dependencies {
        // Add the dependency for the Cloud Functions library
        // When NOT using the BoM, you must specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-functions-ktx:20.3.0'
    }
    

Java

  1. 안내에 따라 Firebase를 Android 앱에 추가합니다.

  2. 모듈(앱 수준) Gradle 파일(일반적으로 <project>/<app-module>/build.gradle)에서 Cloud Functions Android 라이브러리의 종속 항목을 추가합니다. 라이브러리 버전 관리 제어에는 Firebase Android BoM을 사용하는 것이 좋습니다.

    dependencies {
        // Import the BoM for the Firebase platform
        implementation platform('com.google.firebase:firebase-bom:32.0.0')
    
        // Add the dependency for the Cloud Functions library
        // When using the BoM, you don't specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-functions'
    }
    

    Firebase Android BoM을 사용하면 앱에서 항상 호환되는 Firebase Android 라이브러리 버전만 사용합니다.

    (대안) BoM을 사용하지 않고 Firebase 라이브러리 종속 항목을 추가합니다.

    Firebase BoM을 사용하지 않도록 선택한 경우에는 종속 항목 줄에 각 Firebase 라이브러리 버전을 지정해야 합니다.

    앱에서 여러 Firebase 라이브러리를 사용하는 경우 모든 버전이 호환되도록 BoM을 사용하여 라이브러리 버전을 관리하는 것이 좋습니다.

    dependencies {
        // Add the dependency for the Cloud Functions library
        // When NOT using the BoM, you must specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-functions:20.3.0'
    }
    

클라이언트 SDK 초기화

Cloud Functions의 인스턴스를 초기화합니다.

Swift

lazy var functions = Functions.functions()

Objective-C

@property(strong, nonatomic) FIRFunctions *functions;
// ...
self.functions = [FIRFunctions functions];

Web version 9

const app = initializeApp({
  projectId: '### CLOUD FUNCTIONS PROJECT ID ###',
  apiKey: '### FIREBASE API KEY ###',
  authDomain: '### FIREBASE AUTH DOMAIN ###',
});
const functions = getFunctions(app);

Kotlin+KTX

private lateinit var functions: FirebaseFunctions
// ...
functions = Firebase.functions

Java

private FirebaseFunctions mFunctions;
// ...
mFunctions = FirebaseFunctions.getInstance();

함수 호출

Swift

let addMessageURL = URL(string: "https://addmessage-xyz1234-uc.a.run.app/addMessage")!

functions.httpsCallable(addMessageURL).call(["text": inputField.text]) { result, error in
  if let error = error as NSError? {
    if error.domain == FunctionsErrorDomain {
      let code = FunctionsErrorCode(rawValue: error.code)
      let message = error.localizedDescription
      let details = error.userInfo[FunctionsErrorDetailsKey]
    }
    // ...
  }
  if let data = result?.data as? [String: Any], let text = data["text"] as? String {
    self.resultField.text = text
  }
}

Web version 9

import { getFunctions, httpsCallableFromURL } from 'firebase/functions';

const functions = getFunctions();
const addMessage = httpsCallableFromURL(
  functions,
  // the URL of the function
  "https://addmessage-xyz1234-uc.a.run.app/addMessage"
);

addMessage({ text: messageText })
  .then((result) => {
    // Read result of the Cloud Function.
    const data = result.data;
    const sanitizedMessage = data.text;
  });

Kotlin+KTX

private fun addMessage(text: String): Task<String> {
    // Create the arguments to the callable function.
    val data = hashMapOf(
        "text" to text,
        "push" to true
    )

    return functions
            // The URL of the function
            .getHttpsCallableFromUrl(URL("https://addmessage-xyz1234-uc.a.run.app/addMessage"))
            .call(data)
            .continueWith { task ->
                // This continuation runs on either success or failure, but if the task
                // has failed then result will throw an Exception which will be
                // propagated down.
                val result = task.result?.data as String
                result
            }
}

클라이언트의 오류 처리

서버에서 오류가 발생하거나 결과 프로미스가 거부되면 클라이언트가 오류를 수신합니다. 함수가 반환한 오류의 유형이 function.https.HttpsError이면 클라이언트가 서버 오류로부터 오류 code, message, details를 수신합니다. 그 외의 경우는 오류에 INTERNAL 메시지와 INTERNAL 코드가 포함됩니다. 호출 가능 함수의 오류 처리 방법에 대한 안내를 참조하세요.

Swift

if let error = error as NSError? {
  if error.domain == FunctionsErrorDomain {
    let code = FunctionsErrorCode(rawValue: error.code)
    let message = error.localizedDescription
    let details = error.userInfo[FunctionsErrorDetailsKey]
  }
  // ...
}

Web version 9

import { getFunctions, httpsCallableFromURL } from "firebase/functions";

const functions = getFunctions();
const addMessage = httpsCallableFromURL(
  functions,
  // the URL of the function
  "https://addmessage-xyz1234-uc.a.run.app/addMessage"
);

addMessage({ text: messageText })
  .then((result) => {
    // Read result of the Cloud Function.
    const data = result.data;
    const sanitizedMessage = data.text;
  })
  .catch((error) => {
    // Getting the Error details.
    const code = error.code;
    const message = error.message;
    const details = error.details;
    // ...
  });

Kotlin+KTX

addMessage(inputMessage)
    .addOnCompleteListener { task ->
        if (!task.isSuccessful) {
            val e = task.exception
            if (e is FirebaseFunctionsException) {
                val code = e.code
                val details = e.details
            }
        }
    }

앱을 출시하기 전에 내 앱만 호출 가능 함수 엔드포인트에 액세스할 수 있도록 앱 체크를 사용 설정해야 합니다.