saewoo
githubemail
2023-5-11

컴포넌트 생성을 전담하는 팩토리 컴포넌트 만들기

박아무개씨의 고군분투

비즈니스 조건에 따라 컴포넌트를 렌더링 하는 경우가 왕왕 있습니다. 이런 경우 '어떻게 컴포넌트를 작성해야 관리하기 용이할까?'에 대한 의문이 있었습니다.

최근 '헤드퍼스트 디자인 패턴'이란 책을 읽고 있는데 팩토리 패턴에서 '팩토리'를 위와 같은 경우에 적용하면 괜찮을 것 같았습니다.

설명을 돕기 위해 실제로 있을 법한 이야기로 구성해 보았습니다.

박아무개씨의 고군분투

프론트엔드 개발자 박아무개씨는 신규 서비스로 숙박 서비스를 제공하는 웹 사이트를 구현하라는 지시가 떨어졌고,

그중 예약 상세 페이지를 구현하는 역할을 맡게 되었습니다.

기획서를 훑어보다 박아무개씨는 결제 방식에 따라 '결제 정보'를 다르게 보여줘야 하는 부분을 발견했습니다.

조건은 선불이냐 후불이냐로 간단했습니다. 박아무개씨는 다음과 같은 코드를 작성했습니다.

const ReservationDetailMobilePage = () => {
  return (
    <>
      <예약일 />
      <숙소위치 />

      ...otherComponents

      { isPrepaid ? <PrePaid> : <PostPay> }
    </>
  )
}

삼항 연산자로 조건에 따라 view를 렌더링 하도록 했습니다. 박아무개씨는 아주 뿌듯했습니다.

시간이 흘러 회사는 성장했고, 더 많은 매출을 올리기 위해서 회사는 '공항 픽업'이라는 옵션을 추가로 판매하기로 했습니다.

기획자는 디자이너에게 적합한 UI 디자인을 요구했고, 디자이너는 조건에 따라 다른 UI를 보여주도록 디자인 했습니다.

기존 선불, 후불 2가지 경우에서 옵션이 추가되어 2 * 2의 4가지의 경우로 조건이 변경되었습니다.

  • 선불 && 옵션 o
  • 선불 && 옵션 x
  • 후불 && 옵션 o
  • 후불 && 옵션 x

삼항 연산자를 중첩해서 쓰는것은 읽기가 어려우니 박아무개씨는 다음과 같이 코드를 수정했습니다.

const ReservationDetailMobilePage = () => {
  return (
    <>
      <예약일 />
      <숙소위치 />

      ...otherComponents

      { isPrepaid && hasOptions <PrepaidWithOptions />}
      { isPrepaid && !hasOptions <PrepaidWithoutOptions />}
      { isPostPay && hasOptions <PostPayWithOptions />}
      { isPostPay && !hasOptions <PostPayWithoutOptions />}
    </>
  )
}

아무리봐도 마음에 들지 않아 고민하던 박아무개씨는 문득 스터디에서 팩토리 패턴을 읽은 기억이 떠올랐습니다.

'인스턴스 생성을 전담하는 객체가 팩토리지.. '

'컴포넌트를 객체로 생각하면.. 실제로 객체이고..'

'컴포넌트 생성을 전담하는 컴포넌트를 만들어볼까 ?'

type PaymentType = 'prepaidWithOptions' | 'PrepaidWithoutOptions' | 'PostPayWithOptions' | 'PostPayWithoutOptions';

const PaymentFactory = ({ 
  type 
} : {
  type: PaymentType
}) => {
  if (type === 'prepaidWithOptions') return <PrepaidWithOptions />
  if (type === 'PrepaidWithoutOptions') return <PrepaidWithoutOptions />
  if (type === 'PostPayWithOptions') return <PostPayWithOptions />
  if (type === 'PostPayWithoutOptions') return <PostPayWithoutOptions />
}

먼저 결제 정보를 렌더링 하는 팩토리 컴포넌트 PaymentFactory를 만들었습니다. props로 type을 받고 조건에 따라 컴포넌트를 찍어냅니다.

ReservationDetailMobilePage 컴포넌트로부터 걸제정보 인스턴스(컴포넌트)를 생성하는 부분을 PaymentFactory로 캡슐화했습니다.

const ReservationDetailMobilePage = () => {
  const paymentType = getPaymentType();

  return (
    <>
      <예약일 />
      <숙소위치 />

      ...otherComponents

      <PaymentFactory type={paymentType}>
    </>
  )
}

결제 정보 view를 그리는 역할은 PaymentFactory가 전담하기 때문에 박아무개씨는 결제정보에 대한 기획이 추가 혹은 변경되더라도, ReservationDetailMobilePage 컴포넌트는 수정할 필요 없이 PaymentFactory 컴포넌트의 인스턴스 생성(컴포넌트생성)하는 부분만 수정하면 결제정보에 대한 view를 손쉽게 수정할 수 있게 되었습니다.

요약

  1. 삼항 연산자로 조건에 맞는 컴포넌트를 렌더링 함
  2. 조건이 추가됨
  3. 팩토리를 적용하여 결제 정보에대한 컴포넌트 생성을 캡슐화 함

장점

  1. PaymentFactory 컴포넌트 재사용 가능
  2. 변경되는 부분이 캡슐화