DEV Community

Dylan
Dylan

Posted on

Redux에 custom hook으로 Computed value 달아주기

React를 쓰는 많은 분들이 프로젝트를 시작하기 전 첫 번째로 고민하게 되는 기술스택이 글로벌 상태관리 라이브러리 reduxmobx 중 어떤 것을 선택할까가 아닐까 합니다.
깃헙 스타수로 보면 리덕스의 인기가 좀 더 많은 것을 알 수 있습니다. 저도 리덕스를 주로 쓰지만 mobx 의 장점 중 하나는 데코레이터 문법과 computed value 에 있다고 생각합니다.

@computed get discountedPrice() {
  return this.price * this.discount
}

이런식으로 store에 데코레이터 문법으로 getter를 달아주는 방식입니다. redux에는 api가 없지만 reselect 라는 라이브러리를 통해서 비슷한 방식을 구현할 수 있습니다.
이제 react에 훅이 도입되면서 추가 라이브러리 없이도 computed value를 쉽게 구현할 수 있게 되었습니다.

스토어 fruitStore에 서버로부터 과일의 할인율과 가격 데이터를 받아 저장되어 있다고 해보겠습니다.

const initialState: FruitStore = {
  apple: { discount: 0.03, price: 1000 },
  orange: { discount: 0.12, price: 3000 },
  grape: { discount: 0.2, price: 8000 },
  ...
};

저장된 데이터는 Tag 컴포넌트에 3가지 방법으로 표현됩니다.

  1. 할인된 가격을 표현해줍니다.
  2. 할인이 적용된 최종 결제가격을 보여줍니다.
  3. 할인율을 읽기 쉽게 %로 보여줍니다.

데이터를 view에 표현하기 위해서는 항상 아래와 같이 값을 변환해주어야 합니다.

const discountedPrice: number = price * discount;
const billingPrice: number = price * (1 - discount);
const discountPercent: string = `${discount * 100} %`;

만약에 위의 데이터를 쓰는 컴포넌트가 여러개라면 각 컴포넌트마다 같은 코드를 복사/붙여넣기 해야 할 것이고 유지보수하기도 점점 어려워질 것입니다.
스토어에서 3가지 데이터를 모두 저장하는 것도 생각해볼 수 있습니다. 하지만 이런 경우에 nested 된 형태로 스토어를 관리해야 될 가능성이 높아지고, view의 형태가 다양해지면 복잡성이 기하급수적으로 커지게 됩니다.

커스텀훅과 useMemo를 조합해 computed value를 구현해보겠습니다.

// custom hook
function useFruitTag(fruit: string) {
  const { discount, price } = useSelect(({ fruitStore }) => fruitStore[fruit]);

  const discountedPrice = useMemo(() => price * discount, [discount, price]);
  const billingPrice = useMemo(() => price * (1 - discount), [discount, price]);
  const discountPercent = useMemo(() => `${discount * 100} %`, [discount]);

  return {
    discountedPrice,
    billingPrice,
    discountPercent,
  };
}

// component
function AppleTag() {
  const { discountedPrice, billingPrice, discountPercent } = useFruitTag('apple');

  return (
    <div>
      <h1>Apple</h1>
      <p>{discountedPrice}</p>
      <p>{billingPrice}</p>
      <p>{discountPercent}</p>
    </div>
  );
}

이런 방식으로 reselect@computed 없이도 computed value를 쉽게 만들어 쓸 수 있습니다. useMemo를 이용해서 캐싱된 값을 쓰기도 쉽고, 과일의 종류가 늘어나거나 대응해야 하는 view가 늘어나더라도 쉽게 처리할 수 있습니다. 무엇보다 관심사별로 로직이 분리되면서 코드를 읽기 쉬워진다는 게 가장 큰 장점인 것 같습니다.

p.s
useMemoreselect를 완전히 대체한다는 뜻은 아닙니다. 관련된 내용은 여기 에 잘 설명되어 있습니다.

Top comments (0)