자바스크립트는 더 이상 충분하지 않다
동적 타입 언어인 자바스크립트는 배우기 쉽고 유연하지만, 프로젝트의 규모가 커지고 참여하는 개발자가 늘어날수록 유연함은 곧 '독'이 됩니다. 런타임에 발생하는 원인을 알 수 없는 undefined is not a function 에러에 지치셨다면, 이제 TypeScript를 도입할 때입니다.
왜 TypeScript 인가?
TypeScript는 코드 작성 단계에서 버그를 사전에 잡아냅니다. IDE(VS Code 등)와의 강력한 연동을 통해 자동 완성 기능이 극대화되며, 함수가 어떤 파라미터를 받고 어떤 값을 반환하는지가 코드 자체로 문서화(Self-Documenting)됩니다. 이는 새로운 개발자가 프로젝트에 온보딩하는 시간을 획기적으로 단축시킵니다.
안전하고 점진적인 마이그레이션 전략
방대한 레거시 자바스크립트 프로젝트를 하루아침에 TypeScript로 변환하는 것은 불가능에 가깝습니다. 가장 추천하는 방식은 점진적 도입(Incremental Adoption)입니다.
- Step 1: 프로젝트에
tsconfig.json을 추가하고,allowJs: true옵션을 켭니다. 기존.js파일들은 그대로 유지되면서 공존할 수 있습니다. - Step 2: 비즈니스 로직과 무관한 공통 유틸리티 함수나 간단한 컴포넌트부터
.ts/.tsx로 확장자를 변경하며 타입을 정의합니다. - Step 3:
any타입 사용을 임시로 허용하되, 점차 인터페이스(Interface)와 타입(Type Alias)을 구체화하며strict모드로 나아갑니다.
실무에서 부딪히는 흔한 오해
"타입 정의하느라 개발 시간이 두 배로 든다"는 것은 초기에 흔히 겪는 착각입니다. 초기 세팅과 타입 정의에 들어가는 시간은, 향후 디버깅과 유지보수에 소요될 막대한 시간을 선불로 지불하는 것과 같습니다. 장기적인 관점에서 TypeScript는 무조건적으로 프로젝트의 리드 타임을 줄여줍니다.
마치며
이제 프론트엔드/백엔드 생태계에서 TypeScript는 선택이 아닌 기본기가 되었습니다. 완벽하게 이해하고 도입하려 하기보다는, 당장 any로 도배를 하더라도 우선 확장자를 바꾸고 컴파일러의 도움을 받아보는 것부터 시작해 보시기 바랍니다.
팀에서 반발을 줄이는 도입 방식
TypeScript 전환이 실패하는 가장 흔한 이유는 모든 파일을 한 번에 엄격하게 바꾸려 하기 때문입니다. 처음에는 런타임 장애가 잦은 API 응답, 결제/권한 로직, 공통 유틸 함수처럼 안정성이 중요한 영역부터 타입을 붙이는 방식이 현실적입니다.
점진적 전환 순서
- 공통 타입과 API 응답 타입을 먼저 정의해 중복 인터페이스를 줄입니다.
any사용을 금지하기보다 사용 위치를 기록하고 점진적으로 줄입니다.- PR 리뷰에서 타입이 실제 버그를 막은 사례를 공유해 팀의 학습 비용을 낮춥니다.
좋은 타입은 코드의 의도를 설명합니다. 타입 정의가 설명보다 더 복잡해졌다면, 도메인 모델이나 함수 책임이 지나치게 커졌다는 신호일 수 있습니다.
Frontend Note 실무 노트
TypeScript 도입은 팀 문화의 문제이기도 합니다. 처음부터 strict 모드를 켜고 모든 파일을 바꾸면 개발 속도가 멈추고, 팀원은 타입을 방해물로 느끼기 쉽습니다. 더 나은 방식은 장애가 자주 나는 경계부터 타입을 붙이는 것입니다. API 응답, 권한 분기, 결제 금액 계산, 공통 유틸 함수처럼 잘못되면 비용이 큰 곳부터 시작합니다.
마이그레이션 첫 주에는 타입 완성도가 아니라 타입 지도를 만드는 것이 목표입니다. 어떤 API가 어떤 데이터를 반환하는지, null이 가능한 필드가 무엇인지, 화면마다 중복된 타입이 얼마나 있는지 확인합니다. 이때 any를 전면 금지하기보다 사용 위치를 기록하고 줄이는 방식을 추천합니다.
type ApiResult<T> =
| { ok: true; data: T }
| { ok: false; message: string };
type UserRole = "guest" | "member" | "admin";
좋은 타입은 코드 리뷰를 쉽게 만듭니다. 함수 이름만 보고 알기 어려운 입력과 출력이 타입으로 드러나면 리뷰어는 구현보다 의도에 집중할 수 있습니다. 반대로 타입 정의가 너무 복잡하다면 함수 책임이 과한 신호일 수 있습니다. 이 경우 타입을 더 억지로 쓰기보다 도메인 모델과 함수 경계를 먼저 나누는 편이 낫습니다.
도입 체크리스트
- API 응답 타입을 먼저 만든다.
- 공통 유틸 함수부터 ts로 바꾼다.
- any 사용 위치를 이슈로 기록한다.
- PR에서 타입이 막은 버그 사례를 공유한다.
적용 순서 예시
기존 JavaScript 프로젝트를 TypeScript로 바꿀 때는 파일 확장자를 대량으로 바꾸는 작업부터 시작하지 않습니다. 먼저 런타임 장애가 반복되는 위치를 찾고, 그 위치의 입력과 출력을 문서화합니다. 예를 들어 사용자 권한, 결제 상태, API 응답 코드처럼 분기 조건이 많은 영역은 타입을 붙였을 때 효과가 큽니다.
첫 번째 스프린트에서는 타입 정의 파일을 만드는 정도로 충분합니다. API 응답 모양을 unknown으로 받고 화면에서 바로 쓰는 코드를 줄이고, 변환 함수를 하나 둡니다. 변환 함수 안에서 null 가능성과 기본값을 처리하면 화면 컴포넌트는 훨씬 단순해집니다.
type RawUser = {
id?: string;
name?: string | null;
role?: "guest" | "member" | "admin";
};
type User = {
id: string;
name: string;
role: "guest" | "member" | "admin";
};
function normalizeUser(raw: RawUser): User {
return {
id: raw.id ?? "anonymous",
name: raw.name ?? "이름 없음",
role: raw.role ?? "guest"
};
}
이 방식은 타입을 단순히 컴파일러 만족용으로 쓰지 않게 해줍니다. 외부 데이터의 불확실성을 한곳에서 정리하고, 내부 코드에서는 안정된 타입만 다루게 만드는 것이 TypeScript 도입의 실제 장점입니다.
리뷰 기준
- 타입이 실제 데이터 흐름을 설명하는지 확인합니다.
- 컴포넌트마다 같은 타입을 반복 선언하지 않습니다.
- 임시
any는 주석과 이슈 번호를 남깁니다. - 타입 오류를 무시하기 위해
as를 남발하지 않습니다.
참고 자료와 업데이트 기준
Frontend Note의 글은 실무 적용 관점에서 작성하며, 관련 기술의 공식 문서와 브라우저 지원 현황이 바뀌면 내용을 다시 점검합니다. 특히 프레임워크 버전, API 정책, 성능 측정 방식은 시간이 지나며 달라질 수 있으므로 적용 전에는 최신 문서를 함께 확인하는 것을 권장합니다.
- MDN Web Docs에서 웹 표준과 브라우저 동작을 확인합니다.
- web.dev에서 성능, 접근성, 사용자 경험 기준을 확인합니다.
- React 공식 문서에서 React 관련 API와 권장 패턴을 확인합니다.
마이그레이션을 오래 유지하는 방식
TypeScript 전환은 초기에 열심히 설정해도 팀의 일상 코드 리뷰에 들어오지 않으면 금방 멈춥니다. 가장 효과적인 방식은 한 번에 strict 모드를 켜는 것이 아니라 변경이 잦은 폴더부터 타입 기준을 높이는 것입니다. 예를 들어 API 응답을 다루는 services, 공통 컴포넌트가 있는 components, 폼 검증을 담당하는 forms처럼 오류가 사용자 경험에 바로 연결되는 영역을 먼저 정합니다. 이때 기존 JavaScript 파일을 모두 바꾸려 하지 말고 새로 작성하거나 크게 수정하는 파일부터 ts와 tsx로 옮기면 부담이 줄어듭니다.
리뷰 기준도 함께 정해야 합니다. any를 완전히 금지하기보다 왜 필요한지 주석으로 남기고, 외부 응답처럼 불확실한 값은 unknown에서 좁혀 가도록 안내합니다. 타입 선언 파일은 임시 해결책으로만 두고, 자주 쓰는 응답 구조는 실제 런타임 검증과 함께 관리하는 것이 안전합니다. 마이그레이션 진행률은 파일 수보다 위험한 암시적 타입이 줄었는지, 컴포넌트 props가 문서 없이도 이해되는지, 리팩터링 때 IDE가 잡아주는 오류가 늘었는지로 판단하는 편이 실무에 더 가깝습니다.
실무 적용 전 최종 점검
이 글의 내용을 프로젝트에 적용하기 전에는 현재 코드의 목적과 사용자 흐름을 먼저 확인해야 합니다. 같은 패턴이라도 관리자 화면, 공개 랜딩 화면, 입력이 많은 폼 화면에서는 우선순위가 다릅니다. 관리자 화면은 반복 작업 속도와 오류 복구가 중요하고, 공개 화면은 초기 로딩과 접근성이 중요하며, 폼 화면은 검증 메시지와 상태 보존이 중요합니다. 따라서 예제 코드를 그대로 붙이기보다 어떤 파일에서 책임을 나눌지, 어떤 값이 외부 입력인지, 실패했을 때 사용자가 무엇을 보게 되는지를 함께 검토하는 것이 좋습니다.
작업 후에는 변경 전후를 비교할 수 있는 작은 기록을 남깁니다. 수정한 컴포넌트, 확인한 브라우저, 실패 케이스, 되돌리는 방법을 적어 두면 다음 배포에서 같은 문제를 빨리 찾을 수 있습니다. 특히 React와 TypeScript 코드는 타입 오류가 사라져도 런타임 흐름이 틀릴 수 있고, CSS 수정은 특정 화면 폭에서만 깨질 수 있습니다. 그래서 로컬 확인, 빌드 확인, 실제 화면 확인을 분리해 진행하는 습관이 필요합니다. Frontend Note의 예시는 이 과정을 돕기 위한 출발점이며, 팀의 코드 스타일과 배포 방식에 맞게 작게 조정해서 사용하는 편이 안전합니다.