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. 오류 분석
에러 메시지를 한 줄씩 뜯어보면:
- **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`이 맞지 않는다는 뜻
- Missing: @opentelemetry/api@1.9.0 from lock file → @opentelemetry/api@1.9.0 패키지가 package-lock.json에 없다는 뜻
- 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. 왜 이 방법이 먹혔나
정리하면 이렇다:
- Firebase SDK 내부에서 @opentelemetry/api를 간접 의존(transitive dependency)하고 있었다
- 로컬에서 npm install할 때는 이게 node_modules 안에 설치가 되긴 하는데, package-lock.json에 제대로 기록이 안 되는 경우가 있다
- 로컬에서는 node_modules에 파일이 있으니까 빌드/실행이 된다
- 근데 Cloud Build에서는 npm ci로 클린 설치를 하니까, package-lock.json에 없는 패키지는 설치가 안 된다
- 해결: @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로 프로젝트 전환할 때 주의점, 그리고 실수로 잘못된 프로젝트에 배포했을 때 대처법 같은 것들.
'web' 카테고리의 다른 글
| [3편] 환경변수 관리와 Firebase 멀티 프로젝트 운영 팁 (0) | 2026.03.18 |
|---|---|
| [1편] Next.js + Firebase 프로젝트 생성부터 초기 설정까지 (Windows 11) (0) | 2026.03.18 |
| 해외에서 진짜 많이 쓰는 웹 UI 레퍼런스 5가지 (0) | 2026.03.18 |
댓글