본문으로 건너뛰기
개발 머꼬
개발 노트JavaScript
hohyeon.dev24

TypeScript any를 unknown으로 바꿨더니 컴파일 에러가 쏟아진 이유

  • #Engineering Note
  • #Type Safety
  • #TypeScript

문제 발생

외부 API 응답 타입을 몰라서 any로 처리하던 코드를 안전하게 만들려고 unknown으로 바꿨더니, 이전에는 문제없이 컴파일되던 곳에서 갑자기 여러 개의 타입 에러가 발생했습니다.

function handle(data: unknown) {
  console.log(data.message); // 에러: 'data' is of type 'unknown'
}

원인 분석

anyunknown은 둘 다 "어떤 타입이든 될 수 있다"는 점은 같지만, TypeScript가 그 값을 다룰 때 취하는 태도가 정반대입니다.

  • any: 사실상 타입 검사를 그 값에 대해서만 완전히 꺼버립니다. any 타입 값은 어떤 속성에 접근하든, 어떤 함수에 넘기든 컴파일러가 아무 검사도 하지 않습니다. 그만큼 런타임 에러를 컴파일 타임에 못 잡아냅니다.
  • unknown: "타입을 모른다"는 사실을 컴파일러가 계속 기억합니다. unknown 타입 값은 타입을 좁히기(narrowing) 전까지는 어떤 속성 접근이나 연산도 허용하지 않습니다. 그래서 data.message처럼 바로 접근하면 에러가 납니다 — 이건 버그가 아니라 unknown이 정확히 의도한 동작입니다.

unknown으로 바꾼 뒤 에러가 난 곳들은, 원래 any였을 때 타입 안전성 없이 그냥 통과되고 있던 잠재적 위험 지점이 이제야 드러난 것입니다.

해결 방안

  1. unknown 값을 실제로 쓰려면 먼저 타입을 좁혀야 합니다 — typeof, instanceof, 커스텀 타입 가드 등을 씁니다.
function handle(data: unknown) {
  if (typeof data === "object" && data !== null && "message" in data) {
    console.log((data as { message: string }).message);
  }
}
  1. 외부 API 응답처럼 형태가 확실하지 않은 값은 Zod 같은 런타임 검증 라이브러리로 파싱하면서 타입을 좁히는 것이 가장 안전합니다 — 컴파일 타임 타입과 런타임 실제 데이터가 어긋나는 것까지 막아줍니다.
const schema = z.object({ message: z.string() });
function handle(data: unknown) {
  const parsed = schema.parse(data); // 여기서 타입이 좁혀지고 런타임 검증도 됨
  console.log(parsed.message);
}
  1. any는 정말 불가피한 경우(타입 정의가 없는 오래된 라이브러리 등)로 최소화하고, "타입을 아직 모른다"는 상태를 표현하고 싶을 때는 기본적으로 unknown을 씁니다.
  2. 기존 코드베이스에 any가 많다면, 한 번에 전부 바꾸기보다 새로 작성하는 코드부터 unknown을 기본으로 쓰고 점진적으로 옮기는 것이 현실적입니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.