iBetter Books
수정

같은 코드, 다른 안전성

JavaScript와 TypeScript로 동일한 기능을 작성해보면 차이가 명확하게 드러납니다. 간단한 "사용자 목록에서 이메일 찾기" 함수를 비교해보겠습니다.

JavaScript 버전

function findEmail(users, name) {  const user = users.find(u => u.name === name);  return user.email;}const users = [  { name: "김민수", email: "[email protected]" },  { name: "이영희", email: "[email protected]" },];console.log(findEmail(users, "박철수")); // TypeError: Cannot read properties of undefined

"박철수"는 목록에 없으므로 findundefined를 반환합니다. undefined.email에 접근하는 순간 런타임 에러가 발생합니다. 이 에러는 코드를 실행해야만 알 수 있습니다.

TypeScript 버전

interface User {  name: string;  email: string;}function findEmail(users: User[], name: string): string | undefined {  const user = users.find(u => u.name === name);  return user?.email;}const users: User[] = [  { name: "김민수", email: "[email protected]" },  { name: "이영희", email: "[email protected]" },];console.log(findEmail(users, "박철수")); // undefined (에러 없음)

TypeScript 버전에서는 두 가지가 달라졌습니다.

  1. users: User[] — 매개변수가 User 배열임을 명시했습니다
  2. user?.emailuserundefined일 수 있다는 것을 TypeScript가 알려줘서, 안전한 접근 연산자(?.)를 사용했습니다

TypeScript는 find의 반환값이 User | undefined라는 것을 알고 있습니다. 만약 user.email이라고 쓰면 에디터에서 바로 경고해줍니다.

VS Code 자동완성의 차이

TypeScript의 가장 체감되는 장점은 에디터의 자동완성입니다.

JavaScript에서의 자동완성

const user = { name: "김민수", age: 25, email: "[email protected]" };user. // ← 여기서 점을 찍으면?

JavaScript에서는 에디터가 user의 속성을 추측합니다. 때로는 정확하지만, 함수의 매개변수나 API 응답처럼 복잡한 경우에는 자동완성이 제대로 작동하지 않습니다.

TypeScript에서의 자동완성

interface User {  name: string;  age: number;  email: string;}const user: User = { name: "김민수", age: 25, email: "[email protected]" };user. // ← 여기서 점을 찍으면 name, age, email이 정확하게 나옵니다

TypeScript에서는 User 인터페이스에 정의된 속성이 100% 정확하게 자동완성됩니다. 오타를 칠 일이 없어지고, 어떤 속성이 있는지 문서를 찾아볼 필요도 없습니다.

컴파일 시점 vs 런타임

JavaScript와 TypeScript의 에러 발견 시점을 비교하면 다음과 같습니다.

JavaScript의 에러 발견 과정

코드 작성 → 저장 → 실행 → 에러 발생! → 원인 추적 → 수정
                         ↑
                    여기서야 알게 됩니다

TypeScript의 에러 발견 과정

코드 작성 → 에러 표시! → 즉시 수정 → 저장 → 실행 → 정상 동작
              ↑
         여기서 바로 알려줍니다

TypeScript는 코드를 작성하는 순간 에러를 표시합니다. 저장하기도 전에, 실행하기도 전에, 문제가 있는 곳에 빨간 밑줄이 그어집니다.

TypeScript는 JavaScript의 확장입니다

한 가지 중요한 사실이 있습니다. 모든 JavaScript 코드는 유효한 TypeScript 코드입니다.

// 이 코드는 JavaScript이면서 동시에 TypeScript입니다const greeting = "안녕하세요";console.log(greeting.toUpperCase());

TypeScript는 JavaScript를 대체하는 것이 아니라, JavaScript 위에 타입 시스템을 얹은 것입니다. 기존 JavaScript 코드를 그대로 사용할 수 있고, 필요한 부분에만 타입을 추가할 수 있습니다.

TypeScript 코드는 최종적으로 JavaScript로 변환(컴파일)되어 실행됩니다.

TypeScript (.ts) → 컴파일(tsc) → JavaScript (.js) → 실행(Node.js/브라우저)

타입 정보는 컴파일 과정에서 모두 제거됩니다. 브라우저나 Node.js가 실행하는 것은 순수한 JavaScript입니다. 타입은 개발 시점의 안전장치이며, 실행 시점에는 성능에 아무런 영향을 주지 않습니다.

핵심 차이 정리

항목 JavaScript TypeScript
타입 지정 없음 (동적 타입) 있음 (정적 타입)
에러 발견 시점 실행 시 (런타임) 작성 시 (컴파일 타임)
자동완성 제한적 정확함
학습 곡선 낮음 약간 높음
실행 환경 브라우저/Node.js 직접 실행 컴파일 후 JavaScript로 실행
파일 확장자 .js .ts
기존 JS 코드 호환 100% 호환

언제 TypeScript를 선택할까

TypeScript가 특히 빛을 발하는 상황이 있습니다.

  • 팀 프로젝트: 다른 사람이 작성한 함수의 매개변수 타입을 바로 알 수 있습니다
  • 대규모 프로젝트: 파일이 많아질수록 타입 시스템의 가치가 커집니다
  • API 연동: 서버에서 받아오는 데이터의 구조를 타입으로 정의하면 실수가 줄어듭니다
  • 리팩터링: 변수나 함수의 이름을 바꿀 때, 영향받는 모든 곳을 자동으로 찾아줍니다

반면 간단한 스크립트나 일회성 코드에서는 JavaScript만으로 충분할 수 있습니다. 중요한 것은 상황에 맞는 선택을 하는 것입니다.

다음 챕터에서는 TypeScript를 둘러싼 생태계를 한눈에 살펴보겠습니다.