일반

Exclusive 타입, 역직렬화 그리고 소통

leesche 2022. 12. 18. 10:19

지난 12월, 상호배타적인 프로퍼티를 갖는 객체를 타입으로 만들고 싶었다. 왜냐하면 그렇게 요청을 보내야 했기 때문이다. 예를 들어 성과를 조회하기 위해 서버에 요청을 하는 상황에서 요청 Body를 두 종류로 보내야 했다.

  • 상품 아이디를 보내면 상품에 대한 성과를 요청한다.
  • 상점 아이디를 보내면 상점의 모든 상품에 대한 성과를 요청한다.
interface GetProductPerformance {
  productId: number
}

interface GetStorePerformance {
  storeId: number
}

이 두가지 케이스를 모두 포함하는 타입을 만들고 싶었다. 이때 productId와 storeId는 상호 배타적으로, 하나가 있으면 다른 하나는 객체에 없어야 했다. 혼자서 배운 타입스크립트 지식으로는 잘 되지 않아서, 여기저기 조사한 끝에 아래 커스텀 타입을 만들 수 있었다. (출처)

type Exclusive<
  T extends Record<PropertyKey, unknown>,
  U extends Record<PropertyKey, unknown>
> =
  | (T & { [k in Exclude<keyof U, keyof T>]?: never })
  | (U & { [k in Exclude<keyof T, keyof U>]?: never })

이렇게 커스텀 타입을 작성해서 상호배타적인 프로퍼티를 갖는 객체 타입을 작성했다. 매우 뿌듯했다. 하지만 이내 청천벽력 같은 소식을 동료 프론트엔드 개발자에게 들었다. 정확한 것은 기억나지 않지만 이런 느낌이었다.

"이렇게 복잡하게 하지 않고, null만 할당해줘도 서버에서 알아서 처리할 거예요."

띠용. 나는 놀라서 이 명세를 만든 서버 개발자에게 그렇게 해도 되냐고 물어봤다.

"네, 서버에서 역직렬화 할 때 알아서 프로퍼티가 없어지고, 그렇게 null을 할당해서 보내줘도 잘 작동할 거예요."

허탈했다. 직렬화, 역직렬화는 객체를 전송 가능한 상태로 만드는 것이고 역직렬화는 직렬화된 객체를 다시 객체의 형태로 만드는 것이다. 이때 값이 null인 프로퍼티는 취급되지 않는 듯했다. 아니면 애초에 null이기 때문에 상관 없었나.

이 과정에서 얻었던 교훈은 다음과 같았다.

  • 소통은 중요하다.
  • 명세대로 구현할 때 어려움이 있다면, 혼자서 끙끙 앓으면서 해결하려고 하기보다 명세를 만든 개발자, 같은 경험을 했을 것 같은 동료 개발자에게 (될 수 있으면 빠르게) 물어보자.

하지만 문제를 해결하긴 했었고 그 과정에서 지식도 늘었기 때문에 완전히 손해만 본 것은 아니었다. 너무 시무룩해하지 말고, 경험치가 쌓인 것에 기쁘게 생각하자.

참고문헌