타입스크립트, 유니온 타입의 함정? 타입 좁히기로 해결!
유니온 타입(Union Type) 변수 사용 시, 타입스크립트가 특정 메서드 사용을 막는 문제를 타입 좁히기(Type Narrowing)로 해결
타입 가드(Type Guard)는 조건문 등을 활용하여 타입을 좁히는 표현으로, typeof, instanceof, in 연산자 등이 존재
판별된 유니온(Discriminated Union) 패턴은 API 응답 처리 등에서 유용하며, status 필드를 활용하여 타입 분기
타입 좁히기 시 타입 단언(Type Assertion) 남용과 콜백 함수 내 타입 풀림에 주의해야 함
타입 좁히기(Type Narrowing)의 기본 원리
타입스크립트(TypeScript)에서 유니온 타입(Union Type)은 변수가 여러 타입 중 하나가 될 수 있음을 의미한다. 하지만, 유니온 타입 변수는 공통 프로퍼티(Property)와 메서드(Method)만 사용할 수 있다는 제약이 있다. 타입 좁히기(Type Narrowing)는 조건문(Conditional Statement) 등을 활용하여 유니온 타입의 범위를 좁혀, 특정 타입 전용 기능 사용을 가능하게 한다. 예를 들어, `typeof` 연산자를 사용하여 문자열(string) 여부를 확인하고, 해당 블록 내에서 문자열 전용 메서드를 안전하게 호출할 수 있다. 이러한 과정을 통해 타입 안전성(Type Safety)을 확보하고, 런타임 에러(Runtime Error)를 방지한다.
타입 가드(Type Guard) 패턴 심층 분석
타입 좁히기를 실현하는 구체적인 도구는 타입 가드(Type Guard)이며, 실무에서 다양한 패턴으로 활용된다.
`typeof`: 자바스크립트(JavaScript)의 `typeof` 연산자를 기반으로 원시 타입(Primitive Type)을 구분하며, `null` 처리 시 주의가 필요하다.
`instanceof`: 특정 클래스(Class)의 인스턴스(Instance) 여부를 확인하며, 클래스 기반 타입(Class-based Type)에 유용하다.
`in`: 객체(Object)에 특정 프로퍼티(Property) 존재 여부를 확인하며, 인터페이스(Interface)나 타입 별칭(Type Alias)으로 정의된 객체 타입 구분에 효과적이다.
각 패턴은 상황에 따라 적합성이 다르므로, 특징과 사용 시점을 이해하는 것이 중요하다.
판별된 유니온(Discriminated Union) 패턴의 활용
실무에서 가장 많이 사용되는 타입 좁히기 패턴 중 하나는 판별된 유니온(Discriminated Union)이다. 유니온을 구성하는 각 타입이 공통적으로 가지고 있는 리터럴 타입(Literal Type) 필드를 기준으로 분기하는 방식이다. API 응답 처리(API Response Handling)를 예로 들면, `status` 필드를 판별자(Discriminant)로 사용하여 성공(success)과 에러(error) 응답을 구분할 수 있다.
장점: 코드의 의도가 명확하고, 새로운 상태 추가 시 확장성이 용이하다.
never 타입(never Type)과 exhaustive check를 함께 사용하면, 누락된 분기를 컴파일(Compile) 단계에서 잡아낼 수 있다. 리액트(React)의 API 호출 상태 관리나, Redux(Redux) 같은 상태 관리 도구에서 액션(Action)을 구분할 때 자주 사용된다.
타입 가드 함수(Type Guard Function)의 활용
타입스크립트(TypeScript)가 자동으로 추론하지 못하는 상황에서, 타입 가드 함수(Type Guard Function)를 활용하여 타입 좁히기를 구현할 수 있다. is 키워드를 사용하여 반환 타입을 `value is string` 형태로 선언하면, 함수가 `true`를 반환할 때 해당 매개변수가 특정 타입임을 타입스크립트에 알려줄 수 있다.
장점: 반복적인 타입 체크(Type Check) 로직을 함수로 분리하여 코드 중복을 줄이고, 타입 안전성(Type Safety)을 유지한다.
활용 사례: 배열(Array)에서 `null`이나 `undefined`를 걸러내는 필터링(Filtering) 패턴에서 유용하게 사용된다.
이러한 패턴은 코드의 가독성(Readability)을 높이고, 유지보수(Maintenance)를 용이하게 한다.
타입 좁히기(Type Narrowing) 사용 시 주의사항
타입 좁히기는 강력한 기능이지만, 몇 가지 주의해야 할 점이 있다.
타입 단언(Type Assertion) 남용: 타입 좁히기 대신 `as`를 사용하여 강제 변환하면, 타입 안전성(Type Safety)을 포기하는 결과를 초래한다. 런타임(Runtime) 값과 타입이 일치하지 않으면 에러를 잡을 수 없다.
콜백 함수(Callback Function) 내 타입 풀림: 바깥 스코프(Scope)에서 타입을 좁혀놨더라도, 콜백 함수 안에서는 정보가 유지되지 않을 수 있다.
해결책: 재할당 가능성이 없고 제어 흐름상 안전하다고 판단되는 경우를 제외하고, 좁혀진 값을 별도의 `const` 변수에 담아두는 것이 안전하다. 타입 좁히기를 올바르게 사용하면, 타입스크립트(TypeScript)의 강력한 기능을 최대한 활용할 수 있다.