TypeScript 프로젝트를 처음 구성할 때는 보통 tsc로 끝납니다. 컴파일도 되고, 타입 체크도 되고, 설정도 단순합니다.
그런데 프론트엔드나 Next.js, React 기반 프로젝트로 넘어가거나, 모노레포에서 웹과 서버를 같이 관리하기 시작하면 Babel이 빌드 파이프라인에 섞이는 순간이 생깁니다.
이때 흔히 생기는 혼란은 하나입니다. “둘 다 TypeScript를 처리하는 것 같은데, 왜 둘 다 필요한가?” 실제로는 역할이 꽤 다릅니다.
TypeScript 컴파일러(tsc)가 하는 일
tsc는 크게 두 가지 일을 합니다.
- 타입 체크
- TypeScript 문법을 JavaScript로 변환
실무에서 중요한 건, 타입 체크가 tsc의 핵심 역할이라는 점입니다. 트랜스파일(변환)만 놓고 보면 tsc 말고도 선택지가 많습니다.
tsc -p tsconfig.json
서버 프로젝트처럼 단순한 Node 런타임을 타깃으로 할 때는 tsc만으로도 충분한 경우가 많습니다.
Babel이 하는 일
Babel은 기본적으로 “변환기”입니다. 코드를 분석해서 다른 형태의 JavaScript로 바꿉니다.
TypeScript 문법을 이해하게 만들 수도 있지만, Babel은 타입 체크를 하지 않습니다. TypeScript의 타입 정보는 컴파일 단계에서 전부 제거합니다.
Babel이 주로 빛나는 부분은 다음입니다.
- 다양한 문법 변환(플러그인 생태계)
- 폴리필/타깃 브라우저 전략과 결합
- JS/TS 혼합 코드베이스에서의 일관된 변환
“Babel로 TypeScript 컴파일”의 의미
Babel로 TypeScript를 처리한다는 말은, 대부분 “타입을 제거하고 JS로 변환한다”는 뜻입니다.
그래서 Babel을 쓰는 프로젝트는 보통 이렇게 운영합니다.
- Babel: 트랜스파일
- tsc: 타입 체크만 수행 (noEmit)
이 역할 분리를 모르면, “빌드가 통과했는데 타입 오류가 남아 있는 상태”가 생기고, 그 오류를 놓치기 시작합니다.
통합 빌드에서 가장 많이 쓰는 패턴
실무에서 많이 쓰는 형태는 다음 두 가지입니다.
1) Babel로 빌드, tsc로 타입 체크
프론트엔드 빌드에서는 이 구조가 자연스럽습니다. 예를 들어 webpack/rollup/vite 같은 번들러와 Babel이 붙습니다.
// package.json scripts 예시
{
"scripts": {
"build": "babel src --extensions .ts,.tsx --out-dir dist",
"typecheck": "tsc --noEmit"
}
}
이 방식은 변환 파이프라인이 유연합니다. 대신 타입 체크는 반드시 별도로 강제해야 합니다. CI에서 typecheck를 빠뜨리면 품질이 바로 무너집니다.
2) tsc로 타입 체크 + 트랜스파일, Babel은 선택적
Node 백엔드에서 흔한 구조입니다. 빌드는 단순하고, 타입 체크와 변환이 한 번에 끝납니다.
tsc -p tsconfig.json
이 구조는 안정적이지만, 특정 문법 변환이나 플러그인 기반 최적화가 필요해지면 Babel이 다시 등장합니다.
왜 “둘 다” 쓰는 프로젝트가 많은가
둘 다 쓰는 이유는 대개 한 가지입니다. Babel 생태계를 쓰고 싶지만, 타입 체크는 포기할 수 없기 때문입니다.
예를 들어 다음 상황에서는 Babel이 자연스럽게 들어옵니다.
- 레거시 JS 코드와 TS 코드가 섞여 있는 경우
- 특정 Babel 플러그인(코드 변환/최적화)이 필요한 경우
- 브라우저 타깃을 세밀하게 관리해야 하는 경우
반대로 서버 단일 런타임만 목표라면, tsc만으로도 충분한 경우가 많습니다.
통합 빌드에서 자주 발생하는 실수
운영에서 자주 보는 문제는 다음과 같습니다.
- Babel 빌드만 돌리고 타입 체크를 생략한다
- 빌드 성공을 타입 안전성의 보장으로 착각한다
- tsconfig와 Babel 설정의 타깃이 어긋난다
특히 tsconfig의 module/target과 Babel의 preset-env 타깃이 다르면, 같은 코드가 서로 다른 JS로 변환됩니다. 이 경우 디버깅 난이도가 올라갑니다.
모노레포에서의 현실적인 구성
모노레포에서는 패키지마다 요구가 달라집니다. 웹 패키지는 Babel/번들러 중심, 서버 패키지는 tsc 중심으로 운영되는 경우가 많습니다.
이때 기준이 없으면,
- 어떤 패키지는 Babel + tsc
- 어떤 패키지는 tsc only
- 어떤 패키지는 swc/esbuild
같은 저장소 안에서 빌드 방식이 제각각이 됩니다. 이 자체가 문제는 아니지만, 타입 체크를 어디에서 강제하는지(로컬, CI, pre-commit) 이 부분이 흐려지면 나중에 고생하게 됩니다.
Babel과 TypeScript는 경쟁 관계라기보다, 담당하는 역할이 다른 도구입니다. TypeScript는 타입 체크를 중심으로 생각하는 편이 안정적이고, Babel은 변환 파이프라인을 유연하게 만드는 도구로 보는 편이 맞습니다.
둘을 같이 쓰는 경우에는 “빌드 성공”과 “타입 안전”을 분리해서 관리해야 합니다. 타입 체크를 별도 단계로 강제하지 않으면, 시간이 지날수록 코드베이스는 조용히 무너집니다.
결국 선택은 팀과 환경에 따라 달라질 수 있습니다. 다만 어떤 조합을 쓰더라도, 타입 체크를 어디에서 책임지는지와 변환 타깃이 일관된지는 초반에 정리해두는 편이 좋습니다.
'개발 > Typescript' 카테고리의 다른 글
| [TYPESCRIPT] Jest + TypeScript 테스트 환경을 운영 기준으로 잡는 구성 (0) | 2026.02.26 |
|---|---|
| [TYPESCRIPT] ESLint + Prettier + TypeScript 설정을 운영 기준으로 잡는 방법 (0) | 2026.02.25 |
| [TYPESCRIPT] ts-node-dev, swc, esbuild를 같이 써보고 나서 보이는 차이 (0) | 2026.02.23 |
| [TYPESCRIPT] 모노레포 환경에서 TypeScript를 관리하다 보면 보이는 경계들 (0) | 2026.02.22 |
| [TYPESCRIPT] 모듈 보강을 쓰게 되는 순간과 타입 경계가 흔들리는 지점들 (0) | 2026.02.21 |