스케일러블한 React Native + Expo Router 폴더 구조 설계
Source: Dev.to
수년간, 팀과 제품 전반에 걸쳐 반복된 한 가지 교훈이 있습니다:
폴더 구조는 미학이 아니라 규모에 맞는 의사결정에 관한 것입니다.
저는 Expo + Expo Router를 사용한 React Native 앱을 개발해 왔으며, 대규모·장기 프로젝트에 잘 맞는 구조를 공유하고자 합니다.
Note: 아래 모든 폴더는 최상위
src/디렉터리 아래에 위치합니다.
폴더 개요
src/
├─ app
├─ components
├─ config
├─ hooks
├─ lib
├─ providers
├─ screens
└─ utils
app/ — 라우팅을 일급 시민으로
Expo Router는 라우트가 기술적 지름길이 아니라 사용자 흐름을 반영할 때 빛을 발합니다.
app/
├─ (authenticated)
├─ (home-tabs)
├─ (unauthenticated)
├─ _layout.tsx
└─ index.tsx
(unauthenticated)
- 로그인, OTP, 온보딩
- 탭 없음, 방해 요소 없음
- 인증 가드의 명확한 경계
(authenticated)
- 로그인 후 진입점
- 앱 수준 레이아웃, 리다이렉트 및 전역 상태를 처리
(home-tabs)
- 하단 탭에 실제로 속하는 화면만
- 그 외 모든 것(모달, 흐름, 상세 화면)은 탭 외부에 존재
흐름: Unauthenticated → Authenticated → Tab‑based home → Non‑tab flows
추측 없이, 실수로 탭이 중첩되지 않으며, 라우터 스파게티도 없습니다.
components/ — 디자인 시스템, 무작위 재사용이 아님
The structure follows Atomic Design, applied pragmatically:
components/
├─ atoms
├─ molecules
├─ organisms
└─ templates
- Atoms – 순수하고, 재사용 가능하며, 테스트 가능한 UI 기본 요소
- Molecules – 의도를 가진 작은 조합
- Organisms – 기능을 인식하는 UI 블록
- Templates – 레이아웃 패턴(전체 화면이 아님)
Benefits
- 앱 전반에 걸친 UI 일관성
- 디자인 시스템이 발전할 때 손쉬운 리팩터링
- 컴포넌트가 일반적인 잡동사니 서랍이 되지 않고 재사용 가능하게 유지
lib/ — 앱의 두뇌 (덤핑 그라운드가 아님)
lib/는 의도적으로 구조화되었습니다:
lib/
├─ auth
├─ backend
├─ implementation
├─ interface
└─ vector-icon
backend/
- API 클라이언트 (Axios / fetch 래퍼)
- TanStack Query 클라이언트 설정
- 서버‑상태 훅
- 백엔드 데이터 모델
- 명확한 계약 (interface / implementation)
- 플랫폼‑에 종속되지 않는 추상화 – 모킹, 테스트, 교체가 용이
예시
// lib/backend/_models/...
// lib/backend/server-state/queries/useGetThoughtOfDayApi.ts
// lib/backend/server-state/query-client.ts
// lib/backend/supabase/supabase-client.ts
// lib/backend/supabase/supabase-safe-call.ts
// lib/backend/supabase-db/fetch-thought-of-day.ts
auth/
- 인증 상태, 제공자, 경계가 함께 존재
- 인증 로직이 UI에 섞여 들어가지 않음
이러한 분리는 API가 변경되거나 백엔드 제공자를 교체하거나 네트워크 없이 테스트가 필요할 때 큰 도움이 됩니다.
screens/ — 화면은 라우트가 아니다
screens/
├─ authenticated
└─ unauthenticated
- 화면은 UI와 화면‑레벨 상태를 포함합니다.
- 라우트(
app/)는 화면이 언제 표시될지 결정합니다.
Result: 화면은 이식 가능하고, 라우트는 선언적입니다.
utils/, hooks/, providers/ — 규모를 지원하기
| Directory | Purpose |
|---|---|
| utils | 순수 로직, React 의존성 없음 – 테스트가 쉽고 신뢰할 수 있습니다. |
| hooks | 앱 전용 동작 – 일반 유틸리티를 훅으로 가장한 것이 아닙니다. |
| providers | 테마, 쿼리 클라이언트, 안전 영역, 전역 앱 컨텍스트 – 앱 전반의 관심사에 대한 단일 진실 원천. |
왜 이 구조가 확장 가능한가
- 여러 팀에 걸쳐 작동 – 명확한 소유권.
- 신규 엔지니어의 인지 부하를 감소시킵니다.
- 리팩토링 없이 기능 기반 성장 지원.
- React Native 및 Web(Expo)에서도 동일하게 잘 작동합니다.
가장 중요한 것은, 이 구조가 프레임워크 내부가 어떻게 조직되어 있는지가 아니라 사용자가 앱을 이용하는 방식을 반영한다는 점입니다.
최종 생각
- 프레임워크는 진화합니다.
- 제품 요구사항이 변경됩니다.
- 팀이 성장합니다.
좋은 폴더 구조는 그것과 싸우지 않고 — 그것을 흡수합니다.
전문가 팁
스크린 컴포넌트를 가볍게 유지하세요. 스크린은 구성과 네비게이션에 집중하고, 개별 섹션 컴포넌트가 상태, 커스텀 훅, API/TanStack Query 로직을 사용되는 곳 근처에서 관리하도록 합니다.
프로덕션 수준의 Expo Router 앱을 설계하는 분들에게 도움이 되길 바랍니다. 다른 분들의 프로젝트 구조도 궁금합니다!