본문 바로가기
web

[2편] Firebase Hosting 배포하기 + Cloud Build 오류 트러블슈팅 (Windows 11)

by kkong93 2026. 3. 18.

1편에서 프로젝트 생성이랑 초기 설정까지 끝냈다. 이번 편에서는 실제 배포 과정이랑, 내가 한참 삽질했던 opentelemetry 오류를 어떻게 해결했는지 정리한다.

솔직히 이 글을 쓰는 이유의 80%가 이 트러블슈팅 때문이다. 진짜 몇 시간을 날렸다.

 

1편은 여기로 .. ->

2026.03.18 - [web] - [1편] Next.js + Firebase 프로젝트 생성부터 초기 설정까지 (Windows 11)

 

[1편] Next.js + Firebase 프로젝트 생성부터 초기 설정까지 (Windows 11)

나는 회사에서 안드로이드 개발을 주로 하는데, 어느 순간부터 Firebase Cloud Functions이랑 웹 대시보드까지 같이 맡게 됐다. 그러다 보니 자연스럽게 Next.js + TypeScript + Firebase 조합으로 프로젝트를

kkong-93.tistory.com

 


개발 환경 (다시 한번)

  • OS: Windows 11
  • 터미널: PowerShell
  • Node.js: v18
  • Firebase CLI: 최신
  • Firebase 프로젝트: 2개 등록 (.firebaserc에 default, production)

1. 로컬에서 먼저 확인

배포 전에 로컬에서 빌드가 되는지 확인한다:

npm run build

Next.js 빌드가 성공하면 .next/ 폴더에 결과물이 나온다. 정적 export를 쓰는 경우는 out/ 폴더에 나오고.

로컬에서 빌드 성공하면 일단 코드 문제는 아닌 거다. 근데 로컬에서 되는데 배포에서 안 되는 경우가 있다. 이게 진짜 짜증나는 부분인데, 이유는 로컬이랑 Cloud Build 환경이 다르기 때문이다.


2. Firebase 프로젝트 확인

배포 전에 어떤 프로젝트에 배포하는 건지 꼭 확인한다:

firebase use
* default (project-a)
  production (project-b)

*가 붙어있는 게 현재 활성 프로젝트다. 프로젝트 전환하려면:

firebase use production

나는 프로젝트가 2개라서 이걸 자주 헷갈렸다. 잘못된 프로젝트에 배포하면... 뭐 큰일은 안 나지만 귀찮아진다.


3. 배포

Hosting만 배포할 때:

firebase deploy --only hosting

Functions도 같이 배포할 때:

firebase deploy

또는 명시적으로:

firebase deploy --only functions,hosting

4. 오류 발생 — 여기서부터 지옥 시작

배포를 했더니 이런 에러가 떴다:

Build failed with status: FAILURE and message:
npm error code EUSAGE
npm error
npm error `npm ci` can only install packages when your package.json
and package-lock.json or npm-shrinkwrap.json are in sync.
Please update your lock file with `npm install` before continuing.
npm error
npm error Missing: @opentelemetry/api@1.9.0 from lock file

그리고 아래쪽에:

Functions deploy had errors with the following functions:
    firebase-frameworks-sound1-e8a34:ssrsound1e8a34(asia-east1)
Error: There was an error deploying functions

5. 오류 분석

에러 메시지를 한 줄씩 뜯어보면:

  1. **npm ci can only install packages when your package.json and package-lock.json are in sync** → Cloud Build 환경에서 npm ci를 실행하는데, package.json과 package-lock.json`이 맞지 않는다는 뜻
  2. Missing: @opentelemetry/api@1.9.0 from lock file → @opentelemetry/api@1.9.0 패키지가 package-lock.json에 없다는 뜻
  3. firebase-frameworks-sound1-e8a34:ssrsound1e8a34(asia-east1) → Firebase의 Web Frameworks 기능(Next.js SSR 지원)을 쓰고 있어서, Cloud Build에서 빌드를 시도하다가 실패한 것

핵심 원인

Firebase Hosting에서 Next.js SSR을 배포하면, Firebase가 내부적으로 Cloud Build를 돌려서 빌드한다. 이 Cloud Build 환경에서 npm ci를 실행하는데, npm ci는 npm install과 다르게 package-lock.json을 기준으로 정확히 설치한다.

문제는 @opentelemetry/api라는 패키지가 Firebase SDK 내부적으로 의존하는 건데, 이게 package.json에는 의존성 트리 어딘가에 있지만 package-lock.json에는 제대로 기록이 안 된 상태였다.

Android 개발자를 위한 비유: npm ci는 Gradle에서 --no-daemon --no-build-cache로 클린 빌드하는 것과 비슷하다. 캐시나 로컬 상태에 의존하지 않고, lock 파일 기준으로 정확히 설치한다. 그래서 로컬에서 npm install로는 잘 되는데 CI 환경에서는 실패하는 거다.


6. 삽질 과정 (실패한 시도들)

시도 1: 그냥 npm install 다시

npm install
firebase deploy --only hosting

실패. 같은 에러. npm install은 로컬 node_modules를 기준으로 부족한 것만 추가하기 때문에, lock 파일이 깨진 상태에서는 제대로 고쳐지지 않았다.

시도 2: package-lock.json 삭제 후 재생성

rm package-lock.json
npm install
firebase deploy --only hosting

실패. lock 파일이 새로 생기긴 했는데, 여전히 opentelemetry가 제대로 안 잡혔다. 기존 node_modules가 남아있으면 npm이 거기 있는 패키지를 기준으로 lock 파일을 만들어서, 근본적인 해결이 안 됐다.

시도 3: 클로드 CLI에게 도움 요청

솔직히 여기서 클로드 CLI한테 에러 메시지 보여주면서 "이거 어떻게 해?" 했다. 근데 계속 비슷한 답변만 나왔다. "npm install 다시 해보세요", "node_modules 삭제하고 다시 설치해보세요" 정도. 근데 그게 안 되니까 물어본 건데...

결국 핵심을 찾아낸 건 에러 메시지를 다시 정독하면서였다. Missing: @opentelemetry/api@1.9.0 from lock file — 이 패키지가 명시적으로 lock 파일에 없다는 게 문제니까, 명시적으로 설치해주면 되는 거 아닌가? 싶었다.


7. 해결 방법

결국 이 순서로 해결했다:

Step 1: opentelemetry 명시적 설치

npm install @opentelemetry/api@1.9.0

이렇게 하면 package.json의 dependencies에 @opentelemetry/api가 추가되고, package-lock.json에도 정확한 버전이 기록된다.

Step 2: node_modules, lock 파일 완전 초기화

# PowerShell 기준
rm -Force package-lock.json
rm -Recurse -Force node_modules
rm -Recurse -Force .firebase

주의: 위 명령어는 PowerShell 기준이다. Git Bash나 WSL이면 rm -rf 를 쓰면 된다.

# Git Bash / WSL / Mac / Linux
rm -f package-lock.json
rm -rf node_modules
rm -rf .firebase

.firebase 폴더도 삭제하는 이유는, Firebase CLI가 이전 배포 캐시를 가지고 있어서 꼬일 수 있기 때문이다.

Step 3: 새로 설치

npm install

이제 package.json에 @opentelemetry/api@1.9.0이 명시되어 있으니까, 새로 만들어지는 package-lock.json에도 정확하게 기록된다.

Step 4: 배포

firebase deploy --only hosting

드디어 성공.

✔ Deploy complete!

Hosting URL: https://my-project.web.app

8. 왜 이 방법이 먹혔나

정리하면 이렇다:

  1. Firebase SDK 내부에서 @opentelemetry/api를 간접 의존(transitive dependency)하고 있었다
  2. 로컬에서 npm install할 때는 이게 node_modules 안에 설치가 되긴 하는데, package-lock.json에 제대로 기록이 안 되는 경우가 있다
  3. 로컬에서는 node_modules에 파일이 있으니까 빌드/실행이 된다
  4. 근데 Cloud Build에서는 npm ci로 클린 설치를 하니까, package-lock.json에 없는 패키지는 설치가 안 된다
  5. 해결: @opentelemetry/api를 package.json에 직접 명시하면, lock 파일에도 확실하게 기록되고, Cloud Build에서도 설치가 된다

여기서 핵심은 node_modules와 package-lock.json을 둘 다 날리고 새로 설치하는 것이다. 하나만 날려서는 안 됐다. node_modules가 남아있으면 npm이 거기 있는 패키지 기준으로 lock 파일을 만들어버리기 때문이다.

Android 개발자를 위한 비유: Gradle에서 ./gradlew clean만으로 안 되고, .gradle 캐시 폴더까지 통째로 삭제해야 해결되는 빌드 오류가 있잖아. 그거랑 같은 맥락이다.


9. npm ci vs npm install 차이

이번 삽질하면서 확실히 알게 된 건데, 이 두 명령어의 차이를 정리해둔다:

npm install npm ci

기준 package.json package-lock.json
용도 개발할 때 CI/CD 배포할 때
node_modules 기존 거 유지하면서 부족한 것만 추가 기존 거 삭제하고 처음부터 설치
lock 파일 수정될 수 있음 수정 안 됨 (불일치하면 에러)
속도 느림 빠름

Cloud Build(그리고 대부분의 CI/CD)에서는 npm ci를 쓴다. 그래서 로컬에서 npm install 한 다음에 package-lock.json을 커밋하는 게 중요하다. lock 파일이 최신 상태여야 CI에서 npm ci가 통과한다.


10. 배포 후 확인

배포가 성공하면 Firebase Console > Hosting에서 배포 이력을 확인할 수 있다. 또는 터미널에 나온 URL로 직접 접속해볼 수 있다.

✔ Hosting URL: https://my-project.web.app

커스텀 도메인을 연결하고 싶으면 Firebase Console > Hosting > 커스텀 도메인 추가에서 설정하면 된다.


11. 정리: 배포 명령어 치트시트

# 현재 Firebase 프로젝트 확인
firebase use

# 프로젝트 전환
firebase use production

# Hosting만 배포
firebase deploy --only hosting

# Functions만 배포
firebase deploy --only functions

# 전부 배포
firebase deploy

# 배포 전 프리뷰 (실제 배포 안 함)
firebase hosting:channel:deploy preview

다음 편 예고

3편에서는 환경변수 관리 방법이랑, Firebase 프로젝트 2개 운영할 때 주의할 점들을 정리한다. .env.local vs Firebase 환경설정의 차이, firebase use로 프로젝트 전환할 때 주의점, 그리고 실수로 잘못된 프로젝트에 배포했을 때 대처법 같은 것들.

반응형

댓글