Ayden's journal

객체 타입 지향

아래는 전역 상태 관리 라이브러리 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

활동하기