타입 가드(Type Guard)란 무엇인가?
타입스크립트의 타입 가드(Type Guard)는 코드 실행 중 변수의 타입을 판별하여 특정 타입임을 좁히는 방식입니다. 유니온 타입이나 any 타입을 사용할 때, 올바른 타입에 따라 안전하게 접근할 수 있도록 돕습니다. 조건문 안에서 사용되며, 타입 안전성을 강화하는 핵심 기능입니다.
function process(value: string | number) { if (typeof value === "string") { console.log(value.toUpperCase()); } else { console.log(value.toFixed(2)); } }
위 함수는 typeof
연산자를 사용해 타입을 확인하고, 타입에 따라 적절한 메서드를 호출합니다.
내장 타입 가드 종류
TypeScript는 몇 가지 기본적인 타입 가드 방법을 제공합니다:
- typeof: string, number, boolean, symbol, bigint, function 등
- instanceof: 클래스 기반 객체의 인스턴스를 판별
- in 연산자: 객체에 특정 속성이 존재하는지 확인
function isDate(value: any): boolean { return value instanceof Date; }
function hasName(obj: any): boolean { return "name" in obj; }
이러한 기본 가드는 자주 사용되는 타입에 대해 안전하게 타입을 좁히는 데 유용합니다.
사용자 정의 타입 가드 (User-defined Type Guard)
사용자 정의 타입 가드는 특정 조건을 만족할 경우, 개발자가 직접 타입을 좁힐 수 있도록 만드는 함수입니다. 반환 타입에 value is Type
형태를 사용합니다.
type Dog = { kind: "dog"; bark: () => void }; type Cat = { kind: "cat"; meow: () => void }; type Pet = Dog | Cat; function isDog(pet: Pet): pet is Dog { return pet.kind === "dog"; } function handlePet(pet: Pet) { if (isDog(pet)) { pet.bark(); } else { pet.meow(); } }
이처럼 커스텀 타입 가드를 사용하면 유니온 타입에서 안전하게 분기 처리할 수 있습니다.
in 연산자를 활용한 타입 가드
in
연산자는 객체의 특정 속성 유무를 검사해 타입을 좁힐 수 있습니다.
type Admin = { name: string; accessLevel: number }; type Guest = { name: string }; function isAdmin(user: Admin | Guest): user is Admin { return "accessLevel" in user; }
accessLevel
속성의 존재 여부로 타입을 판별하여, Admin
타입임을 안전하게 확인할 수 있습니다.
instanceof를 활용한 클래스 타입 가드
클래스 인스턴스를 판별할 경우에는 instanceof
연산자를 사용합니다.
class Car { drive() { console.log("Driving..."); } } class Boat { sail() { console.log("Sailing..."); } } function isCar(vehicle: Car | Boat): vehicle is Car { return vehicle instanceof Car; } function move(vehicle: Car | Boat) { if (isCar(vehicle)) { vehicle.drive(); } else { vehicle.sail(); } }
이 방식은 클래스를 기준으로 타입을 판별할 수 있어, 객체지향 구조에 적합합니다.
복잡한 유니온 타입 처리 예제
유니온 타입이 많아질수록 타입 가드의 중요성이 커집니다. 예를 들어 다양한 알림 타입을 처리하는 구조를 보면 다음과 같습니다.
type Email = { type: "email"; subject: string }; type SMS = { type: "sms"; phone: string }; type Push = { type: "push"; title: string }; type Notification = Email | SMS | Push; function handleNotification(notif: Notification) { switch (notif.type) { case "email": console.log(`Email: ${notif.subject}`); break; case "sms": console.log(`SMS to: ${notif.phone}`); break; case "push": console.log(`Push title: ${notif.title}`); break; } }
이 방식은 디스크리미네이티드 유니온(discriminated union)이라 불리며, 각 타입에 고유 식별자 필드를 포함해 타입을 명확히 구분합니다.
사용자 정의 타입 가드의 장점
- 복잡한 타입 판별 로직을 함수로 추출 가능
- 가독성과 재사용성 향상
- 코드 내 안전성 강화 및 런타임 오류 방지
- TypeScript의 타입 시스템을 적극적으로 활용 가능
주의할 점
타입 가드 구현 시 다음과 같은 점을 유의해야 합니다:
- 타입이 불완전한 경우
any
나unknown
을 사용하기보다 명확한 검사 수행 - 객체 속성 검사 시 존재 여부만 판단하지 말고 타입까지 확인
- 타입 가드를 너무 남용하면 코드 복잡도가 높아질 수 있음
결론: 타입 안전성과 가독성을 동시에 높이는 타입 가드
TypeScript 타입 가드는 코드의 타입 안전성을 유지하면서 유연하고 복잡한 로직을 구현할 수 있는 강력한 도구입니다. 특히 유니온 타입, 클래스 인스턴스, 복합 객체를 다룰 때 효과적입니다.
사용자 정의 타입 가드는 TypeScript의 정적 분석 기능을 극대화하며, 실무에서 데이터의 안정성과 예측 가능성을 보장하는 데 매우 유용합니다. 타입 가드를 전략적으로 활용하면 코드의 품질이 한층 향상됩니다.