본문 바로가기
개발

[TS] any 대신 unknown을 써야 하는 이유와 타입 가드 활용법

by 돌미나리는야생미나리 2026. 4. 27.

타입스크립트(TypeScript)를 처음 접하면 가장 먼저 배우는 단어가 있습니다. 바로 any입니다. 어떤 타입이든 허용한다는 마법 같은 단어지만, 아이러니하게도 any를 남발하는 순간 우리는 타입스크립트를 사용하는 가장 큰 이유인 '타입 안전성'을 포기하게 됩니다.

실무에서 외부 API 응답이나 동적 데이터를 다룰 때, 우리는 데이터의 정확한 형태를 알 수 없는 상황에 직면합니다. 이때 습관적으로 any를 쓰기보다 더 안전하고 견고한 unknown 타입을 선택해야 합니다. 오늘은 왜 unknown이 any보다 우월한지, 그리고 unknown 데이터를 안전하게 요리하는 타입 가드(Type Guard) 기법을 상세히 알아보겠습니다.


1. any: 모든 안전장치를 해제하는 위험한 열쇠

any는 타입스크립트의 타입 검사기(Type Checker)를 완전히 무력화합니다.

TypeScript
 
let value: any = 10;
value.toUpperCase(); // 컴파일 타임에 에러가 발생하지 않음 -> 런타임에 에러 발생!

위 코드에서 value는 숫자지만 any 타입이기 때문에 문자열 메서드인 toUpperCase 호출을 허용합니다. 결국 이 오류는 사용자가 앱을 실행하는 도중 "Uncaught TypeError"라는 이름으로 터지게 됩니다. 사실상 자바스크립트를 쓰는 것과 다를 바 없는 상태가 되는 것이죠.


2. unknown: "무엇인지 모르니 확인하고 쓰세요"

unknown 타입은 any와 마찬가지로 모든 값을 할당받을 수 있습니다. 하지만 결정적인 차이점은 '사용할 때' 발생합니다.

2.1 unknown의 제약 사항

unknown 타입으로 정의된 변수는 타입을 정적으로 확정하기 전까지는 어떠한 동작도 수행할 수 없습니다.

TypeScript
 
let value: unknown = 10;

// 에러 발생: 'value'의 형식이 'unknown'입니다.
value.toUpperCase(); 

// 타입을 확인해야만 사용 가능
if (typeof value === "string") {
  console.log(value.toUpperCase()); // 이제 안전하게 사용 가능!
}

이처럼 unknown은 개발자에게 "이 데이터의 타입을 먼저 검증하라"라고 강제합니다. 컴파일러가 우리 대신 안전장치를 한 번 더 확인해 주는 셈입니다.


3. unknown을 안전하게 다루는 법: 타입 가드(Type Guard)

unknown 데이터의 정체를 밝히기 위해 사용하는 기법이 바로 타입 가드입니다.

3.1 typeof 연산자 (기본 타입 검사)

숫자, 문자열, 불리언 등 자바스크립트의 기본 타입을 확인할 때 사용합니다.

3.2 instanceof 연산자 (클래스/객체 검사)

특정 클래스의 인스턴스인지 확인할 때 유용합니다.

TypeScript
 
function processDate(input: unknown) {
  if (input instanceof Date) {
    console.log(input.toISOString()); // Date 객체임이 보장됨
  }
}

3.3 사용자 정의 타입 가드 (Type Predicates)

가장 강력하고 실무에서 많이 쓰이는 방식입니다. is 키워드를 사용하여 함수가 특정 타입임을 명시적으로 알려줍니다.

TypeScript
 
interface User {
  name: string;
  age: number;
}

// User 타입인지 확인하는 가드 함수
function isUser(target: any): target is User {
  return (
    typeof target === "object" &&
    target !== null &&
    "name" in target &&
    "age" in target
  );
}

function greet(input: unknown) {
  if (isUser(input)) {
    console.log(`안녕하세요, ${input.name}님!`); // 여기서 input은 자동으로 User 타입이 됨
  }
}

4. 실무 예시: API 에러 처리

API 통신 중 발생하는 에러(catch 문의 error 객체)는 기본적으로 unknown 타입(TS 4.4 이상)입니다. 이때 unknown과 타입 가드를 활용하면 매우 견고한 에러 처리가 가능합니다.

TypeScript
 
try {
  await fetchData();
} catch (error: unknown) {
  if (error instanceof AxiosError) {
    // axios 에러인 경우 처리
    console.error(error.response?.data.message);
  } else if (error instanceof Error) {
    // 일반 에러인 경우 처리
    console.error(error.message);
  } else {
    // 그 외의 경우
    console.error("알 수 없는 에러 발생", error);
  }
}

이전처럼 (error as any). message라고 썼을 때 발생할 수 있는 런타임 폭탄을 원천 차단할 수 있습니다.


5. any는 절대 쓰면 안 되나요?

현실적으로 any가 필요한 순간도 있습니다.

  • 마이그레이션 중인 프로젝트에서 타입을 일일이 지정하기 벅찰 때.
  • 타입 정의가 너무 복잡하여 생산성을 심각하게 해칠 때.
  • 제네릭 등으로 도저히 해결 안 되는 복잡한 고차 함수를 짤 때.

하지만 이는 어디까지나 '임시방편'이어야 합니다. any를 썼다면 반드시 TODO 주석을 남기고 나중에 적절한 타입으로 교체해야 합니다.