Skip to content

로깅 함수 분리하기

로깅함수는 훅이나 다른 함수에 같은 위계로 추상화하지 않습니다.

좋지 않은 예시

tsx
// hook
export const usePurchaseProduct = ({ productId }: { productId: number }) => {
  const { mutate } = useMutation(productQuery.purchase());
  const product = useProductDetail(productId);

  const purchaseProduct = () => {
    sendCPLog('scc_button_click', { productId });
    mutate(product);
  };

  return purchaseProduct;
};

// component
export const PurchaseButton = ({ productId }: { productId: number }) => {
  const purchaseProduct = usePurchaseProduct(productId);

  const handlePurchaseButtonClick = () => {
    purchaseProduct();
  };

  return <button onClick={handlePurchaseButtonClick}>구매하기</button>;
};

문제

로깅과 비즈니스 로직의 혼재

usePurchaseProduct는 상품을 구매하는 비즈니스 로직을 담당하는 훅으로, 로깅과는 다른 책임을 가집니다. 즉, usePurchaseProduct 비즈니스적 맥락을 담고 있고 로깅은 사용자의 인터렉션에 따른 로그 수집에 대한 책임을 가집니다.

이 구조에서는 다음과 같은 문제가 생깁니다.

  • 필요한 데이터가 다름: 구매 로직은 product 전체가 필요하지만, 로깅은 productId만 있으면 됩니다. 관심사가 다르면 필요한 데이터도 다르고, 변경 이유도 달라집니다. 로깅 스펙이 바뀔 때마다 비즈니스 로직 훅을 수정해야 하는 상황이 생깁니다

  • 공통 로직 사용의 오류 가능성: 만약 다른 지면에서 구매 로직이 필요하다면, 그때도 동일한 로깅이 필요할지 알 수 없습니다. 다른 로그가 필요할 경우 해당 훅을 수정해야 합니다. 이는 훅의 재사용성을 떨어뜨립니다.

좋은 예시

tsx
// hook
export const usePurchaseProduct = ({ productId }: { productId: number }) => {
  const { mutate } = useMutation(productQuery.purchase());

  const purchaseProduct = () => {
    mutate(product);
  };

  return purchaseProduct;
};

// component
export const PurchaseButton = ({ productId }: { productId: number }) => {
  const purchaseProduct = usePurchaseProduct(productId);

  const handlePurchaseButtonClick = () => {
    purchaseProduct(productId);
    sendCPLog('scc_button_click', { productId });
  };

  return <button onClick={handlePurchaseButtonClick}>구매하기</button>;
};

훅은 비즈니스 로직만 담당하고, 로깅은 컴포넌트의 이벤트 핸들러에서 호출합니다.