타입 가드(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의 정적 분석 기능을 극대화하며, 실무에서 데이터의 안정성과 예측 가능성을 보장하는 데 매우 유용합니다. 타입 가드를 전략적으로 활용하면 코드의 품질이 한층 향상됩니다.