학습 공간 개념 · 판단 · 실험

Frontend Performance Lab

TypeScript

정적 타입으로 값의 관계 모델링하기

TypeScript는 값의 구조를 기준으로 코드를 실행하기 전에 오류를 찾아 주지만, 컴파일 후에는 타입이 사라지므로 외부 데이터의 런타임 검증을 함께 설계합니다.

이럴 때 필요해요

API 응답 경계와 컴포넌트 상태 모델을 명확히 표현해 잘못된 프로퍼티 접근과 불가능한 상태 조합을 미리 차단할 수 있습니다.

어떻게 동작하나요?

서버에서 받아온 데이터나 함수 인자에서 오타로 존재하지 않는 프로퍼티를 읽으려 할 때, 브라우저에서 직접 실행해 보기 전에 에디터가 바로 빨간 줄로 알려 주는 것이 TypeScript의 정적 타입 검사입니다. 다만 이렇게 적은 타입 표기들은 JavaScript로 변환(트랜스파일)될 때 모두 제거됩니다. 따라서 컴파일 타임에 타입을 잘 적어 두었더라도 실제 런타임에 네트워크로 들어오는 JSON 응답이나 사용자 입력값이 그 타입과 정말 일치하는지는 자동으로 보장되지 않으므로, 외부 경계에서는 값을 unknown으로 받아 실제 검사를 거치는 것이 안전합니다.

도메인 모델을 정의할 때 interface는 객체나 클래스의 구조를 선언하고 extends 상속이나 선언 병합을 할 때 적합하며, type 별칭은 객체뿐만 아니라 유니온(A | B), 튜플([number, number]) 등 모든 타입 조합에 이름을 붙일 수 있습니다. 특히 성공과 실패처럼 서로 다른 상태 갈래를 표현할 때는 아래 예제처럼 공통 리터럴 필드(ok: true | false)를 둔 판별 유니온(Discriminated Union)을 사용하면, 조건문 하나로 각 상태에 맞는 프로퍼티만 안전하게 꺼내 쓸 수 있습니다. 반대로 두 타입의 조건을 동시에 만족해야 할 때는 인터섹션(&)을 사용합니다.

TypeScript는 초기값과 문맥을 보고 타입을 똑똑하게 추론하므로 단순한 지역 변수에는 타입을 일일이 반복해 적지 않아도 됩니다. 대신 모듈 밖으로 공개하는 함수의 반환 타입이나 상태 모델에는 명시적 타입을 적어 의도를 고정하는 편이 좋습니다. 또한 TypeScript는 타입의 이름이 아니라 필요한 프로퍼티 구조가 맞는지로 호환성을 판단하는 구조적 타이핑(Structural Typing)을 따르며, 위치와 길이에 의미가 있는 배열은 튜플(Tuple)로 표현해 계약을 강화할 수 있습니다.

코드 예제

동작을 설명하는 읽기 전용 예제입니다.

example.ts
type Result = { ok: true; value: number } | { ok: false; error: string };
function display(result: Result): string {
  return result.ok ? String(result.value) : result.error;
}
const point: readonly [number, number] = [10, 20];
typescript · 읽기 전용5줄

공통 판별 필드인 ok를 확인해 유니온 타입을 안전하게 좁히고, 2차원 좌표의 길이와 위치 계약은 readonly 튜플로 표현합니다. 이때 readonly 역시 컴파일 타임 제약이며 런타임 Object.freeze를 수행하지는 않습니다.

자주 헷갈리는 포인트

  • as User 같은 타입 단언은 컴파일러의 경고만 끄는 표기일 뿐, 런타임에 데이터를 변환하거나 검증해 주지 않습니다.
  • 구조적 타이핑이라고 해서 어떤 객체든 무조건 호환되는 것은 아니며, 객체 리터럴 직접 대입 시에는 초과 프로퍼티 검사가 동작합니다.

내 말로 설명해 보기

외부 API 응답을 response as User로 단언하는 것과 실제 필드를 확인하는 타입 가드 함수를 거치는 것은 런타임 안전성에서 어떤 차이가 있을까요?

실전 시나리오에 적용해 보기

함께 읽으면 좋은 개념

관련 면접 질문 6개

TypeScript를 도입하면 어떤 오류를 줄이고 무엇은 검증하지 못하나요?

TypeScript는 JavaScript 코드에 정적 타입 검사를 추가해, 오타로 인한 잘못된 프로퍼티 접근이나 함수 인자 타입 불일치, 처리하지 않은 union 분기 같은 실수를 코드를 실행하기 전(컴파일 및 에디터 단계)에 미리 잡아 줍니다. 또한 IDE의 자동완성과 안전한 리팩터링을 크게 도와주어 협업 시 코드의 계약을 명확하게 만듭니다. 다만 TypeScript의 타입 표기는 트랜스파일 과정에서 모두 제거되므로, 실제 런타임에 네트워크로 들어오는 API 응답이나 사용자 입력값이 선언한 타입과 일치하는지는 별도의 런타임 검증 로직으로 확인해야 합니다.

꼬리질문과 참고자료 →

type 별칭과 interface는 어떤 기준으로 선택하나요?

interface는 객체나 클래스의 구조를 선언할 때 사용하며 extends 상속과 동일 이름의 선언 병합(Declaration Merging)을 지원합니다. 반면 type 별칭은 객체뿐 아니라 유니온(A | B), 튜플, 원시 타입 등 모든 타입 표현식에 이름을 붙일 수 있습니다. 실무에서는 유니온이나 복잡한 타입 조합에는 type을 쓰고, 외부 확장을 열어 두어야 하는 객체 계약에는 interface를 선택하며, 일반 객체 모델은 팀 컨벤션에 맞춰 일관되게 사용합니다.

꼬리질문과 참고자료 →

Union과 Intersection은 값의 가능성을 어떻게 표현하나요?

유니온 타입(A | B)은 값이 A 또는 B 중 어느 하나일 수 있다는 '선택지'를 표현하므로, 타입을 좁히기 전에는 모든 후보에 공통으로 존재하는 안전한 멤버에만 접근할 수 있습니다. 반대로 인터섹션 타입(A & B)은 값이 A의 조건과 B의 조건을 동시에 모두 만족해야 한다는 '결합'을 표현하므로 두 타입의 프로퍼티를 모두 가진 형태가 됩니다. 실무에서는 로딩·성공·실패처럼 상호 배타적인 상태를 모델링할 때 공통 리터럴 필드(예: status)를 둔 판별 유니온(Discriminated Union)을 가장 많이 활용합니다.

꼬리질문과 참고자료 →
나머지 질문 3개

공식 참고 자료

← TypeScript