Published on

리팩토링 중점사항 정리 (기존 프로젝트 문제점 분석)

본격적으로 프로젝트를 진행하기에 앞서, 중점적으로 개선하고 추가/제거/개선해야 할 부분을 찾기 위해 기존 프로젝트에서 느낀 문제점을 분석하고 이를 바탕으로 내용을 정리해 보고자 한다.


디렉터리 구조 변경

  • 기존 디렉터리의 경우 계층별로 구조를 나누어 관리했으나 프로젝트가 진행될수록 디렉터리의 개수가 늘어나게 되었다. 초기 refactoring 과정에서도 계층별 디렉터리 구조로 개발을 진행했으나 관련 파일이 사방에 계층별로 분리되어 작업하는데 불편함을 겪고는 했다.
comma
└── components
    ├── shared
└── (여러 기능이 공통으로 사용하는 코드)
    ├── auth
    ├── admin
    ├── basket
    ├── community
    ├── cafeteriaMenu
    ├── rental
    ...

특히 이번에 auth 관련 API 연동 로직을 React-Query를 적용해 커스텀 hook으로 만들게 되었다. 파일 및 코드가 적은 현 상황에서는 type, hook, component, constant을 공용으로 분리해도 괜찮겠지만 앞으로 다른 기능에서 발생하는 코드를 추가하게 되면 관련 내용을 찾는데 시간이 소모될 것 같다는 생각을 했다.

계층별 분류

comma-refactor
  └──components
     ├── shared
└── (여러 기능이 공통으로 사용하는 코드)
     └── auth
        ├── components
        │   ├── JoinForm.tsx
        │   ├── JoinForm.style.ts
      	│   ├── LoginForm.tsx
        │   └── LoginForm.style.ts
        ├── hooks
        │   ├── constants.ts
        │   ├── useAuth.ts
        │   └── useEmailAuthCode.ts
        └── utils
  • type, hook, common, constant를 각 계층별로 모든 기능이 공유하는 것이 아닌 기능별로 분류할 수 있도록 디렉터리를 구성하였다.
  • 확실히 가독성이 좋다. 관련 기능이 밀집되어 있어 짧은 시간 안에 필요한 코드를 발견할 수 있다.
  • 우연히 지역성의 원칙을 고려한 패키지 구조: 기능별로 나누기 아티클을 보고 관심을 가지게 되어 프로젝트에 적용해 보게 됐다.

🤔 주관적인 장점과 단점?

장점

  • 가독성의 이점으로 작업 속도가 향상된다. 각 기능별 style, constant, hook, component, common, util 디렉터리를 분류하여 원하는 코드를 찾기 수월하다.

단점

  • 디렉터리의 갯수가 많아진다. 각 컴포넌트, 파일별로 디렉터리를 구성하게 되므로 디렉터리의 갯수가 많아진다.


기존 COMMA 프로젝트에서 발생했던 문제점/피드백 받은 내용

1. 과도한 Get 메서드로 호출되는 API 요청

  • 날씨 위젯은 이번 리팩토링에서 제외

기존 날씨 & 학식 위젯

학식 위젯 코드

useEffect(() => {
  const Refresh = async () => {
    const url = `${process.env.REACT_APP_SERVER_DOMAIN}/cafeteriaMenu`;
    const response = await axios.get(url);
    console.log(response);
    if (response.status === 200) {
      for (let i = 0; i < response.data.result.length; i++) {
        if (response.data.result[i].date === todayDate) {
          if (response.data.result[i].category === '한식') {
            koreanData = response.data.result[i].food;
            console.log('한식 조회 성공');
          }
          else if (response.data.result[i].category === '일품') {
            goodData = response.data.result[i].food;
            console.log('일품 조회 성공');
          }
        }
        setState({
          ...state,
          koreanFood: koreanData,
          goodFood: goodData,
        });
        console.log('식단 조회 성공');
      }
    } else {
      console.log('식단 조회 실패입니다');
    }
  }
  Refresh();
}, []);

문제점 분석

  • useEffect hook으로 인해 컴포넌트가 생성(mount) 될 때마다 서버에 데이터를 요청하게 된다.
  • 좌측 위젯으로 모든 페이지에서 조회되기 때문에 페이지 이동 시 새로 생성되어 서버에 데이터를 요청하게 된다.

❌ 식단의 경우 하루 단위로 메뉴가 갱신된다. 변경사항이 적은 데이터를 불필요하게 계속 요청하게 된다.


해결 방법 (아직 식단 위젯 작업을 진행하지 않았기 때문에 상세 코드는 생략)

  • React-Query 라이브러리를 적용해 staleTime(만료) 옵션을 적용하면 staleTime 타임이 끝날 때까지 refetch가 되지 않는다.
  • staleTime 값을 긴 시간으로 설정하게 되면 하루가 지났을 때 학식이 갱신되지 않을 수 있다. 이를 방지하기 위해 새로고침 버튼을 배치해 수동으로 갱신 가능하도록 한다.


컴포넌트 의존성 주입(코드 개선)

  • mutate를 return 하는 hook을 JoinForm 컴포넌트에서 직접 생성하거나 찾았다.
  • JoinForm 컴포넌트가 구체적인 구현 의존성이 아닌 인터페이스에 대한 의존성으로 변경하는 작업을 통해 JoinForm 컴포넌트는 부모 컴포넌트에서 props로 hook을 주입받아 사용하게 된다.
  • 재사용성 측면에서 JoinForm 컴포넌트를 재사용 하고 싶을 때, 다른 훅 구현을 주입하여 외부에서 hook 로직이 변경되어도 JoinForm 컴포넌트는 영향을 받지 않고 사용할 수 있다.
/* JoinPage */

export default function JoinPage() {
  const reqAuthCode = useReqAuthCode();
  const verifyAuthCode = useVerifyAuthCode();
  const signUp = useSignUp();

  return (
    <Container>
      <Logo />
      <JoinForm
        reqAuthCode={reqAuthCode}
        verifyAuthCode={verifyAuthCode}
        signUp={signUp}
      />
    </Container>
  );
};

/* JoinPage > JoinForm */

export default function JoinForm({
  reqAuthCode,
  verifyAuthCode,
  signUp
}: JoinFormProps) {
  const [joinForm, setJoinForm] = useState<JoinState>({
    accountId: '',
    password: '',
    name: '',
    email: '',
    major: '',             // 학과
    status: '',            // 학적
    academicNumber: '',    // 학번
  });
  const [code, setCode] = useState<string>(''); // 인증 코드

  // 이메일 인증 코드 요청
  const handleSendAuthCode = () => {
    reqAuthCode(joinForm.email);
  };

  // 이메일 인증 코드 확인
  const handleVerifyAuthCode = () => {
    const data: VerifyAuthCode = {
      email: joinForm.email,
      code,
    };

    verifyAuthCode(data);
  };

  // 회원가입
  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();

    signUp(joinForm);
  };



마치며

프로젝트를 진행하면서 기존 작성했던 리팩토링 코드에서 또 리팩토링을 하게 되는 상황이 발생했다. 더 나은 코드를 작성하고 있다는 부분에서 안도감이 들지만 아직 갈 길이 멀다는 뜻이라고 생각한다.

초기 목표(중점사항)

  • 이미지, 폰트 최적화
  • 디렉터리 구조 정리
  • 프로젝트 배포
  • React-Query 적용
  • 코드 개선
  • UI 수정 및 추가

이미지, 폰트 최적화의 경우 관련 강의와 자료를 찾아봐야 하는 관계로 우선순위가 뒤로 밀릴 것 같고 현재 만들어진 API 연동 테스트를 진행하며 UI 작업을 진행할 계획이다.