타입스크립트(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 주석을 남기고 나중에 적절한 타입으로 교체해야 합니다.

+ Recent posts