10 KiB
10 KiB
Legacy React Codebase Refactoring
📌 Brief Summary
레거시 React 코드베이스 리팩토링은 기존 소프트웨어의 동작을 온전히 보존하면서, 코드의 가독성, 유지보수성, 확장성을 개선하기 위해 내부 구조를 재설계하는 과정입니다 [1]. 이 과정은 회귀 오류를 막기 위한 단위 테스트(Unit Test) 작성부터 시작하여, 클래스 기반 컴포넌트를 함수형 및 훅(Hooks)으로 전환하고, 상태 관리 도구를 현대화(예: Redux에서 TanStack Query나 Zustand로 마이그레이션)하는 작업을 포함합니다 [2, 3]. 성공적인 리팩토링은 전체를 한 번에 갈아엎는 재작성(Rewrite)을 피하고, 커스텀 훅을 통해 UI와 비즈니스 로직을 분리하는 등 점진적(Incremental)인 최적화를 수행하는 데 초점을 맞춥니다 [1, 4].
📖 Core Content
- 비즈니스 로직 파악 및 단위 테스트 선행: 레거시 리팩토링의 첫걸음은 애플리케이션의 비즈니스 로직과 전역 UI 스토어에 보관된 데이터를 파악하여 멘탈 모델(Mental model)을 그리는 것입니다 [5]. 코드를 수정하기 전 단위 테스트(Unit tests)나 UI 테스트 제품군을 먼저 작성하여, 리팩토링 과정(TS 변환, 훅 전환, 라이브러리 업그레이드 등)에서 기존 기능이 손상되지 않았음을 검증해야 합니다 [2, 6, 7].
- 모던 React 패러다임으로의 전환:
기존 코드베이스에 존재하는 클래스형 컴포넌트를 함수형 컴포넌트와 훅으로 마이그레이션합니다 [3]. 프로젝트가 JavaScript로만 이루어져 있다면 TypeScript로 점진적으로 전환하고, 오래되거나 사용 중단(deprecated)된 라이브러리를 업데이트합니다 [3]. 또한, 불필요한
useEffect사용을 모두 제거하고, 코드에 혼재되어 있는 CSS 스타일링 방식(외부 CSS, 인라인 style, sx 등)을 한두 가지의 표준 방식(예: CSS modules)으로 통일합니다 [3, 8, 9]. - 상태 관리 도구의 책임 분리: 과거 단일 전역 스토어(예: Redux)에 뭉쳐있던 상태들을 분리합니다. 서버 상태(Server state) 관리를 위해서는 TanStack Query를 추가하여 Redux 의존도를 줄이고, 클라이언트 측 전역 상태는 Context API나 Zustand로 관리하며, 지역 상태(Local state)는 가급적 각 컴포넌트 내부로 격리하는 것이 모범 사례입니다 [3].
- 점진적 마이그레이션(Incremental Migration)과 커스텀 훅: 리팩토링 시 애플리케이션 전체를 한 번에 재작성(rewrite)하는 것은 매우 위험합니다. "재작성이 아닌 리팩토링(refactor, do not rewrite)" 철학에 따라, 하나의 스토어나 단순한 기능(예: 알림)부터 시작해 결제 흐름과 같은 복잡한 도메인으로 하나씩 점진적 마이그레이션을 진행해야 합니다 [1]. 이 과정에서 '커스텀 훅'을 주요 리팩토링 단위로 사용하여, 거대한 컴포넌트에서 데이터 페칭이나 폼 처리와 같은 비즈니스 로직을 분리해 모듈성과 테스트 용이성을 높입니다 [4].
- 규칙 기반의 관리 및 자동화: ESLint(eslint-plugin-react, eslint-plugin-react-hooks 등)를 도입하여 코드 스타일과 모범 사례를 강제하고, 팀 차원의 일관된 코드 품질을 유지합니다 [10]. 더 나아가 Claude Code 같은 AI 도구를 활용해 코드베이스를 평가받고 작은 단위의 PR(Pull Request)을 생성하게 함으로써 리팩토링 시간을 단축할 수 있습니다 [11, 12].
⚖️ Trade-offs & Caveats
- 전면 재작성(Rewrite) vs 점진적 리팩토링: 기존 앱의 규모가 매우 작다면 오히려 바닥부터 새 앱을 작성하는 것이 리팩토링보다 쉽고 빠를 수 있습니다 [6]. 그러나 규모가 큰 앱의 경우 재작성은 리스크가 너무 크므로, 기능 개발을 멈추지 않은 상태에서 아키텍처를 현대화하는 '점진적 마이그레이션' 방식을 택해야 합니다 [1].
- 추상화와 단순성의 충돌 (DRY vs KISS): DRY(Don't Repeat Yourself) 원칙에 따라 중복 코드를 추출하는 것은 좋지만, 과도한 추상화는 원본 코드를 여러 번 반복하는 것보다 오히려 디버깅과 이해를 더 어렵게 만들어 KISS(Keep It Simple, Stupid) 원칙을 위배하게 될 부작용이 있습니다 [13]. 따라서 패턴이 최소 세 번 이상 반복되기 전까지는 조기 최적화나 복잡한 추상화를 피하는 것이 좋습니다 [13].
- 새로운 도구(TypeScript 등) 도입의 오버헤드: 타입스크립트를 추가하거나 새로운 상태 관리 패턴을 입히는 것은 안전성을 높이지만, 코드베이스에 복잡성을 더하고 팀원들에게 인지적 부하를 줍니다 [14]. 따라서 개발자들의 숙련도를 고려하여 JS 파일을 TS 파일로 한 개씩 점진적으로 변환하는 식의 접근이 권장됩니다 [14].
🔗 Knowledge Connections
Related Concepts
[관계 유형 A (아키텍처/기반 기술)]
- Feature-Sliced Design
- 연결 이유: 레거시 코드의 난해한 폴더 구조를 해결하기 위해, 코드를 기술적 계층이 아닌 비즈니스 기능(Feature)과 책임 범위에 따라 분할하는 최신 아키텍처 방법론이기 때문입니다 [15, 16].
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: 거대하고 강하게 결합된 컴포넌트들을 응집도 높은 모듈로 쪼개는 기준과 단방향 의존성 구조를 설계하는 방법 [16].
- SOLID Principles
- 연결 이유: 단일 책임 원칙(SRP) 등은 복잡하게 얽힌 컴포넌트 로직을 더 작고 한 가지 일만 하는 컴포넌트로 리팩토링하는 데 핵심적인 가이드라인을 제공합니다 [17].
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: 거대한 컴포넌트가 언제, 그리고 왜 훅과 하위 컴포넌트로 분리되어야 하는지에 대한 객관적 판단 기준 [11, 17].
[관계 유형 B (구현/활용 도구)]
- Unit Testing
- 연결 이유: 코드를 리팩토링할 때 기존 로직이 망가지지 않았음을 보장하기 위해 선행되어야 하는 가장 중요한 실무 도구이기 때문입니다 [2, 7].
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: 코드베이스를 안전하게 해체하고 모듈화하기 위한 테스트 기반 접근법과 회귀 버그 방어 전략 [7].
- Zustand
- 연결 이유: 기존 레거시 앱에서 흔히 발생하는 Context API의 불필요한 렌더링 문제나, 무겁고 복잡한 Redux 코드를 리팩토링할 때 가장 권장되는 경량 상태 관리 라이브러리이기 때문입니다 [3, 18, 19].
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: 상태를 분리하고 셀렉터(Selector) 패턴을 적용하여 글로벌 상태 변경 시 발생하는 성능 병목을 해결하는 원리 [20, 21].
Deeper Research Questions
- 레거시 클래스형 컴포넌트의 라이프사이클 메서드(
componentDidMount,componentDidUpdate등)를 함수형 컴포넌트의 훅(useEffect등)으로 변환할 때, 논리적 오류 없이 1:1로 매핑하거나 구조를 개선하는 구체적인 패턴은 무엇인가? - 점진적 마이그레이션을 진행할 때, 레거시 시스템의 Redux 스토어와 새로운 시스템의 Zustand/TanStack Query 상태를 시스템 장애나 충돌 없이 공존시키는 방법은 무엇인가?
- UI 리팩토링 과정에서 레이아웃이나 색상 등 의도치 않은 시각적 회귀(Visual regression)를 방지하기 위해 Storybook 및 Chromatic과 같은 도구를 어떻게 CI 파이프라인에 통합할 수 있는가?
- FSD(Feature-Sliced Design) 아키텍처를 기존 레거시 코드베이스에 적용하려 할 때, 여러 기능(Feature) 간에 교차하여 사용되는 횡단 관심사(Cross-cutting concerns)는 어디에 어떻게 배치해야 하는가?
- React 코드베이스에서
useEffect의 오용이 대표적인 안티패턴으로 꼽히는 이유는 무엇이며, 이를 식별해 내고 대체 패턴(파생 상태, 이벤트 핸들러 등)으로 리팩토링하는 기준은 무엇인가?
Practical Application Contexts
- Implementation: 거대한 단일 파일로 작성된 컴포넌트(300줄 이상)나 혼재된 스타일(인라인, 외부 파일)을 가진 레거시 코드를 식별한 후, ESLint 린터를 추가해 규칙을 세우고 공통 로직을 커스텀 훅으로 분리하는 작업에 즉시 적용할 수 있습니다.
- System Design: 프로젝트의 폴더 구조를 기존의 기술 중심(components, hooks, styles) 구조에서 도메인 및 기능 중심(features 내부에 각 도메인 배치) 구조로 재편성하여 확장성을 갖춘 아키텍처로 탈바꿈시킵니다.
- Operation / Maintenance: 기능 배포를 중단하지 않고 운영 중인 시스템에서 "한 번에 하나의 스토어" 단위로 코드를 점진적으로 리팩토링하며, 변경 사항에 대해 CI/CD 환경에서 테스트 통과 여부를 검증합니다.
- Learning Path: React 기초 장난감 앱(Toy app)을 직접 만들어 생태계를 이해한 뒤, SOLID 및 DRY 원칙을 학습하고, 커스텀 훅과 테스트 작성법을 익힌 후 실제 레거시 코드를 개선하는 방향으로 학습 로드맵을 설계합니다.
- My Project Relevance: 다른 개발자들이 작성한 오래된 코드를 인수받아 유지보수해야 할 때, 코드를 무작정 뜯어고치는 대신 비즈니스 로직을 우선 매핑하고 단위 테스트를 짜며 AI 도구의 도움을 받아 점진적으로 구조를 정돈하는 실무 지침으로 활용할 수 있습니다.
Adjacent Topics
- TanStack Query (React Query)
- 확장 방향: 기존의 전역 상태 관리 도구(Redux)에서 관리하던 '서버 상태'를 떼어내어, 어떻게 효율적으로 데이터 페칭(fetching), 캐싱, 그리고 동기화 처리를 자동화할 수 있는지 탐구합니다.
- React Performance Optimization
- 확장 방향: 코드를 리팩토링하는 과정에서
React.memo,useMemo,useCallback과 같은 메모이제이션 기법을 적재적소에 배치해 컴포넌트의 렌더링 성능을 극대화하는 방안으로 이해를 넓힙니다.
- 확장 방향: 코드를 리팩토링하는 과정에서
Last updated: 2026-04-30