JavaScript로 규모가 커지는 프로젝트를 다루다 보면 타입 오류로 인한 런타임 에러를 자주 경험하게 됩니다. TypeScript는 JavaScript에 정적 타입을 더한 언어로, 코드 작성 단계에서 오류를 미리 잡아주고 IDE 자동 완성을 강력하게 지원합니다.

왜 TypeScript인가?

TypeScript를 사용하면 얻을 수 있는 이점은 다음과 같습니다.

  • 컴파일 타임 오류 검출 — 실행 전에 타입 오류를 잡습니다.
  • IDE 자동완성 — 객체의 속성과 메서드가 자동으로 제안됩니다.
  • 코드 문서화 — 타입 자체가 코드의 문서가 됩니다.
  • 리팩터링 안전성 — 변수명이나 구조를 바꿀 때 영향 범위를 정확히 알 수 있습니다.

기본 타입

// 기본 타입 선언
let name: string = '홍길동';
let age: number = 30;
let isActive: boolean = true;

// 배열
let fruits: string[] = ['사과', '바나나'];
let scores: Array<number> = [100, 95, 88];

// 튜플 — 길이와 각 요소 타입이 고정된 배열
let point: [number, number] = [10, 20];

// any — 타입 검사를 건너뜀 (남용 금지)
let anything: any = '문자열이었다가';
anything = 42; // 숫자로 바뀌어도 오류 없음

// unknown — any보다 안전한 대안
let input: unknown = getUserInput();
if (typeof input === 'string') {
  console.log(input.toUpperCase()); // 타입 검사 후 사용
}

인터페이스와 타입 별칭

객체의 구조를 정의할 때 interfacetype을 사용합니다.

// Interface
interface User {
  id: number;
  name: string;
  email: string;
  age?: number; // 선택적 속성 (Optional)
  readonly createdAt: Date; // 읽기 전용
}

// Type alias
type Point = {
  x: number;
  y: number;
};

// 유니온 타입
type Status = 'pending' | 'active' | 'inactive';

// 함수 타입
function greet(user: User): string {
  return `안녕하세요, ${user.name}님!`;
}

// 화살표 함수
const add = (a: number, b: number): number => a + b;

제네릭(Generics)

제네릭을 사용하면 타입을 파라미터처럼 사용할 수 있어, 다양한 타입에 재사용 가능한 코드를 작성할 수 있습니다.

// 제네릭 함수
function identity<T>(arg: T): T {
  return arg;
}

identity<string>('hello'); // 'hello'
identity<number>(42);     // 42

// 제네릭 인터페이스
interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
}

// 사용 예시
const userResponse: ApiResponse<User> = {
  data: { id: 1, name: '홍길동', email: 'hong@example.com', createdAt: new Date() },
  status: 200,
  message: 'success'
};

유틸리티 타입

TypeScript는 기존 타입을 변환하는 유틸리티 타입을 내장합니다.

interface User {
  id: number;
  name: string;
  email: string;
  password: string;
}

// Partial — 모든 속성을 선택적으로
type UserUpdate = Partial<User>;

// Pick — 특정 속성만 선택
type UserPublic = Pick<User, 'id' | 'name' | 'email'>;

// Omit — 특정 속성만 제외
type UserWithoutPassword = Omit<User, 'password'>;

// Readonly — 모든 속성을 읽기 전용으로
type ReadonlyUser = Readonly<User>;

// Record — 키-값 맵 타입
type UserMap = Record<string, User>;
핵심 정리
TypeScript는 JavaScript의 상위집합으로, 기존 JS 코드를 그대로 사용하면서 점진적으로 타입을 추가할 수 있습니다. any 사용을 최소화하고, 인터페이스와 제네릭을 적극 활용하면 유지보수하기 쉬운 코드를 작성할 수 있습니다.