Files
2nd/10_Wiki/Topics/Frontend/Large-scale Application Refactoring.md
T

10 KiB

Large-scale Application Refactoring

📌 Brief Summary

대규모 애플리케이션 리팩토링은 코드의 동작 방식을 보존하면서 내부 구조를 개선하여 오래된 코드베이스의 유지보수성과 확장성을 회복하는 과정이다 [1]. 이는 단순히 코드를 '수정'하는 것이 아니라, 복잡한 비즈니스 로직을 분리하고 구조적 결합도를 낮추는 것을 목표로 한다 [2]. 성공적인 리팩토링을 위해서는 점진적인 접근 방식, 엄격한 아키텍처 적용, 그리고 코드 변경을 뒷받침할 수 있는 테스트 구축이 필수적이다 [1, 3].

📖 Core Content

  • 점진적 마이그레이션 전략 (Incremental Migration): 대규모 애플리케이션을 한 번에 전면 재작성(Rewrite)하는 것은 리스크가 매우 크기 때문에, "재작성이 아닌 리팩토링" 전략이 권장된다 [1]. 예를 들어 상태 관리 도구를 Context API에서 Zustand로 마이그레이션할 때, 알림과 같은 단순한 유틸리티 스토어부터 시작해 결제 흐름과 같은 복잡한 도메인으로 한 번에 하나의 스토어씩 점진적으로 이동해야 한다 [1].
  • 기능 및 도메인 기반 구조로의 개편: 레거시 앱에서 흔히 쓰이는 파일 타입 기반 구조(components, hooks 등을 따로 모으는 방식)는 앱이 커질수록 탐색과 유지보수를 어렵게 만든다 [4, 5]. 따라서 비즈니스 기능별로 코드를 모으는 기능 기반 구조나, 단방향 의존성을 강제하는 엄격한 계층 모델인 Feature-Sliced Design(FSD)으로 폴더 구조를 재편하는 것이 핵심적인 리팩토링 목표가 된다 [6-8].
  • 커스텀 훅을 통한 로직 캡슐화: 현대 React 리팩토링의 기본 단위는 커스텀 훅이다 [9]. 복잡한 데이터 페칭이나 폼 핸들링 로직을 거대한 UI 컴포넌트에서 추출하여 useFetch, useForm 등의 훅으로 분리하면, UI와 비즈니스 로직이 격리되어 더 빠르고 독립적인 유닛 테스트가 가능해진다 [9, 10].
  • 테스트를 통한 안전망 확보: 코드를 본격적으로 수정하기 전에 테스트(Unit Test, UI Test 등)를 작성하는 것이 최우선 방어선이다 [3, 11, 12]. 기존 기능이 깨지지 않았는지 검증할 뿐만 아니라, 테스트 코드를 작성하는 과정 자체가 개발자가 기존 애플리케이션의 비즈니스 로직과 흐름을 깊이 이해하도록 강제하는 학습 도구가 된다 [13, 14].
  • 레거시 안티패턴 및 스택 제거: 효율적인 구조를 위해 불필요하게 렌더링을 유발하는 다수의 useEffect를 제거하고, 클라이언트와 서버 상태를 분리하기 위해 TanStack Query와 같은 도구를 도입해야 한다 [15]. 또한, 가능할 경우 클래스 기반 컴포넌트를 함수형 컴포넌트와 훅으로 변환하고, 일관성 없는 CSS 적용 방식을 하나로 통일하는 작업도 수반된다 [15, 16].

⚖️ Trade-offs & Caveats

  • DRY와 KISS 원칙의 충돌: 중복을 제거하려는 DRY(Don't Repeat Yourself) 원칙을 과도하게 적용할 경우, 추상화가 지나치게 복잡해져 코드를 단순하게 유지해야 하는 KISS(Keep It Simple, Stupid) 원칙을 위반하게 된다 [17]. 따라서 특정 패턴이 세 번 반복될 때까지 기다렸다가 추상화를 진행하는 것이 조기 최적화로 인한 부작용을 막는 방법이다 [17].
  • 재작성(Rewrite) vs 리팩토링(Refactoring)의 기로: 리팩토링 대상인 앱의 규모가 비교적 작다면 처음부터 새로 앱을 구축하는 것이 오히려 효율적일 수 있다 [11]. 그러나 대형 앱의 경우 전체 재작성은 위험이 커서 점진적 마이그레이션을 해야 하는데, 이 경우 전환 기간 동안 두 가지 다른 기술이나 아키텍처 패턴이 공존해야 하는 과도기적 기술 부채를 감당해야 한다 [1].
  • 컴파일러 자동화 도입의 장벽: React Compiler와 같이 성능 최적화(메모이제이션)를 자동화해 주는 도구를 도입하면 수동 최적화 코드를 지워 코드를 간결하게 만들 수 있다 [18]. 하지만 기술 부채가 많은 레거시 코드베이스의 경우, 기존 코드가 'React의 규칙(Rules of React)'을 광범위하게 위반하고 있다면 컴파일러가 제대로 작동하지 않으므로, 도입 전 대대적인 사전 리팩토링이 선행되어야 하는 제약이 따른다 [19].
  • 공유(Shared) 모듈의 비대화: 기능 기반 아키텍처(예: FSD)로 분리할 때, 공통으로 쓰이는 코드를 무분별하게 'Shared' 계층에 넣으면 해당 계층이 복잡한 스파게티 코드가 되고 변경 시 영향 범위(Blast Radius)가 기하급수적으로 커지는 위험이 있다 [20, 21].

🔗 Knowledge Connections

[아키텍처 및 기반 원칙]

  • Feature-Sliced Design

    • 연결 이유: 대규모 코드베이스의 스파게티화를 해결하고, 도메인/기능 중심의 단방향 의존성 규칙을 부여하여 확장 가능한 구조를 만드는 리팩토링의 궁극적 목표 모델이기 때문이다 [7, 22].
    • 이 개념을 통해 더 깊게 이해할 수 있는 부분: 기능(Feature)과 계층(Layer)을 어떻게 나누고 캡슐화하여 서로 간의 의존성 결합을 끊어내는지에 대한 실무적 아키텍처 구조 [6, 23].
  • SOLID Principles

    • 연결 이유: 단일 책임 원칙(SRP) 등을 통해 거대한 컴포넌트가 가지는 여러 책임을 분리하고, 함수나 컴포넌트를 테스트 가능하게 잘게 쪼개는 리팩토링의 핵심 이론적 배경이기 때문이다 [24, 25].
    • 이 개념을 통해 더 깊게 이해할 수 있는 부분: 기능적 컴포넌트 내에서 인터페이스(Props)를 어떻게 분리하고, 확장에 열려있으면서 수정에는 닫힌 코드 작성을 구현하는 방법 [25, 26].

[구현 및 활용 도구]

  • Unit Testing

    • 연결 이유: 레거시 코드 구조를 변경할 때 기능이 망가지지 않았음을 보장하는 첫 번째 단계이자 가장 중요한 안전망 역할을 수행하기 때문이다 [3, 12].
    • 이 개념을 통해 더 깊게 이해할 수 있는 부분: 코드를 어떻게 더 작고 논리적인 블록 단위로 나누어(Triangulation) 의존성 없이 독립적으로 검증할 수 있는지에 대한 방법론 [9, 12].
  • Custom Hooks

    • 연결 이유: 리액트 컴포넌트 내부에 복잡하게 얽힌 상태와 사이드 이펙트 로직을 외부로 추출하는 리팩토링의 주된 단위이자 도구이기 때문이다 [9].
    • 이 개념을 통해 더 깊게 이해할 수 있는 부분: UI 렌더링 책임과 비즈니스 데이터 처리 책임을 어떻게 물리적으로 격리하여 코드 재사용성을 높일 수 있는지의 원리 [9, 10].

Deeper Research Questions

  • 리팩토링 과정에서 Feature-Sliced Design(FSD) 아키텍처를 도입할 때, 여러 기능(Feature)에서 동시에 요구되는 교차 관심사(Cross-cutting concerns) 로직은 어떤 방식으로 분리하여 관리해야 하는가?
  • 테스트 코드가 전무한 거대한 레거시 React 앱을 리팩토링할 때, 어떤 부분(예: 공통 유틸, UI 컴포넌트, 전역 상태 등)부터 우선순위를 두고 테스트 커버리지를 확보해야 하는가?
  • 대규모 애플리케이션의 Context API 기반 전역 상태를 Zustand 등 현대적 상태 관리 도구로 점진적으로 마이그레이션할 때 발생하는 상태 동기화 문제는 어떻게 해결할 수 있는가?
  • 기존의 수동 메모이제이션(useMemo, useCallback) 코드가 많은 레거시 앱에 React Compiler를 도입하기 위해 코드 내의 'Rules of React' 위반 사항을 추적하고 검증하는 효과적인 자동화 프로세스는 무엇인가?
  • 클래스형 컴포넌트를 함수형 컴포넌트와 커스텀 훅 구조로 리팩토링할 때 발생할 수 있는 메모리 누수(Memory Leaks) 패턴을 감지하고 방지하는 디버깅 전략론은 무엇인가?

Practical Application Contexts

  • Implementation: 거대한 폼 처리나 API 페칭 로직이 뷰와 결합하여 300줄이 넘어가는 기존 컴포넌트를, 단일 책임 원칙(SRP)에 따라 순수 뷰 컴포넌트와 비즈니스 로직을 담은 커스텀 훅으로 추출하여 다시 연결한다 [9, 25].
  • System Design: 폴더 구조를 components/, hooks/ 같은 파일 유형 구분이 아닌, 비즈니스 도메인(예: 인증, 결제)을 중심으로 모은 기능 기반 혹은 FSD 기반 폴더 계층 구조로 전면 재설계한다 [8, 27].
  • Operation / Maintenance: ESLint와 Husky 등의 도구를 파이프라인에 구축하여 리팩토링된 코드가 상위 계층을 잘못 참조하는 역의존성(Reverse dependency)을 발생시키거나 코드 컨벤션이 어긋나지 않도록 엄격히 통제한다 [28].
  • Learning Path: 리팩토링해야 할 코드를 파악하기 전, 먼저 작은 빈 프로젝트나 '토이 앱'을 만들어 리팩토링에 도입할 새로운 기술(React의 최신 기능 등)의 기초를 실습하여 개념을 확립한다 [29].
  • My Project Relevance: 다른 개발자들이 작성한 레거시 코드를 인계받아 논문 프로젝트용으로 리팩토링해야 하는 경우, 먼저 기존 로직을 파악하기 위한 유닛 테스트를 작성한 후, 무분별하게 혼용된 CSS 스타일 정책을 하나로 통일시키고 불필요한 전역 상태를 지역 상태로 전환하는 실무 프로세스를 진행한다 [14, 16, 30, 31].

Adjacent Topics

  • Web Performance Optimization
    • 확장 방향: 리팩토링 작업과 병행하여 번들 사이즈 감소(코드 스플리팅), 리렌더링 최적화, 불필요한 렌더 블로킹 제거 등을 통해 애플리케이션의 런타임 및 로딩 속도를 향상하는 전략적 기법을 탐구한다.
  • State Management Fragmentation
    • 확장 방향: 레거시 앱의 거대한 단일 전역 상태를 분석하여 로컬 컴포넌트 상태, 전역 UI 상태, 서버 캐시 상태, URL 상태 등으로 파편화 및 전문화하여 각각에 맞는 도구(Zustand, React Query 등)로 이관하는 설계 방법론을 조사한다.

Last updated: 2026-04-30