React - View와 Logic 분리하기 (custom hook)

Yang⭐·2022년 12월 20일
0

비즈니스 로직 - 뷰 컴포넌트 분리의 필요성

실무 중 코드 리팩토링을 진행하면서 ( 이직도 준비하면서 ) 뷰 컴포넌트와 비즈니스 로직의 분리 필요성을 느끼고 있다.

  1. A가 작업 중인 페이지는 모든게 짬뽕되어 있는 상태.
  2. B가 해당페이지와 엮여있는 로직을 수정해야하는 상황

👉 A가 작업중인 브랜치를 dev에 merge시켜줄 때 까지 기다리기 or 같은 파일 작업 후 충돌난거 해결하기

위와 같은 말도 안 되는 상황들이 생긴다.


Custom hook을 활용하자

간단하게 OTP인증 유효시간을 보여주는 컴포넌트를 수정해보았다.

// OTPCount.tsx (수정전)
const OTP_TIME = 60;

const OtpCounter = ({ counterStart }) => {
  const [count, setCount] = useState(OTP_TIME);

  useEffect(() => {
    const timeout =
      counterStart &&
      setTimeout(() => {
        setCount(count - 1);
      }, 1000);
    if (count === 0) {
      setCount(30);
      swalWarning("인증시간이 초과됐습니다.");
    }
    return () => clearTimeout(timeout);
  }, [count, counterStart]);

  return <P>{count}</P>;
};

위 코드는 뷰 컴포넌트로 counter 만 출력하는 부분을 맡아야한다. ( useEffect를 굳이 여기서 쓸 필요가 없음. )


// hooks.ts
const OTP_TIME = 60;

export const useOTPCounter = ( counterStart ) => {
  const [count, setCount] = useState(OTP_TIME);

  useEffect(() => {
    const timeout =
      counterStart &&
      setTimeout(() => {
        setCount(count - 1);
      }, 1000);
    if (count === 0) {
      setCount(30);
      swalWarning("인증시간이 초과됐습니다.");
    }
    return () => clearTimeout(timeout);
  }, [count, counterStart]);
  
  return count;
}

위 와 같이 OTP인증시간 countdown용 커스텀 훅을 만들고 아래 처럼 해당 구조를 수정했다.

// OTPCount.tsx (수정 후)
const OtpCounter = ({ counterStart }) => {
    const { count } = useOTPCounter(counterStart);
  
    return <P>{count}</P>;
};

여기서 아래 글을 읽고 진정한 클린코드란 무엇인가? 고민해보는 계기가 되었다.

클린 코드는 짧은 코드가 아닌 읽기 좋은(찾고 싶은 로직을 빠르게 찾을 수 있는) 코드입니다.
https://blog.leehov.in/57?category=951478


위 내용을 토대로 해당 데이터가 어떻게 렌더링하는지 파악이 가능하도록 수정했다.

// hooks.ts
const OTP_TIME = 60;
const ONE_SECOND = 1000;

export const useOTPCounter = ({ counterStart, seconds, onTimeout }) => {
  const [count, setCount] = useState(OTP_TIME);

  useEffect(() => {
    const timeout =
      counterStart &&
      setTimeout(() => {
        setCount(count - 1);
      }, ONE_SECOND);
    
    if (count === 0) {
      setCount(seconds);
      onTimeout();
    }
    return () => clearTimeout(timeout);
  }, [count, counterStart]);
  
  return count;
}
// OTPCount.tsx (수정 후)
const OtpCounter = ({ counterStart }) => {
    const { count } = useOTPCounter({ counterStart, seconds:30, onTimeout:alertMessage });
  
  	const alertMessage = () => swalWarning("인증시간이 초과됐습니다.");
      
    return <P>{count}</P>;
};

개인적으로 counterStart를 props로 여러번 전달하는게 안좋아보인다. store에 저장해 전역으로 관리하는 방법으로 진행하는 식으로 수정해봐야겠다.


끝으로

내 방 정리도 못하는데 누가봐도 읽기 쉽고 적절히 분리된 코드를 작성하는게 쉽진 않다.

ViewLogic 을 분리하고 custom hook pattern을 적용시키는 연습이 많이 필요하단것을 느끼고있다.


참고한 자료

0개의 댓글