Firexport 로고

Firexport가이드

Firestore 데이터를 Excel로 내보내는 방법

Excel은 Firestore를 직접 읽을 수 없으므로 어떤 길이든 파일을 거칩니다. Excel에서 여는 CSV이거나, 스크립트가 대신 써 주는 .xlsx입니다. 파일을 얻는 것은 쉬운 부분입니다. Excel이 글자를 깨뜨리거나, 앞자리 0을 떼거나, 모든 것을 한 열에 욱여넣지 않게 여는 것이 대부분의 사람이 막히는 지점이고, 이 가이드는 그것도 다룹니다.

2026년 9월 24일 업데이트. 방법 1의 확장 프로그램 Firexport는 저희가 만든 것입니다. 방법 2에는 필요 없습니다.

짧은 답

  • 지금 당장 쿼리 하나의 결과만: 콘솔의 쿼리 빌더(Query builder)에서 브라우저 확장 프로그램으로 CSV를 내보낸 뒤, 데이터 > 텍스트/CSV에서(Data > From Text/CSV)로 가져옵니다. 방법 1
  • 컬렉션 전체를, 숫자와 날짜의 형식까지 잡힌 채로: .xlsx 파일을 쓰는 Admin SDK 스크립트. 방법 2
  • Excel 대신 Google Sheets: 어느 파일이든 파일 > 가져오기(File > Import)로 가져옵니다. Google Sheets

1. Firebase 콘솔에서 CSV를 내보낸 뒤 Excel에서 열기

Firebase 콘솔의 Firestore 페이지에는 쿼리를 실행해 결과를 표로 보여 주는 쿼리 빌더(Query builder)가 있지만, 그 결과를 내려받을 방법은 없습니다. Firexport는 그 결과 옆에 내보내기 버튼을 추가하는 무료 Chrome 확장 프로그램입니다.

  1. Chrome 웹 스토어에서 Firexport를 설치합니다.
  2. Firebase 콘솔에서 Firestore Database를 열고 패널 뷰(Panel view)에서 쿼리 빌더(Query builder)로 전환합니다(데이터 탭의 오른쪽 위).
  3. 쿼리 범위(Query scope)와 경로(Path)에서 컬렉션을 고르고, 필요하면 필터나 제한을 더한 뒤 실행(Run)을 클릭합니다.
  4. 결과 위의 내보내기를 클릭합니다. Firexport가 결과의 모든 페이지를 훑어 CSV 파일 하나를 내려받습니다.
  5. Excel에서 데이터 > 텍스트/CSV에서(Data > From Text/CSV)(데이터 가져오기 및 변환 그룹)로 가서 파일을 고르고, 미리 보기를 확인한 뒤 로드(Load)를 클릭합니다. 미리 보기가 이상하면 아래 표가 어느 설정을 바꿀지 알려 줍니다.
Firebase 콘솔의 Firestore 쿼리 빌더와 Firexport가 쿼리 결과 위에 추가한 내보내기 버튼
내보내기 버튼은 쿼리 빌더 결과 위에 있습니다.

Firexport의 CSV는 UTF-8 바이트 순서 표시로 시작하므로 파일을 그냥 더블클릭해도 Excel이 영어가 아닌 글자를 제대로 읽습니다. 다만 더블클릭하면 Excel이 구분 기호와 형식을 추측하므로, 가져오기 대화상자가 더 안전한 길입니다. 무료 버전에서는 타임스탬프가 콘솔에 표시되는 대로 나오고(예: “August 24, 2026 at 1:16:09 PM UTC+9”) Excel은 그것을 텍스트로 둡니다. 1회 구매 라이선스인 Firexport Advanced는 Excel이 날짜로 바꿀 수 있는 ISO 8601 타임스탬프를 쓰고, 콘솔이 “…”로 잘라 버린 긴 맵과 배열을 복원합니다.

다음 Firestore 쿼리를 콘솔에서 바로, Excel에서 깔끔하게 열리는 CSV로 내보내 보세요.

Chrome에 추가 — 무료

Excel이 CSV를 망가뜨릴 때

어떤 방법으로 내보냈든 모든 CSV에서 생기는 일입니다. 해결책은 전부 텍스트/CSV에서(From Text/CSV) 미리 보기의 설정에 있습니다.

보이는 증상이유해결
악센트 문자나 비라틴 문자가 깨짐: “José Müller”가 “José Müller”로 보임CSV가 바이트 순서 표시(BOM) 없는 UTF-8이라 Excel이 시스템의 기본 인코딩으로 읽음BOM으로 시작하는 CSV를 쓰거나(Firexport의 CSV가 그렇습니다) 파일 원본(File Origin)을 65001: 유니코드(UTF-8)로 설정
모든 행이 A열 하나에 들어감사용 지역의 목록 구분 기호가 세미콜론인데(유럽에서 흔함) 파일은 쉼표를 씀구분 기호(Delimiter)를 쉼표로 설정
“00123”이 123이 되고, 20자리 ID가 1.23457E+19가 됨Excel은 숫자처럼 보이는 것을 전부 숫자로 바꾸고, 숫자는 15자리까지만 유지함데이터 형식 검색을 데이터 형식을 검색하지 않음(Do not detect data types)으로 설정해 모든 열이 텍스트로 들어오게 함
타임스탬프가 날짜가 아니라 텍스트로 정렬됨바로 열면 Excel은 2026-08-24T04:16:09Z 같은 ISO 8601 텍스트를 텍스트로 둠. 형식 검색을 끄면 모든 열이 텍스트임데이터 변환(Transform Data)에서 그 열의 형식을 날짜/시간/표준 시간대로 바꿈

맵과 배열은 셀 하나에 텍스트로 들어옵니다. 셀 하나에는 값 하나가 들어가기 때문입니다. 그 필드를 열로 펼쳐야 한다면 데이터 변환(Transform Data)에서 그 열을 선택하고 구문 분석 > JSON(Parse > JSON)을 쓴 뒤 확장합니다. 그러려면 모든 셀이 유효한 JSON이어야 하는데, Firexport Advanced와 아래 스크립트는 늘 그렇게 쓰고, 무료 버전은 값을 JSON으로 되읽을 수 없을 때 콘솔의 텍스트를 씁니다.

2. Admin SDK 스크립트로 .xlsx 파일 쓰기

스크립트는 CSV 단계를 건너뛰고 Excel 통합 문서를 바로 쓸 수 있습니다. 셀마다 처음부터 올바른 형식으로, 숫자는 숫자로, 타임스탬프는 날짜로, ID는 텍스트로 들어가서 앞자리 0이 사라지지 않습니다. Node.js와 Firestore를 읽을 수 있는 계정이 필요합니다. 프로젝트 소유자나 편집자, 또는 Cloud Datastore 뷰어 역할입니다.

  1. Admin SDK와 ExcelJS 설치: npm install firebase-admin exceljs
  2. 로컬 사용자 인증 정보로 로그인: gcloud auth application-default login. 서버에서는 대신 GOOGLE_APPLICATION_CREDENTIALS가 서비스 계정 키를 가리키게 합니다.
  3. 아래 스크립트를 export-to-excel.mjs로 저장하고 your-project-id를 바꾼 뒤 node export-to-excel.mjs users를 실행합니다.
// Usage: node export-to-excel.mjs users   → writes users.xlsx
import ExcelJS from 'exceljs';
import { initializeApp } from 'firebase-admin/app';
import {
  getFirestore,
  FieldPath,
  Timestamp,
  GeoPoint,
  DocumentReference,
} from 'firebase-admin/firestore';

initializeApp({ projectId: 'your-project-id' });
const db = getFirestore();
const collection = process.argv[2];
if (!collection) throw new Error('Usage: node export-to-excel.mjs <collection>');

// Read the collection 1,000 documents at a time, so large collections don't time out.
const docs = [];
let last;
while (true) {
  let query = db.collection(collection).orderBy(FieldPath.documentId()).limit(1000);
  if (last) query = query.startAfter(last);
  const page = await query.get();
  if (page.empty) break;
  docs.push(...page.docs);
  last = page.docs[page.docs.length - 1];
}

// Firestore types become plain values: timestamps as ISO 8601, references as paths.
function plain(value) {
  if (value instanceof Timestamp) return value.toDate().toISOString();
  if (value instanceof DocumentReference) return value.path;
  if (value instanceof GeoPoint) return { latitude: value.latitude, longitude: value.longitude };
  if (value instanceof Uint8Array) return Buffer.from(value).toString('base64');
  if (Array.isArray(value)) return value.map(plain);
  if (value && typeof value === 'object') {
    return Object.fromEntries(Object.entries(value).map(([key, v]) => [key, plain(v)]));
  }
  return value;
}

// A timestamp field becomes a real Excel date (in UTC). A cell holds one value, so maps and arrays
// go in as JSON text. Strings stay strings, so IDs like "00123" keep their leading zeros.
function cell(value) {
  if (value instanceof Timestamp) return value.toDate();
  const v = plain(value);
  return v !== null && typeof v === 'object' ? JSON.stringify(v) : v;
}

const rows = docs.map((doc) => ({ documentId: doc.id, ...doc.data() }));
// Documents in one collection can have different fields, so the columns are all of them.
const columns = [...new Set(rows.flatMap((row) => Object.keys(row)))];

const workbook = new ExcelJS.Workbook();
const sheet = workbook.addWorksheet(collection.slice(0, 31), { views: [{ state: 'frozen', ySplit: 1 }] });
sheet.columns = columns.map((key) => ({ header: key, key, width: 20 }));
sheet.getRow(1).font = { bold: true };
for (const row of rows) {
  const added = sheet.addRow(Object.fromEntries(columns.map((key) => [key, cell(row[key])])));
  added.eachCell((c) => {
    if (c.value instanceof Date) c.numFmt = 'yyyy-mm-dd hh:mm:ss';
  });
}
await workbook.xlsx.writeFile(`${collection}.xlsx`);
console.log(`Wrote ${rows.length} documents to ${collection}.xlsx`);
  • 통합 문서에는 컬렉션 이름을 딴 시트 하나가 있고, 굵은 글씨의 고정된 머리글 행이 있습니다. documentId 열, 그다음 필드마다 열 하나입니다.
  • 타임스탬프는 yyyy-mm-dd hh:mm:ss로 표시되는 Excel 날짜이고 UTC입니다. 맵·배열·지오포인트는 JSON 텍스트, 참조는 문서 경로입니다.
  • Excel 시트 하나에는 1,048,576행이 들어가고, 스크립트는 파일을 쓸 때까지 모든 행을 메모리에 둡니다. 더 큰 컬렉션은 CSV 가이드의 gcloud와 BigQuery 경로를 쓰세요.

Google Sheets

Google Sheets는 두 파일을 모두 엽니다. 파일 > 가져오기(File > Import)로 가서 CSV나 .xlsx를 올리고, 어디에 넣을지(새 스프레드시트, 새 시트, 현재 시트) 고릅니다.

  • CSV라면 구분자를 쉼표로 설정합니다(또는 자동 감지로 둡니다).
  • 데이터에 앞자리 0이 있는 ID·전화번호·코드가 있다면 텍스트를 숫자, 날짜, 수식으로 변환(Convert text to numbers, dates, and formulas)의 체크를 풉니다. 그러지 않으면 Sheets가 “00123”을 123으로 바꿉니다.
  • 스프레드시트 하나에는 모든 시트를 합쳐 총 1,000만 개의 셀이 들어갑니다.

FAQ

Excel이 Firestore에 직접 연결할 수 있나요?

기본 커넥터로는 안 됩니다. Excel의 데이터 가져오기에는 Firestore 원본이 없습니다. 이 가이드처럼 먼저 파일을 내보냅니다. Excel이 가져오는 CSV이거나 스크립트가 쓰는 .xlsx입니다.

CSV에서 Excel이 “José” 같은 깨진 글자를 보여 주는 이유는 무엇인가요?

파일이 UTF-8이지만 바이트 순서 표시가 없어서 Excel이 시스템의 기본 인코딩으로 읽기 때문입니다. BOM으로 시작하는 CSV를 쓰거나(Firexport의 CSV가 그렇습니다), 데이터 > 텍스트/CSV에서(Data > From Text/CSV)로 가져오면서 파일 원본(File Origin)을 65001: 유니코드(UTF-8)로 설정하세요.

Excel이나 Google Sheets에는 행이 몇 개까지 들어가나요?

Excel 워크시트 하나에는 1,048,576행이 들어가고, 셀 하나에는 최대 32,767자가 들어갑니다. Google Sheets 스프레드시트 하나에는 총 1,000만 개의 셀이 들어갑니다. 더 큰 컬렉션은 gcloud와 BigQuery로 CSV를 내보내고 거기서 데이터를 다루세요.

Firestore 데이터를 내보내면 비용이 드나요?

어떤 방법이든 내보내는 문서마다 최소 한 번은 읽고, Firestore는 그것을 문서 읽기로 과금합니다. 일일 무료 할당량 안에서는 비용이 없습니다.