Vercel의 Next.js 빌드 실패 로그에서 환경변수와 Node 버전은 어떻게 점검하나요?
Vercel 빌드가 실패하면 로그의 마지막 문구보다 최초의 의미 있는 오류를 찾으세요. 설치·타입·빌드·환경변수·Node 버전·프로젝트 경로를 나눠 로컬과 비교합니다.
본문 근거: vercel.com/docs/deployments/troubleshoot-a-build · vercel.com/docs/environment-variables · nextjs.org/docs/app/getting-started/deploying
증상
Vercel deployment가 Ready가 되지 않고 Build Error로 종료됩니다.
Git push
↓
Vercel build 시작
↓
install 통과
↓
framework build 실패
↓
deployment 실패
환경
- Next.js
- npm
- Vercel
- GitHub 저장소
Next.js 16의 next build는 lint를 자동 실행하지 않습니다. lint는 프로젝트에 정의된 별도 검사 명령으로 실행합니다.
목차
- build log에서 첫 실패 지점 찾기
- 로컬에서 같은 build 재현
- 환경변수 누락
- TypeScript 오류
- Node 버전 차이
- Root Directory와 build command
- 수정 전
- 수정 후
- 실습 프로젝트 전체 코드
- 결과 확인
- 추가 점검
- 학습 완료 체크리스트
- TaskFlow 프로젝트 적용
- 실습 경로·결과·복구
- 다음 학습
- 공식 문서
- 단독 실습과 결과 확인
build log에서 첫 실패 지점 찾기
배포 로그가 길더라도 마지막 줄만 보는 것보다 최초의 의미 있는 오류를 찾는 편이 중요합니다.
Install dependencies
→ 성공
Next.js build
→ 실패
Type error
→ 실제 원인
뒤쪽에 보이는 “Command exited with code 1”은 결과이고 앞에 있는 타입 오류가 원인일 수 있습니다.
로그를 다음 구간으로 나눠 봅니다.
1. 저장소 checkout
2. dependency install
3. framework detection
4. build command
5. type/page generation
6. deployment output
로컬에서 같은 build 재현
Vercel에서 실패한 build 명령을 로컬에서도 실행합니다.
npm ci
npm run build
로컬에서도 같은 오류가 난다면 플랫폼 설정 이전에 코드나 프로젝트 설정을 고쳐야 합니다.
로컬은 성공하고 Vercel만 실패한다면 환경 차이를 확인합니다.
Node version
environment variables
OS case sensitivity
root directory
build command
external network/service
환경변수 누락
Production 환경에 필요한 변수가 없으면 build 또는 runtime에서 실패할 수 있습니다.
예를 들어 서버 코드가 다음 값을 필수로 요구한다고 가정합니다.
const apiSecret = process.env.API_SECRET;
if (!apiSecret) {
throw new Error("Missing API_SECRET");
}
로컬 .env.local에는 값이 있지만 Vercel Production 환경에 등록하지 않았다면 배포 환경에서는 undefined입니다.
확인할 항목은 다음과 같습니다.
변수 이름 오타
Production 범위 포함 여부
Preview에만 등록되어 있지 않은지
변경 후 새 deployment가 생성되었는지
브라우저 공개 변수와 서버 Secret이 구분되었는지
실제 Secret 값은 build log에 출력하지 않습니다.
TypeScript 오류
Next.js build 과정에서 TypeScript 오류가 배포를 막을 수 있습니다.
예를 들어 데이터 타입은 다음인데
type Task = {
id: string;
title: string;
};
코드에서 존재하지 않는 필드를 사용하면 build가 실패할 수 있습니다.
function TaskItem({ task }: { task: Task }) {
return <p>{task.name}</p>;
}
수정은 실제 데이터 모델에 맞춰 진행합니다.
function TaskItem({ task }: { task: Task }) {
return <p>{task.title}</p>;
}
배포를 통과시키기 위해 type checking 자체를 무조건 끄는 방식은 원인을 숨길 수 있습니다.
Node 버전 차이
로컬과 CI, Vercel에서 서로 다른 Node major를 사용하면 일부 패키지와 build 동작이 달라질 수 있습니다.
프로젝트 기준을 package.json에 명시할 수 있습니다.
{
"engines": {
"node": "24.x"
}
}
정확한 지원 범위는 프로젝트 의존성과 배포 플랫폼의 현재 런타임 지원을 함께 확인합니다.
중요한 것은 “무조건 최신 Node”가 아니라 개발, CI, 배포 환경의 기준을 가능한 한 일치시키는 것입니다.
Root Directory와 build command
monorepo에서는 Vercel이 package.json이 없는 경로를 프로젝트 루트로 보고 있으면 install이나 build가 예상과 다르게 실행될 수 있습니다.
repository/
├─ apps/
│ └─ web/
│ ├─ package.json
│ └─ next.config.ts
└─ packages/
이 경우 실제 웹 앱 루트가 apps/web이라면 Root Directory 설정을 확인합니다.
또한 package.json의 scripts와 Vercel build command가 충돌하지 않는지 확인합니다.
수정 전
// src/lib/env.ts
export const apiUrl = process.env.NEXT_PUBLIC_API_URL!;
non-null assertion은 값이 실제로 존재하도록 만들어 주지 않습니다.
수정 후
// src/lib/env.ts
function required(name: string, value: string | undefined) {
if (!value) {
throw new Error(`Missing environment variable: ${name}`);
}
return value;
}
export const apiUrl = required(
"NEXT_PUBLIC_API_URL",
process.env.NEXT_PUBLIC_API_URL,
);
누락이 있다면 원인이 명확한 오류로 드러납니다.
env.ts 검증은 해당 모듈을 import할 때 실행됩니다. 파일을 추가하기만 해서는 앱 전체의 환경변수가 자동 검사되지 않습니다. 필요한 서버 또는 클라이언트 코드에서 적절한 모듈을 import해야 합니다.
결과 확인
npm ci
npm run build
로컬 build가 통과하면 수정 commit을 push하고 새로운 Vercel deployment의 build log를 확인합니다.
원인 수정
↓
commit
↓
push
↓
새 deployment
↓
build log 확인
↓
Production URL 검증
추가 점검
로컬 Windows에서는 되는데 Linux build에서 실패합니다
파일명 대소문자와 import 경로를 확인합니다.
TaskItem.tsx
vs
taskItem.tsx
Preview는 되는데 Production만 실패합니다
환경변수 범위와 Production branch 설정을 확인합니다.
이전 deployment는 정상입니다
현재 deployment와 이전 성공 deployment의 commit과 설정 차이를 비교합니다. 필요하면 운영 복구를 위해 이전 정상 deployment로 되돌리는 절차를 사용합니다.
이 주제만 따로 연습하기
Vercel 계정 없이 첫 실패 지점을 찾습니다. 가상 로그 입력은 아래 세 줄이며 Task 타입은 { id: string; title: string }이라고 정의합니다.
Install completed
app/tasks/page.tsx: Property 'name' does not exist on type 'Task'
Build failed with exit code 1
수정 계획에는 app/tasks/page.tsx가 표시하려던 값이 제목인지 확인하고, 의도에 맞으면 task.name을 task.title로 바꾼 뒤 같은 버전의 타입검사와 build를 다시 실행한다고 적습니다. 마지막 종료 코드만 고치거나 검사를 끄지 않습니다. 자가 확인: 로컬은 통과하고 원격만 실패한다면 다음으로 commit·Node·루트 경로·변수 범위를 비교합니다. 결과물은 로그 행→입력 타입→변경 위치→재검증의 연결표입니다.
학습 완료 체크리스트
- build log를 단계별로 나눠 볼 수 있습니다.
- 로컬에서 동일 build를 재현할 수 있습니다.
- 환경변수 누락과 타입 오류를 구분할 수 있습니다.
- Node 버전과 Root Directory를 확인할 수 있습니다.
- 원인 수정 후 새 deployment에서 다시 검증할 수 있습니다.
선택 실습: 할 일 앱에 적용하기
TaskFlow는 작업을 관리하는 예제 앱입니다. 이 선택 실습에서는 앱을 배포하고 서버·설정·운영 결과를 확인합니다.
이번 주제: Vercel Next.js 배포 실패 해결 체크리스트: 빌드 로그·환경변수·Node 버전 확인법.
선택: 연재 프로젝트와 전체 자료
연재 프로젝트를 이어 만드는 분을 위한 자료입니다. 이 글의 개념과 예제만 찾으셨다면 필요한 파일과 실행 방법만 펼쳐보세요.
TaskFlow 프로젝트 적용
이 글부터 시작하는 전체 프로젝트
시작 ZIP — deploy-step-06-ci-install-full-20260928을 새 폴더에 풀고, 이번 단계 완료 ZIP은 다른 폴더에 풀어 비교합니다. ZIP 안 프로젝트 폴더의 package.json 위치가 실행 루트입니다.
출발점은 Next.js 누적 TaskFlow와 같은 38단계 소스입니다. 프로젝트에는 홈·로그인·개인 작업 화면, 작업 추가·완료·삭제, Route Handler와 캐시 예제가 들어 있습니다. 설치 버전을 고정한 package-lock.json도 포함합니다.
이번 변경 이유: 누락한 환경변수로 발생한 오류를 분리할 진단 예제를 추가합니다.
.env.examplesrc/lib/env.ts
- 시작 ZIP을 풀고
npm ci로 의존성을 설치합니다. Node.js 24와 npm을 사용합니다. - 위 변경 파일을 아래 전체 내용 또는 완료 ZIP의 같은 경로와 대조해 적용합니다. 다른 앱 파일은 유지합니다.
npm run typecheck,npm run build를 실행합니다. 오류가 있으면 첫 오류의 파일·명령을 기록하고 해결한 뒤 진행합니다.npm run setup-auth로 일회용 로컬 학습 계정을 설정한 후npm run dev를 실행합니다. 생성된.env.local과 입력 값은 공유하지 않습니다.
예상 결과: 홈과 로그인 화면은 공개됩니다. 로그인 전 /tasks는 로그인 화면으로 이동하고 /api/tasks는 401을 반환합니다. 로그인 후 작업 목록에서 추가·완료·삭제를 확인합니다. /cache-lab의 별도 캐시 실습은 npm run cache-api가 필요합니다.
src/lib/env.ts와 src/lib/api.ts(9단계부터)는 환경 차이를 관찰하는 별도 진단 예제입니다. 기본 TaskFlow는 같은 origin의 인증된 /api/tasks를 사용하므로 이 예제로 기존 데이터 조회나 인증을 교체하지 않습니다. 공개 API origin 변수도 해당 진단 예제를 직접 사용할 때만 설정합니다.
저장·운영 범위: 작업은 단일 Node 프로세스 메모리에 저장되고 인증은 학습용 계정 예제입니다. 서버 재시작·서버리스 인스턴스 사이의 데이터 유지는 보장하지 않습니다. 로컬 실행 확인과 실제 운영 서비스 구축은 구분하며, 원격 배포·DNS·SSH·CI·롤백 결과는 직접 실행한 항목만 문서에 기록합니다.
전체 파일·코드 보기
이번 단계의 변경 파일 전체
.env.example
AUTH_SECRET=
DEMO_EMAIL=
DEMO_PASSWORD_SALT=
DEMO_PASSWORD_HASH=
SITE_URL=
NEXT_PUBLIC_API_MOCKING=
NEXT_PUBLIC_API_URL=
src/lib/env.ts
function required(name: string, value: string | undefined) {
if (!value) {
throw new Error(`Missing environment variable: ${name}`);
}
return value;
}
export const env = {
apiUrl: required(
"NEXT_PUBLIC_API_URL",
process.env.NEXT_PUBLIC_API_URL,
),
};
나머지 앱 소스와 잠금 파일은 전체 완료 ZIP에 있습니다. 기존 코드 조각은 설명용이며 위 전체 변경 파일과 ZIP이 이 누적 실습의 기준입니다. 다음 글에는 이번 완료 ZIP을 시작본으로 가져갑니다.
이 회차는 아래 전체 TaskFlow 시작 ZIP을 기준으로 진행합니다. 이전 글의 코드를 모으지 않아도 되며, 완료 ZIP에는 이번 변경과 기존 앱 기능이 함께 들어 있습니다.
실습 프로젝트 전체 코드
이 절의 파일 목록은 기존 앱에 적용하는 참고 문서·설정 코드이며 실행 앱 전체가 아닙니다. 이 조각만 모은 폴더에서 npm ci/build를 실행하지 않습니다. 실행 가능한 전체 시작·완료 ZIP과 설치 루트는 아래 TaskFlow 프로젝트 적용 절에 별도로 안내되어 있습니다. 전체 ZIP에서는 package.json이 있는 프로젝트 폴더를 기준으로 해당 설치·빌드 절차를 따르세요.
.env.example
# 기존 앱에서 실제 사용하는 변수만 설정하세요. 값은 개발/운영 플랫폼에서 별도 관리합니다.
NEXT_PUBLIC_API_URL=
API_SECRET=
.github/workflows/ci.yml
name: CI
on:
push:
branches:
- main
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version: "24.x"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
.nvmrc
24
docs/deployment-plan.md
# TaskFlow Deployment Plan
## Build
- package manager: npm
- install: npm ci
- build: npm run build
## Primary deployment
- platform: Vercel
- environment: Production
## Alternative deployment
- Firebase Hosting: static output일 때 검토
- Firebase App Hosting: server runtime이 필요할 때 검토
## Verification
- Production URL 응답
- 주요 화면 확인
- 환경변수 확인
- 주요 route 확인
- 현재 commit 확인
- rollback 가능 여부 확인
## Runtime decision
- static-only: no
- server runtime required: yes
- reasons:
- Route Handler
- server-side environment variables
## Primary
Vercel Production
## Firebase alternative
App Hosting 우선 검토
## Static variant
정적 결과물로 분리할 경우 Firebase Hosting 검토
## Production deployment
- deployment URL: 운영 문서에서 관리
- commit SHA: 배포마다 기록
- deployment status: 확인
- build log: 확인
- smoke test: 확인
## Domain
- canonical domain: 운영 문서에서 관리
- www policy: canonical로 redirect
- HTTPS: 확인
- DNS status: 확인
- auth callback / allowed origin: 확인
docs/server-access-runbook.md
# Server Access Runbook
## Connection
- host: 저장하지 않음
- user: 환경별 문서에서 별도 관리
- authentication: SSH key 우선
- private key: 저장소에 포함 금지
## Before upload
1. 대상 서버 확인
2. 현재 경로 확인
3. 배포 대상 경로 확인
4. 기존 파일 상태 확인
## After upload
1. 파일 존재 확인
2. owner / permission 확인
3. 서비스가 실제 경로를 읽는지 확인
4. HTTP 결과 확인
## Never commit
- private key
- password
- access token
- production secret
README.md
# 배포·서버 07 — deploy-step-07-vercel-build-debug
이 ZIP은 배포 준비·운영 문서와 해당 회차 설정/코드 조각을 누적한 적용 자료입니다. 실행 앱 전체는 포함하지 않습니다. 기존 deploy-step-00-build-ready 프로젝트에 해당 파일을 검토하여 적용하세요. 이 ZIP 루트에서 npm ci/build를 실행하는 구조가 아닙니다.
.env.example은 값이 빈 설정 안내입니다. 독자의 앱에서 사용하는 변수만 .env.local 또는 플랫폼의 해당 환경에 설정하세요. URL·계정·비밀키를 ZIP에 넣지 않습니다.
CI workflow는 이미 package.json/package-lock.json과 build 스크립트가 있는 기존 npm 프로젝트 루트에 적용합니다. actions/checkout@v7, actions/setup-node@v7, Node 24.x를 사용합니다. GitHub 원격 실행은 검증하지 않았습니다.
src/lib/env.ts는 해당 모듈을 import할 때 환경 변수를 검사합니다. 파일을 추가하기만 해서는 사용하지 않는 앱 전체의 환경 설정이 자동 검증되지 않습니다.
이 자료에는 실제 배포·DNS 변경·SSH 접속·메일/알림·롤백 결과가 없습니다. 운영 작업은 독자가 본인 환경과 권한을 확인한 후 수행해야 합니다. 문서의 확인 항목은 수행할 체크리스트이며 이미 통과했다는 기록이 아닙니다.
src/lib/env.ts
function required(name: string, value: string | undefined) {
if (!value) {
throw new Error(`Missing environment variable: ${name}`);
}
return value;
}
export const env = {
apiUrl: required(
"NEXT_PUBLIC_API_URL",
process.env.NEXT_PUBLIC_API_URL,
),
};
이전 체크포인트
deploy-step-06-ci-install
이번 변경
TaskFlow에 환경변수 검증 파일이 없다면 다음처럼 추가할 수 있습니다.
src/
└─ lib/
└─ env.ts
본문의 env.ts를 사용하되 프로젝트의 실제 변수 이름에 맞춥니다.
검증
npm ci
npm run build
그다음 Vercel에서 다음을 확인합니다.
build log
environment scope
deployment commit
Production URL
이 본문은 특정 TaskFlow 저장소에서 실제 Vercel 배포를 실행했다고 주장하지 않습니다. 실제 cloud build 결과는 운영 반영 단계에서 별도로 확인해야 합니다.
완료 체크포인트
deploy-step-07-vercel-build-debug
실습 경로·결과·복구
독자 질문
Vercel 빌드 실패를 로그에서 어디부터 좁혀야 할까요?
배포 전제
실패한 deployment의 commit SHA와 build 로그, 로컬에 같은 commit을 체크아웃할 수 있는 저장소가 필요합니다. 로그·환경변수 값에 비밀정보가 보이면 공유 전에 가립니다.
실행 단계
- 실패 로그에서 install/build/타입검사 중 첫 오류와 파일 경로를 기록하고 Vercel의 Root Directory·Node 버전·build 명령을 package.json과 대조합니다.
- 동일 commit·Node 버전에서 npm ci와 npm run build를 실행합니다. 타입 오류는 지목된 파일의 입력 타입과 실제 데이터 구조를 고치고, 변수 누락은 사용 환경의 변수 이름과 범위를 확인합니다.
- 작은 수정만 새 commit으로 배포한 뒤 build 로그, deployment 상태, Production 핵심 URL을 순서대로 확인합니다.
성공·실패 확인
로컬 build 종료 코드 0과 원격 build Success는 별도 증거입니다. 원격 build가 통과해도 URL에서 서버 기능이 실패하면 runtime 로그로 분류합니다. 무작정 타입검사를 비활성화하면 원인을 숨길 수 있습니다.
복구와 검증 상태
변경 후에도 실패하면 최초 로그와 새 로그의 오류 위치를 비교합니다. 정상 배포가 존재하면 운영 영향과 데이터 호환성을 확인한 뒤 복귀 절차를 검토하고, 복귀 후 핵심 경로를 다시 확인합니다. 실제 Vercel 재배포는 미확인입니다.
다음 학습
다음 글에서는 build는 성공했지만 특정 주소에서 404 NOT_FOUND가 발생하는 경우를 Next.js route와 rewrites 기준으로 나눠 해결합니다.
다음 글: Vercel 404 NOT_FOUND 오류 해결
다음 회차 확인 과제: 08회차에서는 build가 성공해도 특정 경로가 404인 경우 라우트, rewrite, 정적 자산 요청을 분리합니다.
공식 문서
공식 문서 확인 기준: 2026-09-21
체크포인트 다운로드
글 소개·학습 준비·전체 목차

배포·서버 · 7 / 11 · 오류 해결 · Vercel Build
GitHub Actions에서 npm ci와 npm run build가 통과했는데 Vercel 배포가 실패한다면 “Vercel 문제”라고 바로 결론내리기보다 Vercel에서 실제로 어떤 환경과 명령으로 build가 실행되었는지 확인해야 합니다.
이 글에서는 실패 로그를 기준으로 install, build, 환경변수, Node 버전, 프로젝트 경로 문제를 나눠 진단합니다.
이 글이 도움이 되었나요?
읽음 기록
읽음 — / 전체 11개 · 필수 7개
새 글 받아보기
RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.