React 아키텍처 설계 가이드
React 프로젝트는 작을 때는 빠르게 움직이지만, 규모가 커질수록 코드보다 구조가 속도를 결정합니다. 컴포넌트가 너무 많아도 문제고, 상태가 여기저기 흩어져도 문제며, 서버 데이터와 로컬 UI 상태가 섞이면 유지보수 비용이 빠르게 올라갑니다. 그래서 React 아키텍처의 핵심은 “컴포넌트를 어떻게 짤까”보다 “무엇을 어떤 경계로 나눌까”입니다.
아키텍처 그림 설명
[Route / Screen]
|
v
[Feature Modules]
Auth / Dashboard / Orders
|
+---+------------------------+
| |
v v
[Hooks / Domain Logic] [UI Components]
| |
v v
[Server State Layer] [Design System]
|
v
[API / BFF / Backend]
React 아키텍처에서 중요한 것은 컴포넌트 트리와 책임 트리를 일치시키지 않는 것입니다. 화면은 기능 모듈을 조합하고, 기능 모듈은 훅과 서버 상태 계층을 통해 데이터를 다루며, 디자인 시스템은 표현 규칙을 분리합니다. 이렇게 해야 UI 구조와 데이터 구조가 서로를 끌어내리지 않습니다.
React 아키텍처를 보는 핵심 질문
좋은 React 아키텍처는 보통 다음 질문에 명확히 답합니다.
- 이 컴포넌트는 표현을 담당하는가, 흐름을 담당하는가
- 이 상태는 로컬 UI 상태인가, 공유 상태인가, 서버 상태인가
- 이 로직은 컴포넌트 안에 둘 것인가, custom hook으로 뺄 것인가
- 이 페이지는 CSR 중심인가, SSR/RSC가 더 적합한가
- 팀이 구조를 예측 가능하게 읽을 수 있는가
이 질문에 답이 없는 프로젝트는 도구가 좋아도 결국 복잡도가 쌓입니다.
컴포넌트는 표현과 조합으로 나누는 편이 좋다
React에서는 컴포넌트를 재사용 가능한 표현 단위와 화면 단위 조합 계층으로 나누면 구조가 선명해집니다.
- presentational component: 버튼, 카드, 리스트 아이템처럼 UI 표현 중심
- container 또는 page component: 데이터 로딩, 권한 분기, 화면 조합 중심
- custom hook: 상태와 부수효과 조합 중심
모든 컴포넌트가 다 데이터를 가져오고, 상태를 관리하고, 렌더링까지 같이 하게 되면 나중에 테스트와 리팩터링이 어려워집니다.
상태는 한 덩어리가 아니라 계층이다
React 프로젝트에서 가장 많이 헷갈리는 것은 상태를 어디에 두느냐입니다. 보통은 아래처럼 나누는 편이 자연스럽습니다.
- 로컬 상태: 입력값, 모달 열림, 임시 선택값
- 공유 클라이언트 상태: 인증, 테마, 전역 필터, 사용자 설정
- 서버 상태: API에서 가져오는 목록, 상세, 캐시 가능한 데이터
- URL 상태: 검색어, 정렬, 페이지 번호처럼 링크 공유가 필요한 값
이 구분을 하지 않으면 Context에 너무 많은 데이터가 들어가거나, 반대로 props drilling이 심해집니다. 특히 서버 상태를 전역 상태처럼 관리하려 하면 캐시와 동기화 문제가 빠르게 커집니다.
서버 상태는 별도 계층으로 보는 것이 좋다
React에서 TanStack Query 같은 도구가 중요한 이유는 서버 상태가 단순한 useState 문제가 아니기 때문입니다. 서버 상태에는 다음이 함께 따라옵니다.
- 캐싱
- 재요청 시점
- stale 판단
- 에러와 재시도
- optimistic update
- background refresh
이 요구를 컴포넌트마다 직접 구현하면 구조가 금방 무거워집니다. 그래서 React 아키텍처에서는 서버 상태를 별도 계층으로 생각하는 것이 중요합니다.
Custom Hook은 로직의 경계를 만든다
React에서 구조를 깔끔하게 만드는 데 가장 큰 도구 중 하나가 custom hook입니다. 하지만 모든 로직을 hook으로 뽑는 것이 좋은 설계는 아닙니다. custom hook은 재사용성과 책임이 명확할 때 가장 빛납니다.
예를 들어 다음은 분리 가치가 높습니다.
- 인증 세션 흐름
- 검색 필터 상태 조합
- 무한 스크롤 로직
- 폼 제출과 검증 흐름
- URL 상태 동기화
반대로 한 화면에서만 쓰이고 문맥 의존적인 로직은 페이지 안에 두는 편이 더 읽기 쉬울 수 있습니다.
React 아키텍처에서 렌더링 모델은 구조 결정에 직접 연결된다
현대 React는 CSR만 생각해서는 부족합니다. 특히 Next.js 계열에서는 다음 렌더링 모델이 구조에 직접 영향을 줍니다.
- CSR: 클라이언트 중심 인터랙션이 많을 때
- SSR: 초기 표시와 SEO가 중요할 때
- SSG: 변경 빈도가 낮고 빠른 응답이 중요할 때
- RSC: 서버에서 처리 가능한 부분을 분리해 클라이언트 비용을 줄일 때
어떤 렌더링 모델을 기본값으로 잡느냐에 따라 데이터 로딩 위치, 컴포넌트 경계, 캐시 전략, 번들 크기가 달라집니다.
폴더 구조는 팀이 읽는 방식에 맞춰야 한다
React 프로젝트에서 흔히 쓰는 구조는 두 가지입니다.
- 타입별 구조:
components,hooks,pages,services - 기능별 구조:
features/auth,features/posts,features/dashboard
작을 때는 타입별 구조가 단순하지만, 규모가 커지면 기능별 구조가 협업에 더 유리한 경우가 많습니다. 중요한 것은 어떤 방식을 택하든 공통 UI, 도메인 로직, API 계층, 테스트 위치가 일관되게 보이는가입니다.
공통 UI와 기능 UI를 섞지 않는다
디자인 시스템 성격의 버튼, 인풋, 모달, 토스트와 특정 도메인에서만 의미가 있는 필터 바, 주문 상태 카드, 대시보드 위젯은 분리하는 것이 좋습니다. 공통 UI는 범용성을, 기능 UI는 맥락을 잘 드러내야 합니다.
이 둘을 섞으면 공통 컴포넌트가 점점 도메인 요구를 품게 되고, 결과적으로 어느 쪽도 아니게 되기 쉽습니다.
React 아키텍처에서 흔한 실패 패턴
다음은 자주 보이는 구조 문제입니다.
- Context 하나에 너무 많은 상태를 넣는다
- 페이지마다
fetch와 에러 처리가 중복된다 - custom hook이 많지만 이름만 있고 책임이 모호하다
- 서버 상태와 폼 상태가 섞여 있다
- URL 상태를 로컬 상태로만 관리해 공유와 복원이 어렵다
이 문제들은 보통 프레임워크가 아니라 경계 설계가 흐린 데서 발생합니다.
협업 기준이 없는 구조는 오래 못 간다
React는 자유도가 높아서 팀 규칙이 없으면 금방 스타일이 갈립니다. 그래서 다음 기준이 있으면 좋습니다.
- 데이터 로딩은 어디서 하는가
- 서버 상태 도구를 언제 쓰는가
- Context는 어떤 범위에서만 허용하는가
- custom hook으로 분리하는 기준은 무엇인가
- 페이지/feature/common 컴포넌트의 경계는 무엇인가
아키텍처는 기술 스택보다 팀의 합의 구조에 더 가깝습니다.
마무리
React 아키텍처의 핵심은 컴포넌트를 많이 만드는 것이 아니라 책임을 선명하게 나누는 것입니다. 표현, 조합, 상태, 데이터, 렌더링 모델을 각각 분리해서 바라보면 프로젝트는 훨씬 더 오래 유지되고, 팀도 더 빠르게 움직일 수 있습니다.
운영 환경에서 어려워지는 지점
- React 아키텍처는 경계가 흐려지고 모든 기능이 서로의 내부로 들어가기 시작하면 빠르게 무너진다.
- 대부분의 유지보수 문제는 JSX나 템플릿 때문이 아니라 데이터, 부작용, 조합의 소유권이 불명확하기 때문에 생긴다.
- React 코드베이스는 컴포넌트, 훅, 라우트, 서버 상태 경계가 명시적일 때 강해진다.
중요한 아키텍처 결정
- 파일 타입 편의보다 feature와 책임 기준으로 먼저 조직한다.
- 비즈니스 로직은 도메인 모듈 가까이에, UI 조합은 route나 screen 가까이에 둔다.
- 공용 유틸리티, 데이터 접근, 상태 추상화가 어떤 경계를 넘을 수 있는지 정한다.
실무 예시
유지보수 가능한 프런트 구조는 프레임워크 프리미티브보다 소유권을 반영한다.
features/
billing/
catalog/
shared/
ui/
api/
utils/
app/
routes/
providers/
피해야 할 안티패턴
- 도메인 묶음 없는 거대한 컴포넌트 트리를 만드는 것.
- 모든 것이 흘러들어가는 shared 폴더를 키우는 것.
- 전송 계층, 비즈니스 규칙, 표현 포맷팅을 같은 모듈에 섞는 것.
운영 체크리스트
- shared 코드와 feature 코드 사이 의존 방향을 검토한다.
- 컴포넌트와 훅/composable 재사용이 단순 중복 제거가 아니라 의미 있는 추상화인지 본다.
- 공개 모듈 API를 작고 의도적으로 유지한다.
- route 수준 복잡도가 feature 바깥으로 새기 시작하면 리팩터링한다.
최종 판단
React 아키텍처는 모듈 경계가 제품 동작을 설명할 때 강하다. 소유권 없는 폴더 정리는 보기 좋은 껍데기에 가깝다.
Continue Reading
다음으로 읽기 좋은 글
React 디자인 시스템 아키텍처 가이드
React 기반 디자인 시스템을 설계할 때 필요한 토큰, 컴포넌트 계층, 접근성, 배포 전략을 실무 관점에서 정리합니다.
🖥️ FrontendReact + SPA 아키텍처 가이드
React 기반 SPA를 설계할 때 필요한 화면 경계, 상태 구조, 라우팅, 데이터 계층, 성능 전략을 실무 관점에서 정리합니다.
📈 최신 동향React Foundation 이 엔지니어링 팀에 의미하는 것
React Foundation 소식이 단순 거버넌스 뉴스가 아니라 프레임워크 생태계의 장기 예측 가능성에 어떤 의미를 갖는지 정리한 글입니다.
⚙️ BackendCQRS + Event Sourcing 실전 구현 가이드
CQRS와 Event Sourcing을 도메인 경계와 운영 복잡성의 관점에서 설명하고, Aggregate, Event Store, Projection, Snapshot, 정합성 모델의 선택 기준을 정리합니다.
다음 탐색