Cloud Functions를 사용하면 클라이언트 코드를 업데이트할 필요 없이 Firebase 실시간 데이터베이스에서 이벤트를 처리할 수 있습니다. Cloud Functions를 사용하면 전체 관리 권한으로 실시간 데이터베이스 작업을 실행할 수 있으며 실시간 데이터베이스에 대한 각 변경 사항이 개별적으로 처리되도록 할 수 있습니다. DataSnapshot
또는 Admin SDK 를 통해 Firebase 실시간 데이터베이스를 변경할 수 있습니다.
일반적인 수명 주기에서 Firebase 실시간 데이터베이스 기능은 다음을 수행합니다.
- 특정 실시간 데이터베이스 위치가 변경될 때까지 기다립니다.
- 이벤트가 발생할 때 트리거하고 작업을 수행합니다(사용 사례의 예는 Cloud Functions로 무엇을 할 수 있나요? 참조).
- 지정된 문서에 저장된 데이터의 스냅샷이 포함된 데이터 개체를 받습니다.
실시간 데이터베이스 기능 트리거
functions.database 를 사용하여 실시간 데이터베이스 이벤트에 대한 새 functions.database
를 만듭니다. 함수가 트리거되는 시기를 제어하려면 이벤트 핸들러 중 하나를 지정하고 이벤트를 수신할 실시간 데이터베이스 경로를 지정합니다.
이벤트 핸들러 설정
함수를 사용하면 두 가지 특정 수준에서 실시간 데이터베이스 이벤트를 처리할 수 있습니다. 생성, 업데이트 또는 삭제 이벤트에 대해서만 구체적으로 수신하거나 경로에 대한 모든 종류의 변경을 수신할 수 있습니다. Cloud Functions는 실시간 데이터베이스에 대해 다음 이벤트 핸들러를 지원합니다.
- 실시간 데이터베이스에서 데이터가 생성, 업데이트 또는 삭제될 때 트리거되는
onWrite()
. - 실시간 데이터베이스에서 새 데이터가 생성될 때 트리거되는
onCreate()
. -
onUpdate()
- 실시간 데이터베이스에서 데이터가 업데이트될 때 트리거됩니다. -
onDelete()
- 실시간 데이터베이스에서 데이터가 삭제될 때 트리거됩니다.
인스턴스 및 경로 지정
함수가 트리거되는 시기와 위치를 제어하려면 ref(path)
를 호출하여 경로를 지정하고 선택적으로 instance('INSTANCE_NAME')
로 실시간 데이터베이스 인스턴스를 지정합니다. 인스턴스를 지정하지 않으면 함수가 Firebase 프로젝트의 기본 실시간 데이터베이스 인스턴스에 배포됩니다. 예를 들면 다음과 같습니다.
- 기본 실시간 데이터베이스 인스턴스:
functions.database.ref('/foo/bar')
- "my-app-db-2"라는 인스턴스:
functions.database.instance('my-app-db-2').ref('/foo/bar')
이러한 메서드는 함수가 실시간 데이터베이스 인스턴스 내의 특정 경로에서 쓰기를 처리하도록 지시합니다. 경로 사양은 경로 아래에서 발생하는 쓰기를 포함하여 경로에 닿는 모든 쓰기와 일치합니다. 함수의 경로를 /foo/bar
로 설정하면 다음 두 위치의 이벤트와 일치합니다.
/foo/bar
/foo/bar/baz/really/deep/path
두 경우 모두 Firebase는 이벤트가 /foo/bar
에서 발생하고 이벤트 데이터에 /foo/bar
의 이전 데이터와 새 데이터가 포함된 것으로 해석합니다. 이벤트 데이터가 클 수 있는 경우 데이터베이스 루트 근처의 단일 함수 대신 더 깊은 경로에서 여러 함수를 사용하는 것이 좋습니다. 최상의 성능을 위해 가능한 가장 깊은 수준의 데이터만 요청하십시오.
경로 구성 요소를 중괄호로 묶어 와일드카드로 지정할 수 있습니다. ref('foo/{bar}')
는 /foo
의 모든 자식과 일치합니다. 이러한 와일드카드 경로 구성 요소의 값은 함수의 EventContext.params
개체 내에서 사용할 수 있습니다. 이 예에서 값은 context.params.bar
로 사용할 수 있습니다.
와일드카드가 있는 경로는 단일 쓰기에서 여러 이벤트를 일치시킬 수 있습니다. 의 삽입
{
"foo": {
"hello": "world",
"firebase": "functions"
}
}
"/foo/{bar}"
경로를 두 번 일치시킵니다. 한 번은 "hello": "world"
로, 또 한 번은 "firebase": "functions"
로 일치합니다.
이벤트 데이터 처리
실시간 데이터베이스 이벤트를 처리할 때 반환되는 데이터 객체는 DataSnapshot
입니다. onWrite
또는 onUpdate
이벤트의 경우 첫 번째 매개변수는 트리거 이벤트 전후의 데이터 상태를 나타내는 두 개의 스냅샷이 포함된 Change
객체입니다. onCreate
및 onDelete
이벤트의 경우 반환되는 데이터 개체는 생성되거나 삭제된 데이터의 스냅샷입니다.
이 예에서 함수는 지정된 경로에 대한 스냅샷을 snap
으로 검색하고 해당 위치의 문자열을 대문자로 변환하고 수정된 문자열을 데이터베이스에 씁니다.
// Listens for new messages added to /messages/:pushId/original and creates an // uppercase version of the message to /messages/:pushId/uppercase exports.makeUppercase = functions.database.ref('/messages/{pushId}/original') .onCreate((snapshot, context) => { // Grab the current value of what was written to the Realtime Database. const original = snapshot.val(); functions.logger.log('Uppercasing', context.params.pushId, original); const uppercase = original.toUpperCase(); // You must return a Promise when performing asynchronous tasks inside a Functions such as // writing to the Firebase Realtime Database. // Setting an "uppercase" sibling in the Realtime Database returns a Promise. return snapshot.ref.parent.child('uppercase').set(uppercase); });
사용자 인증 정보 접근
EventContext.auth
및 EventContext.authType
에서 함수를 트리거한 사용자에 대한 권한을 포함한 사용자 정보에 액세스할 수 있습니다. 이것은 보안 규칙을 시행하는 데 유용할 수 있으며 함수가 사용자의 권한 수준에 따라 다른 작업을 완료할 수 있도록 합니다.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
exports.simpleDbFunction = functions.database.ref('/path')
.onCreate((snap, context) => {
if (context.authType === 'ADMIN') {
// do something
} else if (context.authType === 'USER') {
console.log(snap.val(), 'written by', context.auth.uid);
}
});
또한 사용자 인증 정보를 활용하여 사용자를 "가장"하고 사용자 대신 쓰기 작업을 수행할 수 있습니다. 동시성 문제를 방지하려면 아래와 같이 앱 인스턴스를 삭제해야 합니다.
exports.impersonateMakeUpperCase = functions.database.ref('/messages/{pushId}/original')
.onCreate((snap, context) => {
const appOptions = JSON.parse(process.env.FIREBASE_CONFIG);
appOptions.databaseAuthVariableOverride = context.auth;
const app = admin.initializeApp(appOptions, 'app');
const uppercase = snap.val().toUpperCase();
const ref = snap.ref.parent.child('uppercase');
const deleteApp = () => app.delete().catch(() => null);
return app.database().ref(ref).set(uppercase).then(res => {
// Deleting the app is necessary for preventing concurrency leaks
return deleteApp().then(() => res);
}).catch(err => {
return deleteApp().then(() => Promise.reject(err));
});
});
이전 값 읽기
Change
개체에는 이벤트 전에 실시간 데이터베이스에 저장된 내용을 검사할 수 있는 before
속성이 있습니다. before
속성은 모든 메서드(예: val()
및 exists()
)가 이전 값을 참조하는 DataSnapshot
을 반환합니다. 원본 DataSnapshot
을 사용하거나 after
속성을 읽어 새 값을 다시 읽을 수 있습니다. Change
에 대한 이 속성은 이벤트가 발생한 후 데이터 상태를 나타내는 또 다른 DataSnapshot
입니다.
예를 들어 before
속성을 사용하여 함수가 처음 생성될 때 텍스트만 대문자로 표시되도록 할 수 있습니다.
exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
.onWrite((change, context) => {
// Only edit data when it is first created.
if (change.before.exists()) {
return null;
}
// Exit when the data is deleted.
if (!change.after.exists()) {
return null;
}
// Grab the current value of what was written to the Realtime Database.
const original = change.after.val();
console.log('Uppercasing', context.params.pushId, original);
const uppercase = original.toUpperCase();
// You must return a Promise when performing asynchronous tasks inside a Functions such as
// writing to the Firebase Realtime Database.
// Setting an "uppercase" sibling in the Realtime Database returns a Promise.
return change.after.ref.parent.child('uppercase').set(uppercase);
});