프로젝트에 적용해보기

GunYong·2024년 6월 5일

React

목록 보기
3/17
post-thumbnail

프로젝트를 구현하고 배포한 이후에는 리엑트 코드를 리펙토링중이다.
일단 코드를 짜면서 한 페이지에 여러개의 상태가 필요해 많은 useState를 사용해 나타낸 것이 보기도 별로였고 유지보수 단계에서도 힘들었다.

어려웠던 점은 코드를 짤 당시에는 목적과 용도가 분명했던 상태들이 시간이 지나고 다시 보게 되니까 어떤 상황에서 사용하고 어디서 사용했는지 이해하고 파악하는데 상당히 시간이 오래 걸렸다.

그래서 저번 글에서 다뤘던 비슷한 성격을 가진 상태들은 객체로 묶어 관리하고 커스텀 훅을 이용해 JSX 코드와 분리해주었다.

대표적으로 ServiceAgree 즉 회원가입 후 서비스 이용 동의를 받는 페이지를 다뤄보겠다.

이 페이지에서는 각 버튼을 관리하는 상태가 필요하고 버튼의 작동 여부를 나타낼 상태가 필요하다. 기존 코드를 보면 다음과 같다.

const ServiceAgree = () => {
  const navigate = useNavigate();
  const [all, setAll] = useState<boolean>(false);
  const [age, setAge] = useState<boolean>(false);
  const [service, setService] = useState<boolean>(false);
  const [personal, setPersonal] = useState<boolean>(false);
  const [optional, setOptional] = useState<boolean>(false);
  const [next, setNext] = useState<boolean>(false);
  const [data, setData] = useState<Object>({});

  const toggleClickAll = () => {
    setAll(!all);
    setService(!all);
    setPersonal(!all);
  };

  const toggleClickAge = () => {
    setAge(!age);
  };

  const toggleClickService = () => {
    setService(!service);
  };

  const toggleClickPersonal = () => {
    setPersonal(!personal);
  };

  const toggleClickOptional = () => {
    setOptional(!optional);
  };

  useEffect(() => {
    const agreeData: Data = {};

    interface Data {
      marketingAgreement?: boolean;
    }

    if (service && personal) {
      setNext(true);
      agreeData["marketingAgreement"] = true;
      setData(agreeData);
    } else {
      setNext(false);
    }
  }, [age, service, personal]);

  return (
    // JSX 
  );
};

7개의 각각의 useState가 사용되고 있고 각각의 상태를 핸들링 해주는 함수가 반복된다.
다음은 리펙토링 해본 코드이다.

interface Agreements {
  all: boolean;
  age: boolean;
  service: boolean;
  personal: boolean;
  optional: boolean;
  next: boolean;
}

interface Data {
  marketingAgreement?: boolean;
}

// 커스텀 훅을 통한 동작과 구현 코드 분리
const useServiceAgree = () => {
  const [agreements, setAgreements] = useState<Agreements>({
    all: false,
    age: false,
    service: false,
    personal: false,
    optional: false,
    next: false,
  });
  const [data, setData] = useState<Data>({});

  const toggleAgreement = (key: keyof Agreements) => {
    setAgreements((prev) => {
      const updated = { ...prev, [key]: !prev[key] };
      if (key === "all") {
        updated.age = !prev.all;
        updated.service = !prev.all;
        updated.personal = !prev.all;
        updated.optional = !prev.all;
      }
      return updated;
    });
  };

  useEffect(() => {
    const agreeData: Data = {};
    if (agreements.service && agreements.personal) {
      setAgreements((prev) => ({ ...prev, next: true }));
      agreeData["marketingAgreement"] = true;
      setData(agreeData);
    } else {
      setAgreements((prev) => ({ ...prev, next: false }));
    }
  }, [agreements.service, agreements.personal]);

  return { agreements, data, toggleAgreement };
};

const ServiceAgree = () => {
  const navigate = useNavigate();
  const { agreements, data, toggleAgreement } = useServiceAgree();

  return (
    // JSX
  );
};

리펙토링을 통해 얻을 수 있는 이점은 다음과 같았다.

  1. 구조화된 상태 관리: 상태를 객체로 묶어 관리함으로써 관련된 상태들을 한 곳에서 관리할 수 있어 상태의 흐름을 더 쉽게 이해하고 관리

  2. 컴포넌트 분리: 커스텀 훅을 사용하여 상태 관리와 로직을 컴포넌트에서 분리하면, 컴포넌트는 주로 UI 렌더링을 한다. 이는 컴포넌트의 복잡도를 낮추고, 가독성을 높힘

  3. 로직 분리로 인한 테스트 용이성: 상태 관리와 관련된 로직이 커스텀 훅에 모여 있기 때문에, 이 훅을 독립적으로 테스트 가능. UI 테스트와 로직 테스트를 분리하여 진행할 수 있음

  4. 자동 완성 및 타입 추론: TypeScript는 코드 작성 시 자동 완성과 타입 추론을 제공해서 오류를 줄여준다.

전에 개발자 컨버런스를 갔을때 들었던 말이 생각이 난다.

쓰레기 코드라도 구현,작동이 되는 코드가 작동도 안되는 리펙토링 잘된 이쁜 클린보다 100배 낫다.

이 말의 의미는 리펙토링은 작동하는 코드를 가지고 진행하는 과정이라는 것. 주객전도가 되지 않아야 한다는 점이다.

리펙토링 단계는 구현이 끝난 시점, 혹은 분기점에 진행하면 좋을 것 같다.

구현 이후에 리펙토링을 진행하며 자연스래 좋은 코드 습관들이 내것이 되어 구현 단계에서도 나타나지 않을까?

구현과 리펙토링 모두 열심히 공부하고 발전시켜야겠다.

매일 한 컴포넌트씩 리펙토링을 진행해봐야겠다.

06.07 -> 서재 페이지 리펙토링 ✅ : 기존 300줄이었던 코드를 연관성 있는 상태를 객체화 하여 묶고 커스텀 훅으로 동작 부분을 분리 후 파일 처리 하였다.
분리를 하고 코드를 보니까 가독성이 무척 높아졌고 코드를 수정하기도 편리해졌다!
--->> JSX 코드 : Library.tsx / 동작 코드 : useBookLibrary.tsx
https://github.com/Book-us/bookus-client/commit/99c63efac9b24d899baab2298b4b87847ed1e608

0개의 댓글