이번에는 TypeScript 코드를 다룰 때 타입 안전성을 높이고, 유지보수와 확장성을 강화하는 방법 중 "고급 타입" 활용에 대해 정리해 보겠습니다.
TypeScript 고급 타입 활용
유니온 타입과 인터섹션 타입
TypeScript를 배우다 보면 타입을 직접 명시하는 방식이 눈에 띕니다. string이나 number 같은 기본 타입만으로는 복잡한 상황을 다루기에 부족한 경우가 있는데, 이때 유니온 타입( | )과 인터섹션 타입( & )을 사용할 수 있죠.
유니온 타입은 값이 여러 타입 중 하나일 수 있다는 걸 표현합니다. 예를 들어, 버튼이 "켜짐", "꺼짐", "로딩 중" 같은 상태를 가질 수 있다고 하면, React Native에서 이를 타입으로 나타내면 다음과 같습니다.
type ButtonStatus = "on" | "off" | "loading";
const updateButtonStatus = (status: ButtonStatus) => {
console.log(`버튼 상태: ${status}`);
};
updateButtonStatus("on"); // 문제없음
updateButtonStatus("wrong"); // 오류: "wrong"은 정의되지 않음
이렇게 하면 실수를 줄이고 코드의 안정성을 높일 수 있죠. 오타나 엉뚱한 값을 넣을 일이 줄어드는 거예요.
인터섹션 타입은 여러 타입을 하나로 묶어줍니다. 사용자 기본 정보와 관리자 권한을 함께 가진 데이터를 표현하고 싶을 때 다음 처럼 해볼 수 있습니다.
type Person = { name: string; age: number };
type Manager = { position: string };
type ManagerPerson = Person & Manager;
const manager: ManagerPerson = {
name: "영희",
age: 28,
position: "팀장",
};
React Native에서는 컴포넌트의 속성(props)을 정의할 때 인터섹션 타입이 유용하죠. 텍스트와 버튼 기능을 동시에 가진 컴포넌트를 만들어보면,
type TextAttributes = { content: string; textSize: number };
type ButtonAttributes = { onClick: () => void };
type TextButtonAttributes = TextAttributes & ButtonAttributes;
const TextButton = ({ content, textSize, onClick }: TextButtonAttributes) => (
<TouchableOpacity onClick={onClick}>
<Text style={{ fontSize: textSize }}>{content}</Text>
</TouchableOpacity>
);
유니온 타입은 "이것 아니면 저것"을, 인터섹션 타입은 "이것도 되고 저것도 되는" 경우를 다룹니다. 이 둘을 적절히 활용하면 코드가 더 유연하면서도 단단해지죠.
제네릭 (Generics)
제네릭은 TypeScript에서 타입을 일종의 "변수"처럼 다루는 방법입니다. 함수나 컴포넌트를 만들 때 타입을 고정하지 않고, 나중에 원하는 대로 정할 수 있게 해 줍니다. 왜 이런 게 필요할까요?
배열의 첫 번째 값을 가져오는 함수를 예로 들어보겠습니다.
function pickFirst(arr: any[]): any {
return arr[0];
}
이 코드는 작동하지만, 반환값이 any
라서 타입의 불안정합니다. 이때 제네릭을 적용하면,
function pickFirst<T>(arr: T[]): T {
return arr[0];
}
const numResult = pickFirst<number>([10, 20, 30]); // number 타입
const strResult = pickFirst<string>(["가", "나", "다"]); // string 타입
<T>
는 타입을 변수처럼 쓰겠다는 뜻입니다.
React Native에서 제네릭은 컴포넌트를 재활용할 때 특히 효율적이죠. 리스트를 그리는 컴포넌트를 만들어보면,
type ListAttributes<T> = {
data: T[];
displayItem: (item: T) => JSX.Element;
};
const CustomList = <T,>({ data, displayItem }: ListAttributes<T>) => (
<FlatList data={data} renderItem={({ item }) => displayItem(item)} />
);
// 활용 예시
const fruitList = (
<CustomList data={["사과", "배"]} displayItem={(fruit) => <Text>{fruit}</Text>} />
);
제네릭 덕분에 data
와 displayItem
의 타입이 서로 맞물려 오류를 줄이고 안정성 높은 코드를 구현하게되죠.
타입 추론과 타입 단언
TypeScript는 타입을 굳이 적지 않아도 알아서 판단하기도 합니다. 예를 들면,
let username = "이순신"; // string으로 알아서 판단
username = 100; // 오류: 숫자는 string에 넣을 수 없음
이게 바로 타입 추론입니다. React Native에서 상태를 다룰 때도 사용하죠.
const [score, setScore] = useState(0); // score는 number로 추론됨
그런데 때로는 TypeScript가 우리의 생각을 읽지 못할 때가 있습니다. 그럴 때 타입 단언(Type Assertion)을 사용합니다. DOM에서 요소를 가져오는 경우를 보죠.
const textField = document.getElementById("userInput") as HTMLInputElement;
textField.value = "안녕하세요"; // 문제없음
React Native에서는 ref
를 쓸 때 종종 타입 단언이 필요합니다.
const inputRef = useRef<TextInput>(null);
inputRef.current?.focus();
하지만 타입 단언은 신중해야 합니다. 잘못하면 문제를 감출 수 있거든요.
const data: any = "456";
const strLength = (data as string).length; // 괜찮음
const numError = (data as number).toFixed(2); // 실행 중 오류 발생!
타입 단언은 "내가 확신해"라는 약속과 같으므로, 정말 확실할 때만 써야 합니다.
'프로그래밍 언어 > TypeScript' 카테고리의 다른 글
JavaScript 라이브러리 활용, 타입 정의 파일과 tsconfig.json 설정 - TypeScript #6 (0) | 2025.03.26 |
---|---|
TypeScript와 React Native로 배우는 모듈 시스템 Import/Export - TypeScript #5 (0) | 2025.03.21 |
타입스크립트의 객체 지향 프로그래밍, 클래스, 인터페이스, 상속 정리 - TypeScript #4 (0) | 2025.03.18 |
TypeScript 변수 선언부터 객체 인터페이스까지 기본 문법과 타입 시스템 - TypeScript #2 (0) | 2025.03.07 |
TypeScript로 정적 타입 코딩 효율성 높기 방법을 배워보자 - TypeScript #1 (0) | 2025.03.04 |