FE
[Next.js] TypeScript 프로젝트인데 왜 JavaScript가 아직도 있냐고요?
“TS로 전환했으면 다 TS로 써야지, 왜 JS가 남아있지?”
실제로 팀에 들어가 보면 src/utils/legacy.js나 scripts/deploy.js 같은 파일들이 여전히 살아있는 경우가 많다.
누군가는 이렇게 생각할지도 모른다.
? “그건 그냥 리팩토링이 안 돼서 남아있는 거 아냐?”
그럴 수도 있다. 하지만 실무에서는 전략적으로 JS를 병행하는 사례가 꽤 많다.
이 글에서는 실제 현장에서 자주 발생하는 TS + JS 혼용의 5가지 이유를 정리한다.
1️⃣ 레거시 코드의 점진적 전환
1) 상황
오래된 프로젝트가 JavaScript로 되어 있었고, TypeScript 도입이 결정되었다.
하지만 기존 코드를 전부 TS로 바꾸는 데는 시간도 리소스도 부족했다.
2) 실제로 이렇게 했다
새 기능은 TS로 작성하고, 기존 JS 코드는 그대로 유지했다.
tsconfig.json에 allowJs: true 옵션을 주어 JS를 컴파일 대상에 포함시켰다.
JSDoc을 붙여 타입 추론을 최대한 활용하면서 안정적으로 관리했다.
3) 왜 의미 있나
? 모든 것을 한 번에 바꾸려는 시도는 리스크다.
실무에서는 타입 전환도 “단계적 변화”로 접근하는 게 현명하다.
2️⃣ 외부에서 넘어온 코드, 빠르게 붙이기
1) 상황
외주 업체, 파트너 팀, 혹은 다른 프로젝트에서 JS로 개발된 모듈을 받았다.
타입 정의가 없고, 당장 마감이 촉박한 상황이었다.
2) 실제로 이렇게 했다
utils/external-bridge.js 같은 형태로 그대로 소스 트리에 넣고 사용했다.
필요할 때만 d.ts로 minimal type만 지정하거나 as any 처리했다.
기능이 안정된 후 TypeScript로 리팩토링했다.
3) 왜 의미 있나
? 타입보다 “연동 성공”이 더 중요한 순간이 있다.
TS 도입은 개발자 중심의 도구이지만, 서비스는 사용자가 중심이다.
3️⃣ 프로토타이핑과 실험: 속도가 전부일 때
1) 상황
신기능이나 신규 API 연동을 테스트할 때, TS 설정 없이 빠르게 기능을 실험해야 했다.
2) 실제로 이렇게 했다
poc/, sandbox/ 같은 폴더에 JS로 작성하고 바로 실행했다.
테스트가 성공하면 실제 구현은 TS로 새로 작성했다.
3) 왜 의미 있나
⚡ “속도 우선”인 상황에서는 TS는 오히려 진입장벽이 될 수 있다.
실험은 빠르게, 운영은 견고하게. 이분법적 접근이 실무 생산성을 높인다.
4️⃣ 배포 스크립트, 빌드 도구는 TS가 필요 없다
1) 상황
빌드, 릴리즈, 환경 설정 등의 목적으로 단순한 CLI 스크립트를 작성해야 했다.
예: scripts/deploy.js, scripts/generate-sitemap.js
2) 실제로 이렇게 했다
NodeJS 기반으로 빠르게 JS 파일로 작성하고 실행했다.
ts-node 설치나 컴파일 설정 없이 바로 node 명령으로 사용했다.
3) 왜 의미 있나
?️ 이런 도구는 타입보다 “즉시 실행 가능성”이 중요하다.
TS로 짜야 한다는 원칙보다, 실행성이 더 실용적인 선택일 때가 많다.
5️⃣ 타입이 애매한 외부 라이브러리 대응
1) 상황
타입 정의가 없는 오래된 JS 라이브러리 또는 CDN 스크립트를 연동해야 했다.
TS에서 직접 쓰면 타입 에러가 났고, @types도 제공되지 않았다.
2) 실제로 이렇게 했다
Wrapper를 JS로 만들어 TS로 감쌌다.
필요한 최소 타입만 선언하고 사용했다.
또는 require 방식으로 임포트하고 타입 캐스팅으로 우회했다.
3) 왜 의미 있나
? 타입이 없는 코드를 억지로 TS로 감싸면 오히려 코드가 더 위험해진다.
타입이 불완전한 라이브러리는 JS로 한 발 물러나서 안전하게 통제하는 게 낫다.
✅ 결론: JS는 사라진 게 아니라 더 전략적으로 쓰이고 있다
TypeScript는 강력하다. 하지만 모든 상황에서 항상 TypeScript만 고집하는 건 실용적이지 않다.
JS와 TS의 공존은,
생산성과 안정성
현실과 이상
속도와 안전성
사이에서 균형을 맞추는 실무적인 선택이다.