안녕하세요, 개발자 여러분!
오늘은 최신 풀스택 애플리케이션을 구성하면서 한 번쯤 겪게 되는 문제, 바로 npm run dev 명령어가 실패하는 상황에 대해 이야기해보려고 합니다. 특히 Vite를 프론트엔드로, **Node.js(Express)**를 백엔드로 사용하는 환경에서 이 문제는 더욱 복잡하게 다가올 수 있습니다.
최근 프로젝트를 진행하며 저 또한 이 문제로 몇 시간을 디버깅해야 했는데요. 이 글에서는 제가 겪었던 오류의 발생부터 해결까지의 전 과정을 공유하고, 단순히 표면적인 증상만을 해결하는 것이 아니라, 근본적인 원인을 찾아내고 현대적인 방식으로 문제를 해결하는 방법을 상세히 안내해 드리겠습니다.
첫 번째 증상: "빌드 디렉토리를 찾을 수 없습니다"
모든 설정이 끝난 것 같아 자신 있게 npm run dev를 실행했습니다. 하지만 서버는 곧바로 멈추며 다음과 같은 오류를 뱉어냈습니다.
Error: Could not find the build directory: /path/to/project/client/dist, make sure to build the client first
이 오류 메시지만 보면 "아, 클라이언트를 먼저 빌드해야 하는구나"라고 생각하기 쉽습니다. 하지만 개발 환경(development)에서 소스 코드를 수정할 때마다 매번 수동으로 빌드하는 것은 매우 비효율적입니다. 이는 뭔가 잘못되었다는 명백한 신호였습니다.
문제의 본질은 바로 package.json의 dev 스크립트가 백엔드 서버만 실행하고 있었다는 것입니다.
// package.json (수정 전)
"scripts": {
"dev": "tsx server/index.ts"
}
백엔드 서버는 실행되자마자 프로덕션 모드처럼 빌드된 프론트엔드 파일을 찾으려 했지만, 당연히 개발 중인 소스 코드만 있었기 때문에 디렉터리를 찾지 못하고 오류를 낸 것입니다.
해결을 위한 여정: 연쇄적으로 터지는 오류들
근본 원인을 해결하기 위해 저는 다음과 같은 현대적인 개발 환경 구축을 목표로 삼았습니다.
- 프론트엔드와 백엔드 개발 서버를 동시에 실행한다.
- 백엔드 서버는 API 요청이 아닌 모든 요청을 프론트엔드 개발 서버로 전달(프록시)한다.
- 이 모든 과정이 OS에 상관없이 안정적으로 동작해야 한다.
이 과정에서 저는 여러 번의 실패를 겪었습니다.
- 실패 1: http-proxy-middleware 패키지 누락 프록시 기능을 구현하기 위해 코드에 http-proxy-middleware를 추가했지만, 정작 npm install을 하지 않아 Error: Cannot find package 오류가 발생했습니다. 디버깅의 기본 중 하나를 놓친 셈이죠.
- 실패 2: NODE_ENV 환경 변수 누락 (가장 핵심적인 원인!) 스크립트를 분리하고 수정하는 과정에서, 기존에 있던 NODE_ENV=development 설정을 실수로 빼먹었습니다. 이 작은 실수가 모든 문제의 근원이었습니다. 백엔드 서버는 NODE_ENV가 development로 설정되지 않았기 때문에, 스스로를 프로덕션 환경으로 착각했고, 계속해서 빌드된 정적 파일을 찾으려고 시도했던 것입니다.
최종 해결책: 현대적이고 안정적인 개발 환경 구축하기
수많은 실패 끝에 도달한 최종 해결책은 다음과 같습니다. 이 방법들은 현재 풀스택 개발 환경의 표준적인 모범 사례입니다.
1단계: concurrently로 두 프로세스 동시 실행
**concurrently**는 여러 개의 명령어를 동시에 실행시켜주는 아주 유용한 도구입니다. 이를 이용해 프론트엔드와 백엔드 서버를 한 번에 켤 수 있습니다.
먼저, 개발 의존성으로 설치합니다.
npm install --save-dev concurrently
그리고 package.json 스크립트를 다음과 같이 수정합니다.
// package.json (최종)
"scripts": {
"dev": "concurrently \"npm:dev:client\" \"npm:dev:server\"",
"dev:client": "vite",
"dev:server": "cross-env NODE_ENV=development tsx watch server/index.ts"
}
이제 npm run dev를 실행하면 dev:client와 dev:server 스크립트가 동시에 실행됩니다.
2단계: cross-env로 안정적인 환경 변수 설정
Windows와 macOS/Linux는 환경 변수를 설정하는 방식이 다릅니다. **cross-env**는 이 차이를 해결하여 어떤 OS에서든 동일한 명령어로 환경 변수를 설정하게 해줍니다.
먼저, 개발 의존성으로 설치합니다.
npm install --save-dev cross-env
위 dev:server 스크립트에서 cross-env NODE_ENV=development 부분이 바로 이 역할을 합니다. 이로써 백엔드 서버는 자신이 '개발 모드'임을 명확히 인지하게 됩니다.
3단계: http-proxy-middleware로 프록시 설정
이제 백엔드 서버가 개발 모드일 때, API가 아닌 요청을 Vite 개발 서버(http://localhost:5173)로 넘겨주도록 설정해야 합니다.
먼저, 개발 의존성으로 설치합니다.
npm install --save-dev http-proxy-middleware
server/vite.ts (또는 비슷한 역할을 하는 파일)의 내용을 다음과 같이 작성합니다.
// server/vite.ts
import express from 'express';
import path from 'path';
import fs from 'fs';
import { createProxyMiddleware } from 'http-proxy-middleware';
export const serveVite = (app: express.Application) => {
// NODE_ENV가 'development'일 때만 프록시를 사용
if (process.env.NODE_ENV === 'development') {
console.log('[Express] Development mode: Proxying to Vite server');
app.use(
'/',
createProxyMiddleware({
target: 'http://localhost:5173', // Vite 개발 서버 주소
changeOrigin: true,
ws: true, // 웹소켓 프록시
})
);
} else {
// 프로덕션 환경에서는 빌드된 정적 파일을 제공
const clientBuildPath = path.resolve(__dirname, '..', 'client', 'dist');
console.log(`[Express] Production mode: Serving static files from ${clientBuildPath}`);
if (!fs.existsSync(clientBuildPath)) {
console.error(`[Express] Error: Build directory not found at ${clientBuildPath}`);
process.exit(1);
}
app.use(express.static(clientBuildPath));
app.get('*', (req, res) => {
res.sendFile(path.join(clientBuildPath, 'index.html'));
});
}
};
4단계: devDependencies로 의존성 정리
concurrently, cross-env, http-proxy-middleware는 오직 개발 과정에서만 필요한 도구들입니다. 실제 프로덕션 환경에는 포함될 필요가 없죠. 따라서 이 패키지들은 --save-dev 옵션을 사용하여 devDependencies에 설치하는 것이 올바른 방법입니다. 이는 프로젝트를 깔끔하게 유지하고 최종 빌드 크기를 줄이는 데 도움이 됩니다.
결론
npm run dev의 실패는 단순히 명령어 하나의 문제가 아니었습니다. 현대적인 풀스택 개발 환경의 구조와 동작 방식을 정확히 이해하고 설정하는 과정에서 발생하는 문제였습니다.
핵심을 요약하자면 다음과 같습니다.
- 프로세스 분리: 프론트엔드와 백엔드는 별개의 프로세스이며, 개발 중에는 둘 다 실행되어야 합니다.
- 역할 분담: 개발 시 백엔드는 API 역할에 충실하고, UI 관련 요청은 프론트엔드 개발 서버에 위임(프록시)해야 합니다.
- 환경 인지: 서버가 현재 '개발' 모드인지 '프로덕션' 모드인지 명확히 알 수 있도록 **NODE_ENV**를 설정해주는 것이 매우 중요합니다
'개발' 카테고리의 다른 글
| React와 Node.js에서 GIS를 통한 구글 로그인 연동기 (2) | 2025.08.02 |
|---|---|
| Node.js 풀스택 개발시 로컬 개발 환경과 배포 환경의 괴리에서 발생하는 문제점에 대한 논의 (0) | 2025.08.02 |
| Node.js 앱 무중단 배포: GitHub Actions와 Docker로 EC2 자동화 파이프라인 구축하기 (5) | 2025.08.02 |
| GitHub Actions을 활용한 도커 이미지 빌드 및 EC2에 배포하는 과정 (2) | 2025.07.31 |
| Docker 컨테이너에서 Node.js 풀스택 앱 실행 시 흔한 문제 해결 가이드 (1) | 2025.07.30 |