eatthefrog

리엑트 1000줄 리팩토링 하기 본문

프론트엔드 노트

리엑트 1000줄 리팩토링 하기

eater_forg 2025. 11. 13. 12:45

1. 왜 1000줄이 되었을까

prop drilling 을 피하려다 보니, 모든 상태를 한 파일에 몰빵하게 되었고, 어느새 1000줄이 되어 버렸습니다..

 

그 결과, 하나의 컴포넌트가 세상의 모든 책임을 떠안게 되었습니다.
로딩 상태, 리스트 데이터, 모달 상태, 필터 값, 서버 응답, 클릭 이벤트, 스크롤 로직, API 호출, 토스트 메시지까지 — 전부 한 곳에.

처음엔 “관리 일원화”로 안심했지만, 곧 문제는 터졌습니다.

  • 가독성 최악
  • 함수 간 의존성이 꼬이며 사이드 이펙트 발생
  • 특정 상태의 변경 지점을 추적하기 어려움

결국 “prop drilling의 공포를 피하려다 상태 관리의 늪에 빠진 셈”이었습니다.


2. 의사결정: 전역 상태 vs 커스텀 훅

리팩토링의 핵심 질문은 명확했습니다.
“이 상태는 어디서 관리되어야 하는가?”

1. 전역 상태 관리(Context API / Redux)가 필요한 경우

  • 여러 트리 레벨에서 상태를 공유해야 할 때
  • 앱 전역에서 접근해야 하는 데이터가 있을 때
  • 복잡한 중앙 집중식 로직이 필요할 때

2. 커스텀 훅이 적합한 경우

  • 특정 화면이나 기능에만 국한된 상태
  • 로직 재사용이 필요한 경우
  • 테스트와 유지보수가 중요할 때
  • 전역 상태는 과하지만 props drilling은 피하고 싶을 때

우리의 상황은 하나의 화면에만 필요한 상태였기 때문에, 전역 관리 대신 커스텀 훅을 선택했습니다.


3. 리팩토링 전략: 점진적 접근

한 번에 모든 코드를 갈아엎는 것은 위험합니다.
그래서 다음 3단계로 나눠 점진적으로 진행했습니다.

 

1️⃣ 유틸리티 함수 분리 (낮은 위험도)

  • 순수 함수만 분리
  • 부작용 없음, 즉시 테스트 가능

2️⃣ 상태 관리 훅 분리 (중간 위험도)

  • 상태 로직만 추출
  • 기존 동작 유지, 점진적 이전 가능

3️⃣ 핸들러 훅 분리 (높은 위험도)

  • 복잡한 비즈니스 로직 포함
  • 의존성 관리가 중요, 충분한 테스트 필요

4. React 패턴: useCallback과 Props 그룹화

1. useCallback — 꼭 필요한 곳에만
자식 컴포넌트에 핸들러 함수를 넘길 때만 사용했습니다.
모든 함수에 남발하면 오히려 메모이제이션 비용이 증가하니까요.

const handleSubmit = useCallback(() => {
  console.log("Submit!");
}, []);


2. Props 그룹화 — 가독성 향상

Props가 20개를 넘어가자, 선언부만 봐도 혼란스러웠습니다.
그래서 관련 속성들을 의미 있는 그룹으로 묶었습니다.

<MyComponent
  state={{ value, loading }}
  handlers={{ onChange, onSubmit }}
/>

이렇게 하면 ReturnType<typeof useSomethingState>로 타입 추론이 자동 적용되어 타입 안정성과 유지보수성이 모두 개선됩니다.


5. 관심사 분리: 역할별 나누기

리팩토링의 진짜 핵심은 단순했습니다.
“한 파일이 한 역할만 하게 하자.”

역할 파일 내용

상태 관리 useSomethingState.ts 데이터 관리
비즈니스 로직 useSomethingHandlers.ts 사용자 액션 처리
유틸리티 utils/somethingUtils.ts 순수 함수
UI 렌더링 SomethingList.tsx 화면 구성
export const SomethingList = () => {
  const state = useSomethingState();
  const handlers = useSomethingHandlers(state);

  return (
    <div>
      {state.items.map(item => (
        <Item key={item.id} item={item} />
      ))}
      <button onClick={handlers.addItem}>Add</button>
    </div>
  );
};

6. 리팩토링 후 효과

항목 리팩토링 전 리팩토링 후 개선률

파일 크기 956줄 413줄 57% ↓
Props 개수 20개 9개 55% ↓
함수 복잡도 단일 250줄 함수 여러 함수로 분산 ✅ 개선

7. 실전 팁: 리팩토링 시 유의점

  • 타입 일관성 유지: ReturnType을 적극 활용
  • 점진적 적용: 한 번에 바꾸지 말고, 화면 단위로 이전
  • 의존성 관리: 커스텀 훅 간 순환 의존성 주의
  • useCallback 남용 금지: 진짜 필요한 곳에만