
풀스택으로 프론트·백 둘 다 Node + TS 쓰다 보면 type-check, lint, build가 똑같이 있어서 “같이 돌리면 되겠지” 싶게 됩니다. 근데 이름만 같고 역할이 조금 다르고, 백엔드 쪽은 빌드 빼먹으면 500 터지는 일이 꽤 많습니다.
저도 한 번 소스에서 테이블 지웠는데 npm run start만 켜서 500 나고, 보니까 예전에 빌드해둔 dist/가 돌아가고 있더라고요. 그때부터 “백엔드 start는 빌드 이후”만 따로 기억하고 있습니다.
왜 프론트·백 둘 다 같은 검증을 쓰는 게 좋은지
Node + TypeScript 쓰면 package.json에 type-check, lint, build가 프론트·백 둘 다 비슷하게 들어갑니다. 우연이 아니라 같은 언어, 같은 생태계라서 그렇습니다.
TypeScript, ESLint/Prettier, Node, npm 스크립트—다 둘 다 쓰니까 도구랑 개념이 겹치는 거죠.
같은 검증을 둘 다 쓰는 이유는 단순합니다. 한 번 습관 들여두면 프론트 수정할 때나 백 수정할 때나 똑같은 순서(type-check → lint → build)로 점검하게 되고, 풀스택으로 왔다 갔다 해도 실수가 줄어듭니다.
그리고 요즘처럼 AI가 코드 많이 만들어주면, 그대로 믿기보다 타입·린트·빌드로 한 번 걸러주는 게 안전합니다.
비슷해 보이는데 뭐가 다른가?
이름은 같은데 build가 뭘 하느냐, 실제로 뭘 실행하느냐는 프론트랑 백이 다릅니다.
어떤 것을 검증하는 지를 아는 것은 대 AI 시대가 된 지금 꽤 중요한 포인트라고 생각합니다.
이걸 모르면 “로컬에선 되는데 배포/start 하니까 터진다”는 상황이 나옵니다.
type-check는 프론트나 백이나 목적이 같습니다. 타입 깨져서 런타임 버그 나거나 번들/실행이 터지는 걸 미리 막으려는 것 입니다.
lint도 마찬가지로 스타일·규칙 통일, 안 쓰는 변수·import 순서 같은 실수 방지용입니다. Prettier 붙이면 포맷까지 통일할 수 있고요.
여기까진 똑같습니다.
build에서부터 갈립니다.
프론트(React 등)는 TS/JS + CSS를 번들러(Vite, Webpack)가 묶어서 브라우저용 HTML/JS/CSS로 만듭니다. 배포하는 건 이 “번들 결과물”이죠.
여기서 번들은 쉽게 말하면 프론트 코드는 정말 수십가지 컴포넌트들이 있고, 라이브러리 의존성과 import 체인이 끊임없이 있는데, 이걸 돌릴때 네트워크 요청이
너무 많아진기에, 번들러가 필요한 코드만 묶어서 용량을 줄이고(minify) 최적화한 결과물만 만드는 것을 번들이라고 합니다.
백엔드(Express nest.js 등)는 프론트와 다르게 번들 결과물이 아닌 컴파일에 가깝습니다.
TypeScript를 tsc가 dist/ 같은 곳에 JavaScript로만 컴파일합니다.
Node는 TS를 직접 실행하지 않아서, 실제로 돌아가는 건 항상 이 빌드 결과물입니다.
이름은 둘 다 build인데, 프론트는 “번들링”, 백은 “TS → JS 컴파일”이라고 보면 됩니다.
start / dev도 다릅니다.
프론트는 start/dev가 개발 서버 띄우고 코드 바꾸면 자동으로 다시 번들해주고, 진짜 배포용은 build 한 번 더 돌려서 만듭니다.
백엔드는 start가 이미 빌드된 dist/ 안의 JS를 node dist/index.js로 실행합니다.
그래서 소스 수정했으면 그다음에 꼭 build를 해야 start가 새 코드로 돌아갑니다.
dev는 tsx 같은 걸로 소스(TS)를 바로 실행해서, 빌드 없이 수정해도 크게 상관 없습니다.
분명히 잘 짰는데 500 : AI 쓸 때 제일 빼먹기 쉬운 것
AI로 코드 많이 만들고 프론트·백 동시에 다루다 보면 백엔드 빌드를 자주 빼먹습니다.
소스에서는 테이블/모델 지웠는데 npm run start로 켜면 예전에 빌드해둔 dist/ 그대로 돌아가서 “Table 'xxx' doesn't exist” 500 나는 거, 버그가 아니라 돌아가는 코드랑 소스가 어긋난 것입니다.
프론트는 개발할 때 npm run dev만 켜두면 되고, build는 배포용 만들 때나 가끔 프로덕션 빌드 확인할 때만 돌리면 됩니다.
백엔드는 로컬 개발할 때는 npm run dev만 켜두면 됩니다. 소스 수정이 바로 반영되니까 빌드 안 해도 됩니다.
다시 한번 강조하지만 npm run start로 서버 켤 때만, 소스 바꿨으면 그다음에 꼭 npm run build 한 번 돌리고 나서 start 해야 합니다.
안 하면 예전에 빌드된 dist/가 그대로 돌아가니까 적절한 스키마를 맞춰주지 않아 당연히 500이 뜹니다.
node 기반 방식의 장점 (백엔드, 프론트엔드 호환)
풀스택 개발을 하면 대부분 node 기반의 라이브러리를 활용하기에 npm 검증 방식이 매우 유사하며 이에 따른 장점들이 뒤따릅니다.
프론트·백 둘 다 type-check → lint → build 순서를 맞춰두면 어디를 수정하든 같은 단계로 점검하게 됩니다.
대 AI시대에서의 AI가 만들어준 코드는 그대로 믿지 말고 타입·린트·빌드로 한 번씩 걸러주는 게 안전하고, 백엔드에서 “start 전에 build”만 습관 들이면 “로컬에선 되는데 서버만 500” 같은 상황을 많이 줄일 수 있습니다.
실제로 쓰는 npm 명령어
검증·빌드
명령어 하는 일 언제
| npm run type-check | tsc --noEmit으로 타입만 검사 (파일 안 만듦) | 배포/PR 전, 머지 전 |
| npm run lint | ESLint로 코드 규칙·스타일 검사 | 배포/PR 전, 커밋 전 |
| npm run lint -- --fix | 위와 같고, 자동 고칠 수 있는 건 고침 | lint 에러 났을 때 (Prettier/ESLint fix) |
| npm run build | 프론트: 번들 생성 / 백: TS → JS 컴파일 (dist/) | 배포 전, 백엔드 start 전에 꼭 |
| npm run start | 프론트: 보통 빌드 결과 서빙 / 백: node dist/index.js 실행 | 배포 환경, 로컬에서 “실제 실행” 확인할 때 |
| npm run dev | 프론트: 개발 서버 + HMR / 백: tsx로 src/ 직접 실행 | 로컬 개발할 때 (이거만 켜두면 됨) |
백엔드만 (DB)
명령어 하는 일 언제
| npm run db:migrate | npx sequelize-cli db:migrate — 마이그레이션 적용 | DB 스키마 바꿨을 때 (로컬·스테이징·배포 서버) |
| npm run db:migrate:undo | 마이그레이션 한 단계 되돌리기 | 실수로 migrate 했을 때 |
| npm run db:seed | 시드 데이터 넣기 (있는 경우) | 초기 데이터가 필요할 때 |
3줄 요약
- 로컬 개발: npm run dev 위주. DB 스키마 바뀔 때만 npm run db:migrate 돌리면 됩니다.
- 배포/PR 전: type-check → lint → build 순서로 돌립니다.
- 백엔드 start: 소스 수정했으면 반드시 그다음에 npm run build 한 번 돌린 뒤 npm run start 합니다.
'AI를 슬기롭게' 카테고리의 다른 글
| API 통신 2.0: TanStack Query 학개론 (0) | 2026.04.12 |
|---|---|
| AI에게 맡기기 전 인지해야 할 실수로 push한 커밋 대처법 (1) | 2026.02.01 |