TypeScript any를 unknown으로 바꿨더니 컴파일 에러가 쏟아진 이유
문제 발생
외부 API 응답 타입을 몰라서 any로 처리하던 코드를 안전하게 만들려고 unknown으로 바꿨더니, 이전에는 문제없이 컴파일되던 곳에서 갑자기 여러 개의 타입 에러가 발생했습니다.
function handle(data: unknown) {
console.log(data.message); // 에러: 'data' is of type 'unknown'
}원인 분석
any와 unknown은 둘 다 "어떤 타입이든 될 수 있다"는 점은 같지만, TypeScript가 그 값을 다룰 때 취하는 태도가 정반대입니다.
any: 사실상 타입 검사를 그 값에 대해서만 완전히 꺼버립니다.any타입 값은 어떤 속성에 접근하든, 어떤 함수에 넘기든 컴파일러가 아무 검사도 하지 않습니다. 그만큼 런타임 에러를 컴파일 타임에 못 잡아냅니다.unknown: "타입을 모른다"는 사실을 컴파일러가 계속 기억합니다.unknown타입 값은 타입을 좁히기(narrowing) 전까지는 어떤 속성 접근이나 연산도 허용하지 않습니다. 그래서data.message처럼 바로 접근하면 에러가 납니다 — 이건 버그가 아니라unknown이 정확히 의도한 동작입니다.
즉 unknown으로 바꾼 뒤 에러가 난 곳들은, 원래 any였을 때 타입 안전성 없이 그냥 통과되고 있던 잠재적 위험 지점이 이제야 드러난 것입니다.
해결 방안
unknown값을 실제로 쓰려면 먼저 타입을 좁혀야 합니다 —typeof,instanceof, 커스텀 타입 가드 등을 씁니다.
function handle(data: unknown) {
if (typeof data === "object" && data !== null && "message" in data) {
console.log((data as { message: string }).message);
}
}- 외부 API 응답처럼 형태가 확실하지 않은 값은 Zod 같은 런타임 검증 라이브러리로 파싱하면서 타입을 좁히는 것이 가장 안전합니다 — 컴파일 타임 타입과 런타임 실제 데이터가 어긋나는 것까지 막아줍니다.
const schema = z.object({ message: z.string() });
function handle(data: unknown) {
const parsed = schema.parse(data); // 여기서 타입이 좁혀지고 런타임 검증도 됨
console.log(parsed.message);
}any는 정말 불가피한 경우(타입 정의가 없는 오래된 라이브러리 등)로 최소화하고, "타입을 아직 모른다"는 상태를 표현하고 싶을 때는 기본적으로unknown을 씁니다.- 기존 코드베이스에
any가 많다면, 한 번에 전부 바꾸기보다 새로 작성하는 코드부터unknown을 기본으로 쓰고 점진적으로 옮기는 것이 현실적입니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.