객체 타입 지향
아래는 전역 상태 관리 라이브러리 Caro-kann의 persist 미들웨어가 사용하는 유틸리티 함수의 타입이다. 각각의 함수는 모두 동일한 파라미터인 storageKey와 storageType을 필요로 하고 있다. GetStorage와 SetStorage는 그 이름 덕분에 무슨 동작을 하는지 쉽게 알 수 있다. 하지만 ExecMigration 타입은 정확히 뭘 하는지, GetStorage와 SetStorage 타입과는 무슨 관련이 있는지 알기 어렵다. 모르는 개발자가 처음 이 타입들을 본다면 ExecMigration와 나머지 두 타입이 관계가 없다고 생각할 지도 모르겠다.
type GetStorage: <T>(props: {
storageKey: string;
storageType: keyof StorageConfig | null;
migrate?: Migrate<T>;
initState: T;
}) => { state: T; version: number };
type SetStorage: <T>(props: {
storageKey: string;
storageType: keyof StorageConfig | null;
storageVersion: number;
value: T;
}) => void;
type ExecMigration: <T>(props: {
storageKey: string;
storageType: keyof StorageConfig | null;
migrate?: Migrate<T>;
}) => void;
객체 지향 프로그래밍(OOP)에서는 객체를 중심으로 데이터를 모델링하고, 그 객체에 속한 속성이나 메서드를 통해 상호작용한다. 객체의 구조를 정의하고, 해당 객체가 수행할 수 있는 동작을 캡슐화하는 방식으로 시스템을 설계하는 것이다. 이와 완벽하게 동일하다고 할 수는 없겠지만, "객체와 그 속성의 관계를 정의하고 조작하는" OOP의 아이디어는 TypeScript의 인덱싱 타입을 사용한 타입 설계에서 유사한 방식으로 구현할 수 있다.
아래와 같이 여러 타입을 하나의 객체 타입으로 감싸는 것으로써 세 함수 타입이 공통적으로 사용하는 파라미터를 한 곳에서 관리할 수 있고, 각각의 함수 타입들이 persist 미들웨어를 위한 유틸리티 함수라는 것을 명시적으로 드러낼 수 있다.
export type PersistUtils = {
common: {
storageKey: string;
storageType: keyof StorageConfig | null;
};
getStorage: <T>(props: PersistUtils["common"] & {
migrate?: Migrate<T>;
initState: T;
}) => { state: T; version: number };
setStorage: <T>(props: PersistUtils["common"] & {
storageVersion: number;
value: T;
}) => void;
execMigration: <T>(props: PersistUtils["common"] & {
migrate?: Migrate<T>;
}) => void;
}
또한, 여러 타입을 하나의 객체 타입으로 묶은 덕분에 함수의 구현부에서도 역시 각각의 함수가 persist 미들웨어를 위한 유틸리티 함수라는 사실을 별다른 주석 없이 다른 개발자들에게 알려줄 수 있다.
export const getStorage: PersistUtils["getStorage"]
export const setStorage: PersistUtils["setStorage"]
export const execMigrate: PersistUtils["execMigration"]
복잡한 타입 구조를 하나의 객체 타입으로 묶어 체계적으로 정리하는 행위는 단순히 타입스크립트의 기능을 활용하는 것만으로는 불가능한, 더 나은 협업과 유지 보수를 가능하게 만든다. 이는 단순히 코드를 기능적으로 작성하는 것을 넘어, 코드 스스로가 의도를 명확히 드러내고, 그로 인해 함께 일하는 동료들이 빠르게 맥락을 파악할 수 있도록 도우며, 궁극적으로는 더 나은 개발 경험을 제공한다.
타입스크립트에서 타입 안전성을 보장하는 것은 필요조건일 뿐, 충분조건은 아니다. 타입스크립트의 본질은 단순히 타입 검사를 넘어서, 코드가 개발자 간의 소통을 돕고, 더 의미 있고 유용하게 작동하도록 설계하는 데 있다고 나는 믿는다.
블로그의 정보
Ayden's journal
Beard Weard Ayden