Firexport가이드
CSV와 JSON을 Firestore로 가져오는 방법
Firebase 콘솔은 문서 추가(Add document)로 Firestore 문서를 하나씩 추가할 뿐, 파일을 불러오는 버튼은 없습니다. Google이 제공하는 가져오기인 gcloud firestore import는 CSV나 JSON이 아니라 Firestore 고유의 내보내기 파일만 읽습니다. 파일을 컬렉션에 넣는 방법은 세 가지입니다. 어느 것이 맞는지는 파일의 크기와 출처에 달려 있습니다.
2026년 10월 21일 업데이트. 방법 1의 확장 프로그램 Firexport는 저희가 만든 것입니다. 방법 2와 3에는 필요 없습니다.
짧은 답
| 1. 콘솔 + 확장 프로그램 | 2. Admin SDK 스크립트 | 3. gcloud 관리형 가져오기 | |
|---|---|---|---|
| 준비 | Chrome 확장 프로그램 설치, Google 계정 연결 | Node.js와 Google Cloud 사용자 인증 정보 | Cloud Storage 버킷과 gcloud |
| 읽는 형식 | CSV, JSON, JSON Lines 또는 붙여넣은 텍스트 | CSV, JSON, JSON Lines(코드가 읽는 것) | Firestore 고유의 내보내기 파일만 |
| 필드 유형 | 필드마다 미리보기를 보며 선택 | 코드가 변환하는 대로 | 내보내기의 유형 그대로 |
| 이미 있는 문서 | 그대로 두기, 바꾸기 또는 합치기 | 생성, 덮어쓰기 또는 병합 | 덮어씀 |
| 결제 계정 | 필요 없음 | 필요 없음 | 필요(Blaze 요금제) |
1. Firexport로 Firebase 콘솔에서 가져오기
Firexport는 Firestore 패널 뷰의 각 컬렉션에 있는 문서 추가(Add document) 행 오른쪽 끝에 가져오기 아이콘을 추가하는 무료 Chrome 확장 프로그램입니다. 연결한 Google 계정으로 쓰므로 준비할 서비스 계정 키도, 코드도 없습니다.
- Chrome 웹 스토어에서 Firexport를 설치합니다.
- Firebase 콘솔에서 Firestore Database > 데이터(Data)를 패널 뷰(Panel view)로 열고 가져올 컬렉션을 클릭합니다.
- 문서 추가(Add document) 행 오른쪽 끝의 가져오기 아이콘을 클릭합니다.
- CSV, JSON 또는 JSON Lines 파일을 선택하거나 끌어다 놓습니다. 또는 텍스트 붙여넣기로 전환한 뒤 읽기를 클릭합니다.
- 필드마다 유형과 문서 ID를 어디서 가져올지 확인하고, 문서가 이미 있을 때 어떻게 할지 고릅니다.
- 처음에는 창이 Google 계정 연결을 요청합니다. 프로젝트에 쓸 수 있는 계정을 고르고 Google 창에서 액세스를 허용합니다. 그런 다음 가져오기를 클릭합니다.


필드 유형과 문서 ID
- CSV의 칸은 텍스트이고, JSON에는 타임스탬프나 참조가 없습니다. 그래서 Firexport는 필드마다 값을 보고 유형을 제안합니다: 문자열, 정수, 숫자(double), 부울, 타임스탬프, 참조, 지리적 위치, 바이트(base64), 또는 가져오지 않음. 모든 값이 맞을 때만 그 유형을 제안하며, 어느 것이든 바꿀 수 있습니다.
- 타임스탬프는 ISO 8601(
2026-10-01T09:30:00Z), 브라우저 시간대 기준의2026-10-01 09:30:00, 또는 밀리초로 쓸 수 있습니다. 참조는users/u1같은 경로로 쓸 수 있습니다. 맵과 배열 안의 값은 JSON의 유형을 그대로 유지하므로, 중첩 데이터는 JSON에 넣어야 합니다. CSV 열은 언제나 최상위 필드 하나입니다. - 문서 ID는 고른 열이나 JSON 객체의 키에서 가져오거나, 새 무작위 ID가 됩니다.
Document ID열은 자동으로 선택되므로, Firexport로 내보낸 파일은 그대로 가져올 수 있습니다./users/u1처럼 경로로 쓴 ID는 마지막 조각을 씁니다. - 미리보기는 첫 문서를 쓰일 모습 그대로, 값마다 유형과 함께 보여 줍니다. 유형에 맞지 않는 값이나 반복된 ID처럼 쓸 수 없는 행은 무엇이든 보내기 전에 목록으로 보여 주고 제외합니다.
문서가 이미 있을 때
기본값인 그대로 두기는 새 문서만 만듭니다. ID가 이미 있는 행은 실패하고 목록에 남습니다. 바꾸기는 문서가 파일의 필드만 정확히 갖게 합니다. 합치기는 파일의 필드를 맵 안까지 쓰고, 문서의 다른 필드는 남깁니다. 새 무작위 ID는 기존 문서와 겹치지 않습니다.

문서 하나가 Firestore 쓰기 한 번이며, 프로젝트에 과금됩니다. Firexport는 초당 최대 500회 쓰기를 보내고, 일시적인 이유로 실패한 쓰기는 다시 시도합니다. 모든 행을 쓰면 짧은 알림과 함께 창이 닫힙니다. 일부가 실패하면 그것을 목록으로 보여 주고, CSV 보고서를 제공하며, 이어서 가져올 수 있습니다. 쓰기는 연결된 계정의 권한을 쓰므로 보안 규칙이 적용되지 않습니다. 파일은 브라우저에서 Google로 바로 가며, 저희에게는 오지 않습니다. 파일과 붙여넣은 텍스트는 최대 100MB까지 가능합니다.
스크립트도 서비스 계정 키도 없이, CSV나 JSON 파일을 콘솔에서 바로 Firestore로 가져와 보세요.
2. Admin SDK 스크립트로 가져오기
100MB가 넘는 파일, 일정에 따라 돌리는 가져오기, 또는 변환을 직접 정하고 싶을 때는 Firebase Admin SDK를 쓰는 Node.js 스크립트가 직접 행을 씁니다. 아래 스크립트는 머리글 행이 있는 CSV, JSON 배열, 문서 ID를 키로 한 JSON 객체, 또는 JSON Lines를 읽어 행마다 문서 하나를 씁니다. Firestore에 쓸 수 있는 계정이 필요합니다. 프로젝트 소유자나 편집자, 또는 Cloud Datastore 사용자 역할입니다.
- SDK와 CSV 파서 설치:
npm install firebase-admin csv-parse - 로컬 사용자 인증 정보로 로그인:
gcloud auth application-default login. 서버에서는 대신GOOGLE_APPLICATION_CREDENTIALS가 서비스 계정 키를 가리키게 합니다. - 아래 스크립트를
import-to-firestore.mjs로 저장하고your-project-id를 바꾸고TYPES를 필드에 맞게 정한 뒤node import-to-firestore.mjs orders.csv orders를 실행합니다.
// Usage: node import-to-firestore.mjs orders.csv orders → one document per row in orders
// Reads .csv (with a header row), .json (an array, or an object keyed by document ID) and .jsonl.
import { readFileSync } from 'node:fs';
import { initializeApp } from 'firebase-admin/app';
import { getFirestore, Timestamp } from 'firebase-admin/firestore';
import { parse } from 'csv-parse/sync';
initializeApp({ projectId: 'your-project-id' });
const db = getFirestore();
const [file, collection] = process.argv.slice(2);
if (!file || !collection) throw new Error('Usage: node import-to-firestore.mjs <file> <collection>');
// The column or key that holds each document's ID. Rows without one get a random ID.
const ID_FIELD = 'Document ID';
// CSV cells are all text and JSON has no timestamps: list the fields to convert. The rest stay as they are.
const TYPES = { createdAt: 'timestamp', items: 'integer', total: 'number' };
// 'create' never touches an existing document (that row fails), 'set' replaces it, 'merge' adds to it.
const MODE = 'create';
const text = readFileSync(file, 'utf8');
const isCsv = file.endsWith('.csv');
let rows;
if (isCsv) {
rows = parse(text, { columns: true, bom: true, skip_empty_lines: true });
} else if (file.endsWith('.jsonl')) {
rows = text.split('\n').filter((line) => line.trim()).map((line) => JSON.parse(line));
} else {
const json = JSON.parse(text);
rows = Array.isArray(json) ? json : Object.entries(json).map(([id, data]) => ({ [ID_FIELD]: id, ...data }));
}
function convert(type, value) {
if (type === 'timestamp') {
const date = new Date(/^\d+$/.test(value) ? Number(value) : value); // ISO 8601 or milliseconds
if (Number.isNaN(date.getTime())) throw new Error(`not a timestamp: ${value}`);
return Timestamp.fromDate(date);
}
if (type === 'integer' || type === 'number') {
const number = Number(value);
if (value === '' || !Number.isFinite(number) || (type === 'integer' && !Number.isInteger(number))) {
throw new Error(`not ${type === 'integer' ? 'an integer' : 'a number'}: ${value}`);
}
return number;
}
if (type === 'boolean') return value === true || value === 'true';
return value;
}
// Convert every row before writing anything, so a bad value stops the import instead of half of it.
const docs = rows.map((row, index) => {
const data = {};
for (const [field, value] of Object.entries(row)) {
if (field === ID_FIELD || (isCsv && value === '')) continue; // an empty CSV cell is a missing field
try {
data[field] = TYPES[field] ? convert(TYPES[field], value) : value;
} catch (error) {
throw new Error(`Row ${index + 1}, ${field}: ${error.message}`);
}
}
const id = row[ID_FIELD] ? String(row[ID_FIELD]).split('/').pop() : undefined; // a path keeps its last part
return { row: index + 1, ref: id ? db.collection(collection).doc(id) : db.collection(collection).doc(), data };
});
// BulkWriter sends the writes in batches, keeps under Firestore's rate limits and retries what can be retried.
const writer = db.bulkWriter();
const failures = [];
for (const { row, ref, data } of docs) {
const write = MODE === 'create' ? writer.create(ref, data) : writer.set(ref, data, { merge: MODE === 'merge' });
write.catch((error) => failures.push(`Row ${row} (${ref.id}): ${error.message}`));
}
await writer.close();
console.log(`Wrote ${docs.length - failures.length} of ${docs.length} documents to ${collection}`);
failures.forEach((failure) => console.log(failure));- CSV의 칸은 텍스트입니다. 스크립트는
TYPES에 나열된 필드만 변환하고 나머지는 문자열로 둡니다. 빈 칸은 필드가 없는 것이 됩니다. JSON 값은 유형을 그대로 유지하고 맵과 배열도 그대로 쓰입니다. 다만 배열 바로 안의 배열은 Firestore가 거부합니다. - 무엇이든 쓰기 전에 모든 행을 변환하므로, 잘못된 값이 있으면 가져오기를 반쯤 남기는 대신 그 행과 필드를 알리며 스크립트가 멈춥니다.
MODE를'create'로 두면 이미 있는 문서는 건드리지 않고 그 행은 실패로 나열됩니다.'set'은 문서를 바꾸고'merge'는 파일의 필드를 더합니다. ID가 없는 행은 실행할 때마다 새 무작위 ID를 받으므로, 스크립트를 두 번 실행하면 그 행이 중복됩니다.BulkWriter는 쓰기를 묶어 보내고, 초당 500회로 시작해 Firestore의 500/50/5 규칙이 허용하는 만큼 늘리며, 일시적인 이유로 실패한 쓰기는 다시 시도합니다. 컬렉션은users/u1/orders같은 하위 컬렉션 경로여도 됩니다. 방법 1처럼 보안 규칙을 거치지 않으며, 문서 하나가 쓰기 한 번입니다.
3. gcloud 관리형 가져오기로 데이터베이스 전체 복사하기
gcloud firestore import는 Firestore의 관리형 가져오기로, Google Cloud 콘솔에 있는 Firestore의 가져오기/내보내기 페이지에서도 쓸 수 있습니다. gcloud firestore export가 Cloud Storage 버킷에 Firestore 고유 형식으로 쓴 것만 읽으며, CSV나 JSON은 읽지 않습니다. 백업을 복원하거나, 컬렉션을 유형 그대로 한 프로젝트나 데이터베이스에서 다른 곳으로 복사할 때 씁니다.
# 1. Export the collection from the source project.
gcloud firestore export gs://YOUR_BUCKET/orders-export --collection-ids=orders --project=source-project-id
# 2. Import that export into the destination project.
gcloud firestore import gs://YOUR_BUCKET/orders-export --project=destination-project-id- 결제 계정이 연결돼 있어야 합니다(Blaze 요금제). 가져오기는 문서마다 쓰기 1회입니다.
- 가져오기는 ID가 같은 문서를 덮어쓰고 다른 문서는 그대로 둡니다. Cloud Functions를 트리거하지 않습니다.
- 계정에 소유자, Cloud Datastore 소유자, 또는 Cloud Datastore 가져오기 내보내기 관리자 역할이 필요합니다. 다른 프로젝트의 버킷에서 가져오려면 대상 프로젝트의 Firestore 서비스 에이전트인
service-PROJECT_NUMBER@gcp-sa-firestore.iam.gserviceaccount.com이 그 버킷에 액세스할 수 있어야 합니다.
FAQ
Firebase 콘솔에서 CSV나 JSON 파일을 Firestore로 가져올 수 있나요?
아니요. Firestore의 데이터 탭은 문서 추가(Add document)로 문서를 하나씩 추가할 뿐이고, gcloud firestore import는 Firestore 고유의 내보내기 파일만 읽습니다. CSV나 JSON 파일을 불러오려면 Admin SDK 스크립트, 또는 콘솔에 가져오기를 추가하는 Firexport 같은 브라우저 확장 프로그램이 필요합니다.
JSON 파일을 Firestore로 가져오려면 어떻게 하나요?
문서마다 객체 하나가 든 JSON 배열, 또는 문서 ID를 키로 한 객체를 씁니다. Firexport에서는 컬렉션의 문서 추가(Add document) 행에 있는 가져오기 아이콘을 클릭하고 파일을 고릅니다. 맵과 배열은 JSON의 유형을 그대로 유지합니다. Admin SDK에서는 위 스크립트처럼 파일을 읽어 각 객체를 BulkWriter로 씁니다.
타임스탬프와 참조는 어떻게 가져오나요?
CSV와 JSON은 Firestore 유형을 담을 수 없으므로, 파일 속 타임스탬프는 무언가가 변환하기 전까지 그냥 텍스트입니다. Firexport는 필드의 유형을 타임스탬프, 참조 또는 지리적 위치로 정하면 변환합니다. 스크립트에서는 Timestamp.fromDate(new Date(value))나 db.doc(path)처럼 직접 변환합니다.
가져오기에도 보안 규칙이 적용되나요?
아니요. Firexport의 가져오기, Admin SDK, gcloud는 모두 계정의 IAM 권한으로 쓰며, 보안 규칙은 클라이언트 SDK에만 적용됩니다. 가져오기 전에 데이터와 계정을 확인하세요.
Firestore로 가져오는 데 비용이 얼마나 드나요?
어떤 방법이든 가져오는 문서 하나가 쓰기 한 번입니다. 무료 Spark 요금제에서는 하루 쓰기 20,000회에서 차감되며, 이를 넘는 쓰기는 할당량이 초기화될 때까지 실패합니다. Blaze에서는 무료 등급을 넘는 쓰기가 데이터베이스 위치의 요율로 과금됩니다.
