← 전체 글로 돌아가기

TypeScript

TypeScript unknown 타입을 안전하게 처리하기

unknown 타입은 그냥 any와 다르다. 무조건 타입을 확인하고 나서야 사용할 수 있으므로, 이 특성을 제대로 활용하면 런타임 버그를 줄일 수 있다.

TypeScript의 unknown 타입은 any보다 더 안전하지만, 많은 개발자가 이 차이를 제대로 이해하지 못한다. unknown으로 받은 값은 무조건 타입을 확인하기 전까지 사용할 수 없다. 이게 사실 강점이다.

any vs unknown

any는 "뭐든 될 수 있고, 뭐든 쓸 수 있다"는 뜻이다. TypeScript가 타입 체크를 포기한 것이다.

unknown은 "뭐가 될 수 있지만, 먼저 확인해야 쓸 수 있다"는 뜻이다. TypeScript가 안전성을 강제한다.

let a: any = 1;
a.toUpperCase(); // 에러 없음 (잘못된 코드)

let b: unknown = 1;
b.toUpperCase(); // 컴파일 에러 (올바른 동작)

JSON.parse 결과 처리

가장 흔한 사용처는 JSON.parse 결과다. 파서는 문자열을 파싱하지만, 실제 형태는 알 수 없다.

const data: unknown = JSON.parse(jsonString);

// 먼저 타입을 확인해야 함
if (typeof data === 'object' && data !== null && 'id' in data) {
  console.log((data as any).id);
}

이렇게 명시적으로 확인하면, 런타임에 예상 밖의 데이터가 들어올 때 안전하게 처리할 수 있다.

타입 가드 함수로 안전하게

type guard를 사용하면 unknown 타입을 더 안전하게 처리할 수 있다.

function isString(value: unknown): value is string {
  return typeof value === 'string';
}

function isObject(value: unknown): value is Record<string, unknown> {
  return typeof value === 'object' && value !== null;
}

이 함수들을 사용하면 코드가 명확해지고, 타입 검사가 자동으로 이루어진다.

외부 API 응답 처리

외부 API에서 받은 응답도 unknown으로 처리하는 게 안전하다. API 명세가 바뀔 수 있기 때문이다.

async function fetchUser(id: string): Promise<unknown> {
  const response = await fetch(`/api/users/${id}`);
  return response.json(); // unknown 타입
}

호출한 곳에서는 반드시 타입을 확인한 후 사용해야 한다.

try-catch와 unknown

try-catch에서 catch되는 error도 unknown이다. 그래서 타입을 확인해야 한다.

try {
  // ...
} catch (error) {
  // error는 unknown
  if (error instanceof Error) {
    console.log(error.message);
  } else if (typeof error === 'string') {
    console.log(error);
  }
}

빌드 검증

unknown 타입 관련 오류는 빌드 단계에서 모두 잡혀야 한다.

npm run build

빌드에 경고가 없으면, 런타임에 unknown 타입으로 인한 버그가 거의 없다고 봐도 된다.

unknown으로 시작하는 습관

외부에서 받은 데이터나 사용자 입력은 모두 unknown으로 시작하고, 단계별로 타입을 좁혀가는 습관을 들이자. 이렇게 하면 타입 안정성이 크게 올라간다.