콘솔로 이동

컬렉션 및 하위 컬렉션 삭제

Cloud Firestore의 데이터 삭제를 특히 리소스가 제한된 모바일 앱에서 구현할 경우 다음 같은 이유로 어려울 수 있습니다.

  • 컬렉션을 원자적으로 삭제하는 작업은 없습니다.
  • 문서를 삭제해도 하위 컬렉션의 문서는 삭제되지 않습니다.
  • 문서에 동적 하위 컬렉션이 있는 경우 지정된 경로에서 삭제할 데이터를 파악하기가 어려울 수 있습니다.
  • 500개를 초과하는 문서 컬렉션을 삭제하려면 일괄 쓰기 작업을 여러 번 수행하거나 하나씩 삭제하는 작업을 수백 번 수행해야 합니다.
  • 많은 앱에서 최종 사용자에게 전체 컬렉션을 삭제할 권한을 부여하는 것은 적절하지 않습니다.

다행히 Cloud Function을 작성하여 전체 컬렉션 또는 컬렉션 트리를 안전하고 효율적으로 삭제할 수 있습니다.

계속하기 전에 Cloud Firestore 데이터 모델에 대해 읽어보시기 바랍니다.

솔루션: 호출 가능한 Cloud Function을 사용한 데이터 삭제

이 가이드에서는 호출 가능한 Cloud Function을 사용하여 데이터를 삭제하는 방법을 설명합니다. 이 함수를 배포하면 모바일 앱 또는 웹사이트에서 직접 호출하여 문서 및 컬렉션을 재귀적으로 삭제할 수 있습니다.

함수를 배포하고 데모를 사용해 보려면 샘플 코드를 참조하세요.

Cloud Function

아래 Cloud Function은 컬렉션과 해당 컬렉션의 모든 하위 항목을 삭제합니다.

Cloud Function용으로 자체적인 재귀 삭제 로직을 구현하는 대신 Firebase 명령줄 인터페이스(CLI)에서 firestore:delete 명령어를 활용할 수 있습니다. firebase-tools 패키지를 사용하여 Firebase CLI의 모든 함수를 자체 Node.js 애플리케이션으로 가져올 수 있습니다.

Firebase CLI는 Cloud Firestore REST API를 사용하여 지정된 경로에 있는 모든 문서를 찾아 개별적으로 삭제합니다. 이를 구현하는 데 앱의 특정 데이터 계층구조에 대한 지식은 필요 없으며, 더 이상 상위 항목이 존재하지 않는 '분리된' 문서도 찾아서 삭제합니다.

Node.js

/**
 * Initiate a recursive delete of documents at a given path.
 *
 * The calling user must be authenticated and have the custom "admin" attribute
 * set to true on the auth token.
 *
 * This delete is NOT an atomic operation and it's possible
 * that it may fail after only deleting some documents.
 *
 * @param {string} data.path the document or collection path to delete.
 */
exports.recursiveDelete = functions
  .runWith({
    timeoutSeconds: 540,
    memory: '2GB'
  })
  .https.onCall((data, context) => {
    // Only allow admin users to execute this function.
    if (!(context.auth && context.auth.token && context.auth.token.admin)) {
      throw new functions.https.HttpsError(
        'permission-denied',
        'Must be an administrative user to initiate delete.'
      );
    }

    const path = data.path;
    console.log(
      `User ${context.auth.uid} has requested to delete path ${path}`
    );

    // Run a recursive delete on the given document or collection path.
    // The 'token' must be set in the functions config, and can be generated
    // at the command line by running 'firebase login:ci'.
    return firebase_tools.firestore
      .delete(path, {
        project: process.env.GCLOUD_PROJECT,
        recursive: true,
        yes: true,
        token: functions.config().fb.token
      })
      .then(() => {
        return {
          path: path
        };
      });
  });

위의 Cloud Function은 호출 가능 함수로 구현됩니다. 즉, 로컬 함수와 마찬가지로 모바일 앱이나 웹사이트에서 직접 호출할 수 있습니다.

클라이언트 호출

함수를 호출하려면 Firebase SDK에서 함수에 대한 참조를 가져오고 필수 매개변수를 전달합니다.

/**
 * Call the 'recursiveDelete' callable function with a path to initiate
 * a server-side delete.
 */
function deleteAtPath(path) {
    var deleteFn = firebase.functions().httpsCallable('recursiveDelete');
    deleteFn({ path: path })
        .then(function(result) {
            logMessage('Delete success: ' + JSON.stringify(result));
        })
        .catch(function(err) {
            logMessage('Delete failed, see console,');
            console.warn(err);
        });
}

호출 가능한 클라우드 함수에 클라이언트 SDK를 사용하면 사용자의 인증 상태 및 path 매개변수가 원격 함수로 원활하게 전달됩니다. 함수가 완료되면 클라이언트는 결과 또는 예외가 있는 콜백을 수신합니다. Android, iOS 또는 다른 플랫폼에서 클라우드 함수를 호출하는 방법에 대해 알아보려면 문서를 읽어보시기 바랍니다.

제한사항

위에 나온 솔루션은 호출 가능 함수로 컬렉션을 삭제하는 방법을 다루며 다음 제한사항이 있습니다.

  • 일관성 - 위의 코드는 문서를 한 번에 하나씩 삭제합니다. 진행 중인 삭제 작업이 있을 때 쿼리하면 삭제할 문서 일부만 삭제된 부분적 완료 상태가 결과에 반영될 수 있습니다. 삭제 작업이 균일하게 성공 또는 실패한다는 보장이 없으므로 부분적으로만 삭제되는 경우에 대비해야 합니다.
  • 시간 제한 - 위의 함수는 시간 초과가 발생하기 전에 최대 540초 동안 실행되도록 구성됩니다. 최적의 상황에서는 삭제 코드가 초당 4000개 문서를 삭제할 수 있습니다. 2백만 개를 초과하는 문서를 삭제해야 하는 경우 시간 초과가 발생하지 않도록 자체 서버에서 작업을 실행하는 방안을 고려해야 합니다.