Files
2nd/00_Raw/대규모 React 애플리케이션 아키텍처 구성.md
T

79 lines
12 KiB
Markdown

# [[대규모 React 애플리케이션 아키텍처 구성]]
## 📌 Brief 소스 Summary
대규모 React 애플리케이션 아키텍처 구성은 코드, 상태 관리, 비즈니스 로직이 UI 컴포넌트로 유출되는 것을 막고 예측 가능한 성장을 가능하게 하는 엄격한 설계 방법론을 의미합니다 [1, 2]. 프로젝트가 커짐에 따라 단순히 파일 유형별(컴포넌트, 훅 등)로 폴더를 나누는 방식 대신, 기능(Feature)이나 도메인 중심으로 코드를 조직하는 방식이 필수적입니다 [3, 4]. 성공적인 아키텍처는 단방향 의존성 규칙을 강제하는 Feature-Sliced Design(FSD)과 같은 패턴을 채택하고, SOLID 원칙에 입각한 클린 코드 작성, 그리고 확장 가능한 전역 상태 관리 전략을 결합하여 개발팀의 협업 효율성과 유지보수성을 극대화합니다 [5-7].
## 📖 Core Content
* **기능(Feature) 기반 모듈화 및 Feature-Sliced Design (FSD):**
* 과거의 React 앱은 컴포넌트, 훅, 스타일 등 기술적 역할에 따라 폴더를 분리(Type-Based)했지만, 이는 앱이 확장될수록 비즈니스 로직이 파편화되는 문제를 낳았습니다 [3, 8]. 대규모 앱에서는 도메인 기능별로 코드를 구성하는 특징 기반(Feature-based) 폴더 구조를 사용해야 합니다 [4, 9].
* 이를 체계화한 방법론이 Feature-Sliced Design(FSD)입니다 [10]. FSD는 코드의 스코프와 책임에 따라 `app`, `pages`, `widgets`, `features`, `entities`, `shared`라는 고정된 레이어로 구성됩니다 [5].
* 가장 중요한 규칙은 단방향 의존성(Unidirectional dependencies)으로, 상위 레이어는 하위 레이어를 참조할 수 있지만 반대는 불가능하여 순환 참조를 방지합니다 [5, 11]. 또한 각 슬라이스는 `index.ts`를 통해서만 외부로 노출되는 Public API 규칙을 따라야 캡슐화가 보장됩니다 [7, 12].
* **컴포넌트 설계와 클린 코드 원칙:**
* React의 함수형 컴포넌트에도 SOLID 원칙이 적용됩니다. 단일 책임 원칙(SRP)에 따라 하나의 컴포넌트는 한 가지 일만 해야 하며, 300줄이 넘어가는 컴포넌트는 더 작은 단위로 분리해야 합니다 [13, 14].
* KISS 원칙에 따라 복잡성보다 단순함을 추구하되, DRY 원칙을 지키기 위해 중복되는 로직은 커스텀 훅으로 추출합니다 [15, 16]. 단, 너무 이른 추상화는 코드를 더 읽기 어렵게 만들 수 있으므로 패턴이 3번 이상 반복될 때 추상화하는 것이 좋습니다 [15].
* **확장 가능한 상태 관리 전략:**
* 상태는 로컬 UI 상태, 전역 애플리케이션 상태, 서버 캐시 상태, URL 상태로 엄격히 파편화하여 관리해야 합니다 [17, 18]. 모든 데이터를 Redux와 같은 단일 스토어에 넣는 과거 방식에서 벗어나, 서버 데이터는 TanStack Query를 통해 캐싱하고 UI 상태는 Zustand 등을 사용합니다 [17, 18].
* React의 내장 Context API는 값이 바뀔 때마다 해당 컨텍스트를 구독하는 모든 컴포넌트가 리렌더링되는 문제(Re-render storm)가 있어 빈번하게 변하는 상태에는 적합하지 않습니다 [19, 20].
* 대규모 팀(10명 이상)이나 복잡한 비동기 작업이 많은 환경에서는 일관된 패턴을 강제하는 Redux(RTK)가 여전히 강력하며, 중소규모 팀에서는 보일러플레이트가 적고 셀렉터(Selector)를 통해 불필요한 렌더링을 막아주는 Zustand가 적합합니다 [6, 21, 22].
* **일관된 명명 규칙(Naming Conventions):**
* 운영체제(Windows, macOS, Linux) 간 대소문자 구분 차이로 인한 CI/CD 빌드 실패를 방지하기 위해 파일 및 폴더 이름은 주로 `kebab-case`를 사용합니다 [23-25].
* React 컴포넌트는 `PascalCase`로 작성하며, 변수나 함수, 커스텀 훅은 `camelCase`를 사용합니다 [26, 27].
* **성능 최적화와 안정성:**
* 대규모 번들 사이즈를 줄이기 위해 Vite의 `manualChunks`를 이용한 벤더 코드 스플리팅과, `React.lazy` 및 Suspense를 활용한 라우트 기반 코드 스플리팅이 필수적입니다 [28-32].
* 전체 애플리케이션이 충돌하는 것을 방지하기 위해 Error Boundary를 위젯이나 기능 단위로 선별적으로 배치하여 안전한 Fallback UI를 제공해야 합니다 [33, 34].
## ⚖️ Trade-offs & Caveats
* **FSD 등 엄격한 아키텍처 도입의 오버헤드:** Feature-Sliced Design은 구조적 안정성을 주지만 진입 장벽이 높습니다. 개발자는 특정 모듈이 'feature'에 속하는지 'widget'에 속하는지 분류하는 의미론적 고민에 많은 시간을 쓸 수 있습니다 [35]. 무작정 초기부터 쪼개기 시작하면 3개면 충분했을 슬라이스가 수백 개로 불어나는 과잉 엔지니어링이 발생할 수 있습니다 [36].
* **Barrel Files(`index.ts`)의 한계:** FSD에서 캡슐화를 위해 권장하는 Barrel 파일 패턴은 내부 리팩토링을 쉽게 만들지만, 번들링 과정에서 트리 쉐이킹(Tree-shaking) 성능을 떨어뜨리거나 순환 참조 디버깅을 어렵게 만드는 단점이 존재합니다 [35].
* **상태 관리 도구의 딜레마:** Context API는 서드파티 종속성이 없는('Zero cost') 장점이 있지만 성능 저하의 주범이 될 수 있습니다 [19, 37]. 반면 Zustand는 유연하고 가벼우나 명확한 컨벤션을 강제하지 않아 대규모 팀에서는 코드가 중구난방(Store soup)이 될 위험이 있습니다 [21, 38]. Redux는 디버깅(Time-travel)과 팀 컨벤션 통일에 탁월하지만 보일러플레이트로 인한 개발 속도 저하를 감수해야 합니다 [6, 39, 40].
* **메모이제이션의 비용:** `React.memo`, `useCallback`, `useMemo`를 사용한 렌더링 최적화는 공짜가 아닙니다. 이전 props와 새로운 props를 비교하는 과정 자체에 비용이 들며, 컴포넌트 렌더링 속도보다 비교 연산이 더 오래 걸리거나 얕은 비교(Shallow comparison)의 한계로 인해 오히려 성능이 악화되는 부작용이 발생할 수 있습니다 [41-43]. React Compiler가 이 과정을 자동화해 주지만, 블랙박스로 동작하여 성능 디버깅이 더 어려워지며 일부 라이브러리(불안정한 참조를 반환하는 훅 등)와의 호환성 문제가 생깁니다 [44, 45].
## 🔗 Knowledge Connections
### Related Concepts
#### [아키텍처/기반 기술]
- [[Feature-Sliced Design]]
- 연결 이유: 대규모 React 애플리케이션의 복잡성을 관리하기 위한 현대적인 프론트엔드 아키텍처 방법론입니다 [10, 46].
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: 모듈의 스코프를 나누는 방법, 단방향 의존성 규칙, 그리고 컴포넌트를 비즈니스 도메인 단위로 캡슐화하는 원리를 이해할 수 있습니다 [5, 11].
- [[SOLID 원칙]]
- 연결 이유: OOP의 원칙이지만 React 함수형 프로그래밍에 맞게 변형 적용되어 컴포넌트의 책임과 분리 기준을 제시합니다 [7, 47].
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: 거대한 컴포넌트를 쪼개는 기준(SRP)과 불필요한 prop 전달을 피하는 방법(ISP) 등을 학습할 수 있습니다 [13, 48].
#### [구현/활용 도구]
- [[Zustand]]
- 연결 이유: Context API의 리렌더링 문제를 해결하면서도 Redux보다 낮은 도입 비용으로 전역 상태를 관리할 수 있는 도구입니다 [39, 49].
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: Selector 패턴을 통해 컴포넌트가 구독하는 상태 슬라이스만 변경될 때 렌더링을 트리거하는 성능 최적화 메커니즘을 배울 수 있습니다 [18, 22].
- [[TanStack Query]]
- 연결 이유: UI 앱 상태와 서버 데이터 상태를 분리하는 모던 프론트엔드 아키텍처의 핵심 도구입니다 [18, 50].
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: 데이터 캐싱, 중복 요청 제거, 무한 스크롤 및 낙관적 업데이트 등 비동기 서버 상태를 별도로 관리하는 전략을 이해할 수 있습니다 [18, 50].
- [[Code Splitting]]
- 연결 이유: 거대한 JavaScript 번들로 인해 초기 로딩 속도가 느려지는 문제를 해결하는 핵심 성능 최적화 기법입니다 [29, 51].
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: `React.lazy`와 Suspense를 통한 라우트별 로딩 처리 및 Vite의 `manualChunks`를 활용한 벤더 라이브러리 캐싱 전략을 알 수 있습니다 [30-32].
### Deeper Research Questions
- Feature-Sliced Design(FSD)에서 Feature와 Widget의 경계를 결정하는 명확한 기준은 무엇이며, 인증(Auth)과 같이 여러 도메인에 걸친 교차 절단 관심사(Cross-cutting concerns)는 어느 레이어에 배치해야 하는가?
- React의 내장 Context API가 야기하는 불필요한 리렌더링 문제를 Zustand의 셀렉터(Selector) 패턴은 내부적으로 어떻게 감지하고 해결하는가?
- React Compiler가 도입됨에 따라 기존의 수동 메모이제이션(`useMemo`, `useCallback`, `React.memo`)에 의존하던 성능 최적화 패러다임은 어떻게 변화하며, 레거시 프로젝트 리팩토링 시 어떤 제약 사항이 있는가?
- 10명 이상의 대규모 개발팀에서 상태 관리 아키텍처(Zustand vs Redux)를 선택할 때, 애플리케이션의 비동기 처리 복잡도와 디버깅 요구사항은 어떠한 영향을 미치는가?
- Vite 기반의 번들링 환경에서 `manualChunks`를 활용한 코드 분할 시, 초기 렌더링 성능(Core Web Vitals)에 미치는 구체적인 영향과 캐싱 전략은 무엇인가?
### Practical Application Contexts
- **Implementation:** 거대한 `components/` 폴더 대신 `features/auth/`, `features/checkout/`과 같이 폴더를 구성하고, 해당 기능에 필요한 UI 컴포넌트, API 호출 훅, 타입을 폴더 내부에 캡슐화하여 구현합니다.
- **System Design:** 애플리케이션 계층을 `app`, `pages`, `features`, `entities`, `shared`로 나누고, 상위 계층만 하위 계층을 import 할 수 있도록 ESLint 규칙을 설정하여 순환 참조를 방지하는 시스템을 설계합니다.
- **Operation / Maintenance:** 프로덕션 환경에서는 Error Boundary를 폼이나 서드파티 위젯 등에 씌워 특정 기능이 다운되어도 전체 화면이 백화되는 것을 막고, Sentry나 LogRocket과 연동하여 세션 리플레이 및 에러 로그를 수집합니다.
- **Learning Path:** 소규모 프로젝트에서 React Context API로 시작하여 한계(리렌더링 폭탄)를 경험한 후, Zustand를 학습하고, 궁극적으로 복잡한 비즈니스 요건을 다룰 때 Redux와 구조적 패턴, 그리고 FSD를 도입하는 방향으로 아키텍처 학습을 진행합니다.
- **My Project Relevance:** 코드베이스가 거대해져 컴포넌트와 훅을 찾기 어려워졌을 때, 기존의 기술 스택 중심 폴더를 기능(Feature) 도메인 구조로 리팩토링하고 `index.ts`를 활용하여 모듈 간 강결합을 끊어낼 수 있습니다.
### Adjacent Topics
- [[Micro-Frontends]]
- 확장 방향: 단일 페이지 애플리케이션(SPA)의 규모가 지나치게 커졌을 때, 도메인별로 완전히 독립된 팀 단위 저장소와 배포 파이프라인을 구축하는 엔터프라이즈급 확장 아키텍처로 연구를 진행할 수 있습니다.
- [[React Compiler]]
- 확장 방향: 구조적 아키텍처를 넘어, 개발자가 수동으로 적용하던 렌더링 최적화 로직을 빌드 타임에 컴파일러가 어떻게 자동 캐싱(Memoization)하는지에 대한 동작 원리와 한계점을 탐구할 수 있습니다.
---
*Last updated: 2026-04-30*