Firestore arrayUnion 중첩 배열 오류: 배열을 그대로 넘기면 실패하는 이유

2026.09.16·약 7분·작성: 해비·블로그 소개

arrayUnion(tags)는 태그 배열을 여러 요소가 아니라 배열 하나로 전달합니다. Firestore Standard edition은 배열 안에 배열을 허용하지 않으므로 Nested arrays are not supported 오류가 발생합니다. 여러 값을 추가하려면 arrayUnion(...tags)처럼 펼쳐서 전달합니다.

오류 증상: arrayUnion에 배열을 넣자 중첩 배열 오류가 난다

화면에서 선택한 태그가 ["react", "firebase"]처럼 배열로 준비되어 있을 때 다음처럼 그대로 넘기기 쉽습니다.

const tags = ["react", "firebase"];

arrayUnion(tags);

하지만 Firestore Web SDK가 쓰기 데이터를 직렬화하는 단계에서 아래 오류가 발생합니다.

Function arrayUnion() called with invalid data.
Nested arrays are not supported (found in document posts/post-1)

Firebase JavaScript SDK 공개 이슈에도 같은 오류 문구가 보고되었고, 작성자는 실제 데이터에 배열 안의 배열이 있었다고 확인했습니다. 이 오류는 Rules 거부나 네트워크 장애가 아니라 전송할 값의 구조가 잘못된 경우입니다.

환경·재현 조건: 서버에 쓰지 않고 직렬화 단계만 확인한다

검증 환경: Node.js 24.19.0, Firebase Web SDK 12.19.0, ESM 모듈. writeBatch().update()로 쓰기 명령을 구성하되 commit()은 호출하지 않았습니다. 따라서 실제 Firebase 프로젝트·인증·Rules 없이 동일한 직렬화 오류를 재현할 수 있습니다.

먼저 패키지를 설치합니다.

npm install firebase@12.19.0

repro-array-union.mjs · 원래 코드

import { initializeApp } from "firebase/app";
import { arrayUnion, doc, getFirestore, writeBatch } from "firebase/firestore";

const app = initializeApp({ projectId: "blogflow-repro" }, "array-union-repro");
const db = getFirestore(app);
const tags = ["react", "firebase"];

try {
  writeBatch(db).update(doc(db, "posts", "post-1"), {
    tags: arrayUnion(tags),
  });
  console.log("검증 통과");
} catch (error) {
  console.error(error.message);
  process.exitCode = 1;
}

node repro-array-union.mjs를 실행하면 arrayUnion(tags)를 해석하는 시점에 종료 코드 1로 실패합니다.

원인: arrayUnion은 배열 한 개가 아니라 여러 요소를 받는다

JavaScript API의 함수 시그니처는 arrayUnion(...elements)입니다. 인수 하나하나가 Firestore 배열에 추가할 요소입니다.

  • arrayUnion("react", "firebase"): 문자열 요소 두 개를 전달
  • arrayUnion(tags): 배열 요소 한 개를 전달
  • arrayUnion(...tags): 배열을 펼쳐 문자열 요소 두 개를 전달
arrayUnion에 배열 전체를 넣은 경우와 펼침 연산자로 요소를 나눈 경우 비교
배열 변수 자체를 한 요소로 넣지 말고, 추가할 요소들을 펼쳐 전달합니다.

Firestore Standard edition의 배열에는 다른 배열을 요소로 넣을 수 없습니다. 그래서 arrayUnion(tags)가 만들려는 구조는 개념적으로 [["react", "firebase"]]가 되어 거부됩니다.

변경 코드: 펼침 연산자로 태그를 개별 인수로 전달한다

fix-array-union.mjs · 수정 코드

import { initializeApp } from "firebase/app";
import { arrayUnion, doc, getFirestore, writeBatch } from "firebase/firestore";

const app = initializeApp({ projectId: "blogflow-repro" }, "array-union-fix");
const db = getFirestore(app);
const tags = ["react", "firebase"];

try {
  writeBatch(db).update(doc(db, "posts", "post-1"), {
    tags: arrayUnion(...tags),
  });
  console.log(`전달 요소: ${JSON.stringify(tags)}`);
  console.log("Firestore 직렬화 검증 통과");
} catch (error) {
  console.error(error.message);
  process.exitCode = 1;
}

수정은 괄호 안에 ...를 추가하는 한 줄이지만 의미는 분명합니다. tags 배열을 하나의 값으로 전달하지 않고 각 문자열을 독립된 인수로 전달합니다. 공식 Firestore 문서도 여러 항목을 추가할 때 여러 인수를 사용하거나 배열에 펼침 연산자를 적용하는 예제를 제공합니다.

실제 검증 결과: 같은 입력에서 실패와 통과를 비교했다

Firebase Web SDK 12.19.0 직렬화 실행 결과
실행 입력 기대 결과 실제 결과 종료
원래 코드 arrayUnion(tags) 중첩 배열 오류 Nested arrays are not supported 1
수정 코드 arrayUnion(...tags) 직렬화 통과 ["react","firebase"] 전달 후 통과 0

두 파일을 실제로 실행했고, 원래 코드는 문서 경로 posts/post-1에서 실패했습니다. 수정 코드는 동일한 태그 배열을 사용하면서 오류 없이 쓰기 명령을 구성했습니다.

적용 한계: 펼침 연산자가 모든 배열 문제를 해결하지는 않는다

  • 이번 검증은 commit() 전 직렬화 단계까지입니다. 실제 원격 저장, 인증, Security Rules, 네트워크 성공은 검증하지 않았습니다.
  • arrayUnion()은 기존 배열에 없는 요소만 추가합니다. 원하는 순서에 끼워 넣거나 특정 위치를 수정하는 API는 아닙니다.
  • 객체도 요소로 전달할 수 있지만, 객체의 일부 키만 같은 경우가 아니라 전체 값이 같은지를 기준으로 중복 여부가 판단됩니다.
  • Standard edition에서 실제로 중첩 배열 구조가 필요하다면 배열 대신 map 객체 등 다른 데이터 구조를 설계해야 합니다.
  • tags가 빈 배열일 때 업데이트 자체를 생략할지는 애플리케이션 정책으로 정해야 합니다.

이어서 학습하기

공식 자료와 공개 사례

이 글이 도움이 되었나요?

조회 중

Firebase 학습 순서

필수 13개 · 전체 19개

읽음 기록 관리

전체 과정 목차 (19개)
  1. 필수 길잡이 · Firebase 실무 로드맵: Auth, Firestore, Storage, Functions
  2. 필수 학습 · Firebase 초기화 구조: Next.js firebase.ts 설계 기준
  3. 필수 학습 · Firebase Authentication 사용 기준: 로그인 유지와 비밀번호 재설정
  4. 필수 학습 · Firebase 보안 규칙 설계: Firestore와 Storage 권한 관리하기
  5. 필수 학습 · Firestore CRUD 사용법: 컬렉션 구조와 읽기 쓰기 흐름
  6. 필수 학습 · Firebase Auth Context 설계: 로그인 권한과 라우팅 관리하기
  7. 필수 학습 · Firebase Storage 이미지 업로드 사용법: 상품 이미지 관리 흐름 만들기
  8. 필수 학습 · Firebase Custom Claims 사용법: 관리자 권한 구분하기
  9. 필수 학습 · Firebase Functions v2 사용법: 트리거 배포 Secret 처리
  10. 필수 학습 · Firestore seed data 설계: 리뷰 더미 데이터 구조 잡기
  11. 선택 참고 · Firebase 배포 제외 파일 설정: firebase.json의 ignore 사용법
  12. 선택 참고 · Firebase Firestore 인덱스 삭제 질문 해결: 배포 중 안전하게 판단하기
  13. 선택 참고 · Firebase Auth unauthorized-domain 오류 해결: 로그인 도메인 설정 확인
  14. 선택 참고 · Firebase permission-denied 오류 해결: Firestore Rules 체크리스트
  15. 선택 참고 · Firebase Storage 이미지 오류 해결: 403·404·token·Rules 확인법
  16. 선택 참고 · Firebase CORS 오류 해결: Storage 이미지 업로드가 막힐 때 확인할 설정
  17. 필수 선수 · Firebase 실무 오류 해결 모음: Storage, Firestore, Auth 체크리스트
  18. 필수 학습 · Firestore undefined 오류 해결: 선택 필드가 저장을 막을 때
  19. 필수 학습 · Firestore arrayUnion 중첩 배열 오류: 배열을 그대로 넘기면 실패하는 이유 현재 글

새 글 받아보기

RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.

RSS 피드 구독하기

댓글 남기기