eatthefrog
리엑트 1000줄 리팩토링 하기 본문
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 남용 금지: 진짜 필요한 곳에만
'프론트엔드 노트' 카테고리의 다른 글
| 리팩토링: React AppNavigation 상태 관리 (0) | 2025.11.25 |
|---|---|
| React-Native - Firebase Analytics 도입하기 (0) | 2025.11.19 |
| 비동기 프로그래밍 입문: async, Promise, await 쉽게 이해하기 (0) | 2025.11.12 |
| FCM(Firebase Clouding Messageing) (0) | 2025.11.12 |
| JavaScript: ?? vs || 연산자 비교 (0) | 2025.11.11 |