앱에서 함수 호출

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

호출 가능 함수는 다른 HTTP 함수와 비슷하며 다음과 같은 추가 기능이 있습니다.

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

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

  • iOS용 Firebase SDK 5.18.2
  • Android용 Firebase SDK 16.3.0
  • Firebase 자바스크립트 SDK 5.9.0

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

호출 가능 함수 작성 및 배포

functions.https.onCall을 사용하여 HTTPS 호출 가능 함수를 만듭니다. 이 메소드는 datacontext(선택사항) 등 2가지 매개변수를 취합니다.

// Saves a message to the Firebase Realtime Database but sanitizes the text by removing swearwords.
exports.addMessage = functions.https.onCall((data, context) => {
  // ...
});

실시간 데이터베이스에 SMS를 저장하는 호출 가능 함수의 경우 예를 들어 data에 SMS가 포함될 수 있는 반면 context 매개변수는 사용자 인증 정보를 나타냅니다.

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

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

결과 반환

데이터를 클라이언트에 다시 전송하려면 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 the message.
return admin.database().ref('/messages').push({
  text: sanitizedMessage,
  author: { uid, name, picture, email },
}).then(() => {
  console.log('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 functions.https.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 (!context.auth) {
  // Throwing an HttpsError so that the client gets the error details.
  throw new functions.https.HttpsError('failed-precondition', 'The function must be called ' +
      'while authenticated.');
}

호출 가능 함수 배포

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

$ firebase deploy --only functions:addMessage

클라이언트 개발 환경 설정

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

iOS

  1. 안내에 따라 Firebase를 iOS 앱에 추가합니다.
  2. Cloud Functions 포드를 Podfile에 추가합니다.
    pod 'Firebase/Core'
    pod 'Firebase/Functions'
  3. 파일을 저장하고 pod install을 실행합니다.

  1. 안내에 따라 Firebase를 웹 앱에 추가합니다.
  2. Firebase 및 Cloud Functions 클라이언트 라이브러리를 앱에 추가합니다.
    <script src="https://www.gstatic.com/firebasejs/5.9.0/firebase.js"></script>
    <script src="https://www.gstatic.com/firebasejs/5.9.0/firebase-functions.js"></script>
    
    Cloud Functions SDK는 npm 패키지로도 제공됩니다.
    npm install firebase@5.8.4 --save
    
    Firebase와 Cloud Functions를 수동으로 요청해야 합니다.
    const firebase = require("firebase");
    // Required for side-effects
    require("firebase/functions");
    

Android

  1. 안내에 따라 Firebase를 Android 앱에 추가합니다.
  2. Cloud Functions Android 라이브러리를 app/build.gradle 파일에 추가합니다.
    implementation 'com.google.firebase:firebase-functions:16.3.0'

C++

Android용 C++

  1. 안내에 따라 Firebase를 C++ 프로젝트에 추가합니다.
  2. Cloud Functions Android 라이브러리를 app/build.gradle 파일에 추가합니다.
    implementation 'com.google.firebase:firebase-functions:16.3.0'
  3. C++ SDK에서 libfirebase_app.alibfirebase_functions.a 정적 라이브러리를 연결합니다.

iOS용 C++

  1. 안내에 따라 Firebase를 C++ 프로젝트에 추가합니다.
  2. Cloud Functions 포드를 Podfile에 추가합니다.
    pod 'Firebase/Core'
    pod 'Firebase/Functions'
  3. 파일을 저장하고 pod install을 실행합니다.
  4. C++ SDK에서 firebase.frameworkfirebase_functions.framework를 Xcode 프로젝트에 추가합니다.

Unity

  1. Firebase Unity SDK를 다운로드합니다.
  2. 안내에 따라 Firebase를 Unity 프로젝트에 추가합니다.
  3. 애셋 > 패키지 가져오기 > 커스텀 패키지 메뉴 항목을 선택합니다.
  4. 앞에서 다운로드한 Firebase Unity SDK에서 FirebaseFunctions.unitypackage 패키지를 가져옵니다.
  5. Unity 패키지 가져오기 창이 나타나면 가져오기 버튼을 클릭합니다.

클라이언트 SDK 초기화

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

Swift

lazy var functions = Functions.functions()

Objective-C

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

firebase.initializeApp({
  apiKey: '### FIREBASE API KEY ###',
  authDomain: '### FIREBASE AUTH DOMAIN ###',
  projectId: '### CLOUD FUNCTIONS PROJECT ID ###'
  databaseURL: 'https://### YOUR DATABASE NAME ###.firebaseio.com',
});

// Initialize Cloud Functions through Firebase
var functions = firebase.functions();

자바
Android

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

Kotlin
Android

private lateinit var functions: FirebaseFunctions
// ...
functions = FirebaseFunctions.getInstance()

C++

firebase::functions::Functions* functions;
// ...
functions = firebase::functions::Functions::GetInstance(app);

Unity

functions = Firebase.Functions.DefaultInstance;

함수 호출

Swift

functions.httpsCallable("addMessage").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 text = (result?.data as? [String: Any])?["text"] as? String {
    self.resultField.text = text
  }
}

Objective-C

[[_functions HTTPSCallableWithName:@"addMessage"] callWithObject:@{@"text": _inputField.text}
                                                      completion:^(FIRHTTPSCallableResult * _Nullable result, NSError * _Nullable error) {
  if (error) {
    if (error.domain == FIRFunctionsErrorDomain) {
      FIRFunctionsErrorCode code = error.code;
      NSString *message = error.localizedDescription;
      NSObject *details = error.userInfo[FIRFunctionsErrorDetailsKey];
    }
    // ...
  }
  self->_resultField.text = result.data[@"text"];
}];

var addMessage = firebase.functions().httpsCallable('addMessage');
addMessage({text: messageText}).then(function(result) {
  // Read result of the Cloud Function.
  var sanitizedMessage = result.data.text;
  // ...
});

자바
Android

private Task<String> addMessage(String text) {
    // Create the arguments to the callable function.
    Map<String, Object> data = new HashMap<>();
    data.put("text", text);
    data.put("push", true);

    return mFunctions
            .getHttpsCallable("addMessage")
            .call(data)
            .continueWith(new Continuation<HttpsCallableResult, String>() {
                @Override
                public String then(@NonNull Task<HttpsCallableResult> task) throws Exception {
                    // This continuation runs on either success or failure, but if the task
                    // has failed then getResult() will throw an Exception which will be
                    // propagated down.
                    String result = (String) task.getResult().getData();
                    return result;
                }
            });
}

Kotlin
Android

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
            .getHttpsCallable("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
            }
}

C++

firebase::Future<firebase::functions::HttpsCallableResult> AddMessage(
    const string& text) {
  // Create the arguments to the callable function.
  firebase::Variant data = firebase::Variant::EmptyMap();
  data.map()["text"] = firebase::Variant(text);
  data.map()["push"] = true;

  // Call the function and add a callback for the result.
  firebase::functions::HttpsCallableReference doSomething =
      functions->GetHttpsCallable("addMessage");
  return doSomething.Call(data);
}

Unity

private Task<string> addMessage(string text) {
  // Create the arguments to the callable function.
  var data = new Dictionary<string, object>();
  data["text"] = text;
  data["push"] = true;

  // Call the function and extract the operation from the result.
  var function = functions.GetHttpsCallable("addMessage");
  return function.CallAsync(data).ContinueWith((task) => {
    return (string) task.Result.Data;
  });
}

클라이언트의 오류 처리

서버에서 오류가 발생하거나 결과 프라미스가 거부되면 클라이언트가 오류를 수신합니다.

함수가 반환한 오류의 유형이 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]
  }
  // ...
}

Objective-C

if (error) {
  if (error.domain == FIRFunctionsErrorDomain) {
    FIRFunctionsErrorCode code = error.code;
    NSString *message = error.localizedDescription;
    NSObject *details = error.userInfo[FIRFunctionsErrorDetailsKey];
  }
  // ...
}

var addMessage = firebase.functions().httpsCallable('addMessage');
addMessage({text: messageText}).then(function(result) {
  // Read result of the Cloud Function.
  var sanitizedMessage = result.data.text;
}).catch(function(error) {
  // Getting the Error details.
  var code = error.code;
  var message = error.message;
  var details = error.details;
  // ...
});

자바
Android

addMessage(inputMessage)
        .addOnCompleteListener(new OnCompleteListener<String>() {
            @Override
            public void onComplete(@NonNull Task<String> task) {
                if (!task.isSuccessful()) {
                    Exception e = task.getException();
                    if (e instanceof FirebaseFunctionsException) {
                        FirebaseFunctionsException ffe = (FirebaseFunctionsException) e;
                        FirebaseFunctionsException.Code code = ffe.getCode();
                        Object details = ffe.getDetails();
                    }

                    // ...
                }

                // ...
            }
        });

Kotlin
Android

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

                // ...
            }

            // ...
        })

C++

void OnAddMessageCallback(
    const firebase::Future<firebase::functions::HttpsCallableResult>& future) {
  if (future.error() != firebase::functions::kErrorNone) {
    // Function error code, will be kErrorInternal if the failure was not
    // handled properly in the function call.
    auto code = static_cast<firebase::functions::Error>(future.error());

    // Display the error in the UI.
    DisplayError(code, future.error_message());
    return;
  }

  const firebase::functions::HttpsCallableResult *result = future.result();
  firebase::Variant data = result->data();
  // This will assert if the result returned from the function wasn't a string.
  string message = data.string_value();
  // Display the result in the UI.
  DisplayResult(message);
}

// ...

// ...
  auto future = AddMessage(message);
  future.OnCompletion(OnAddMessageCallback);
// ...

Unity

 addMessage(text).ContinueWith((task) => {
  if (task.IsFaulted) {
    foreach (var inner in task.Exception.InnerExceptions) {
      if (inner is FunctionsException) {
        var e = (FunctionsException) inner;
        // Function error code, will be INTERNAL if the failure
        // was not handled properly in the function call.
        var code = e.ErrorCode;
        var message = e.ErrorMessage;
      }
    }
  } else {
    string result = task.Result;
  }
});

다음에 대한 의견 보내기...

도움이 필요하시나요? 지원 페이지를 방문하세요.