
Expo Router를 사용하여 오늘날 Instagram, WhatsApp, Uber, Netflix를 구축한다면
요약
대규모 모바일 애플리케이션 구축 시 발생하는 폴더 구조의 한계를 분석하고, Expo Router를 활용한 프로덕션급 아키텍처 설계 방식을 제안합니다. 파일 유형 중심이 아닌 비즈니스 기능 중심의 구조화를 통해 확장성과 유지보수성을 확보하는 방법을 다룹니다.
핵심 포인트
- 단순 파일 유형별 구조는 대규모 프로젝트에서 유지보수와 온보딩 문제를 야기함
- 프로덕션 환경에서는 비즈니스 기능(Feature) 단위의 구조화가 필수적임
- Expo Router의 파일 시스템 기반 라우팅을 활용한 효율적인 조직화 가능
- 좋은 아키텍처는 확장성, 유지보수성, 팀 협업 및 개발자 경험을 개선함
대부분의 개발자가 모바일 애플리케이션 구축을 시작할 때, 폴더 구조는 보통 다음과 같은 모습입니다:
app/
components/
screens/
...
소규모 프로젝트의 경우, 이 방식은 완벽하게 작동합니다.
하지만 다음과 같은 서비스를 구축한다고 상상해 보세요:
- 수백만 개의 게시물이 있는 Instagram
- 수십억 개의 메시지가 있는 WhatsApp
- 실시간 위치 업데이트가 있는 Uber
- 방대한 콘텐츠 카탈로그를 가진 Netflix
갑자기 그 단순한 폴더 구조가 무너지기 시작합니다.
파일을 찾기가 어려워집니다.
기능들이 서로 강하게 결합(Tightly coupled)됩니다.
네비게이션(Navigation)이 엉망이 됩니다.
성능 문제가 나타납니다.
그리고 새로운 개발자를 온보딩(Onboarding)하는 과정이 고통스러워집니다.
이제 과제는 단순히 화면(Screens)을 작성하는 것이 아닙니다.
과제는 다음과 같습니다:
수년간의 개발과 수십 명의 엔지니어를 견뎌낼 수 있는 코드베이스를 어떻게 구성할 것인가?
이 지점에서 아키텍처(Architecture)가 중요해집니다.
이 글에서는 Expo Router와 현대적인 React Native 관행을 사용하여 오늘날 대규모 애플리케이션을 어떻게 구조화할 수 있는지 탐구할 것입니다.
목표는 Instagram이나 Uber를 복제하는 것이 아닙니다.
목표는 프로덕션급(Production-grade) 모바일 애플리케이션 뒤에 숨겨진 아키텍처적 사고방식을 이해하는 것입니다.
단순한 폴더 구조가 규모가 커질 때 실패하는 이유
대부분의 초보자용 애플리케이션은 파일 유형별로 구성됩니다.
예시:
components/
screens/
hooks/
...
처음에는 이것이 깔끔하게 느껴집니다.
하지만 몇 달이 지나면:
components/
├── PostCard.tsx
├── UserAvatar.tsx
...
수백 개의 파일이 쌓입니다.
관련된 기능을 찾는 것이 어려워집니다.
애플리케이션을 유지보수하기가 더 힘들어집니다.
소규모 앱 사고방식 vs 프로덕션 사고방식
소규모 앱 사고방식 (Small App Thinking):
파일 유형별로 구성
프로덕션 사고방식 (Production Thinking):
비즈니스 기능(Business feature)별로 구성
대기업은 다음과 같은 관점에서 생각합니다:
- 피드 (Feed)
- 채팅 (Chat)
- 프로필 (Profile)
- 결제 (Payments)
- 알림 (Notifications)
- 인증 (Authentication)
버튼이나 화면이 아니라 말이죠.
아키텍처가 중요한 이유
좋은 아키텍처는 다음과 같은 이점을 제공합니다:
확장성 (Scalability)
기능들이 독립적으로 성장할 수 있습니다.
유지보수성 (Maintainability)
개발자가 코드가 어디에 속해야 하는지 정확히 알 수 있습니다.
팀 협업 (Team Collaboration)
여러 명의 엔지니어가 동시에 작업할 수 있습니다.
성능 (Performance)
복잡성이 증가하더라도 시스템의 효율성이 유지됩니다.
개발자 경험 (Developer Experience)
개발 과정이 예측 가능한 상태로 유지됩니다.
현대적인 Expo Router 접근 방식
Expo Router는 React Native 애플리케이션이 내비게이션 (Navigation)을 처리하는 방식을 변화시켰습니다.
내비게이션 트리 (Navigation trees)를 수동으로 정의하는 대신:
<Stack.Navigator>
라우트 (Routes)가 파일 시스템 기반 (File-system based)이 됩니다.
폴더가 애플리케이션 구조를 정의합니다.
이는 더 깔끔한 조직화를 장려합니다.
프로덕션급 (Production-Grade) Expo Router 구조
대규모 애플리케이션은 다음과 같은 형태를 띨 수 있습니다:
app/
│
├── (public)/
...
중요한 점을 주목하세요.
내비게이션 (Navigation)과 비즈니스 로직 (Business logic)이 분리되어 있습니다.
이는 규모가 커질수록 매우 중요해집니다.
기능 기반 아키텍처 (Feature-Based Architecture)
기능 기반 아키텍처 (Feature-based architecture)는 대규모 애플리케이션에서 가장 흔히 사용되는 패턴 중 하나입니다.
다음과 같은 방식 대신:
components/
screens/
services/
도메인 (Domain)별로 조직화합니다.
features/
├── auth/
...
각 기능은 자체 로직을 소유합니다.
기능 구조 예시
features/chat/
├── components/
...
채팅과 관련된 모든 것이 함께 존재합니다.
이는 개발을 현저히 더 쉽게 만듭니다.
인증 흐름 아키텍처 (Authentication Flow Architecture)
모든 대규모 애플리케이션은 인증 (Authentication)에서 시작합니다.
예시:
- Instagram 로그인
- WhatsApp 등록
- Uber 인증
- Netflix 프로필
일반적인 라우트 구조:
Expo Router의 라우트 그룹 (Route groups)은 이를 매우 깔끔하게 만들어 줍니다.
보호된 라우트 (Protected Routes)
app/
(public)
...
인증되지 않은 사용자는 공개 라우트 (Public routes) 내에 머뭅니다.
인증된 사용자는 보호된 라우트 (Protected routes)에 접근할 수 있습니다.
이 패턴은 확장성이 매우 뛰어납니다.
공유 레이아웃 및 중첩 라우팅 (Shared Layouts and Nested Routing)
대규모 애플리케이션은 종종 UI 구조를 공유합니다.
예시:
Instagram:
Bottom Tabs
Home
...
모든 탭은 동일한 레이아웃을 공유합니다.
Expo Router는 이를 중첩 레이아웃 (nested layouts)을 통해 처리합니다.
(tabs)
_layout.tsx
...
이는 중복을 줄여줍니다.
대규모 애플리케이션에서의 상태 관리 (State Management at Scale)
작은 앱들은 종종 React 상태 (state)에만 의존합니다.
useState()
대규모 애플리케이션은 더 많은 구조를 필요로 합니다.
상태는 보통 세 가지 범주로 나뉩니다.
서버 상태 (Server State)
API로부터 오는 데이터입니다.
예시:
- 피드 게시물 (Feed posts)
- 메시지 (Messages)
- 영화 (Movies)
- 사용자 프로필 (User profiles)
전형적인 솔루션:
TanStack Query
클라이언트 상태 (Client State)
UI와 관련된 상태입니다.
예시:
- 모달 가시성 (Modal visibility)
- 테마 설정 (Theme preferences)
- 내비게이션 상태 (Navigation state)
전형적인 솔루션:
Zustand
Redux Toolkit
지속성 상태 (Persistent State)
앱 실행 간에 저장되는 데이터입니다.
예시:
- 인증 토큰 (Authentication tokens)
- 캐시된 설정 (Cached preferences)
- 오프라인 데이터 (Offline data)
전형적인 저장소:
AsyncStorage
SecureStore
API 레이어 아키텍처 (API Layer Architecture)
흔한 실수는 스크린에서 직접 API를 호출하는 것입니다.
나쁜 예:
FeedScreen.tsx
↓
Fetch API
이는 강한 결합 (tight coupling)을 만듭니다.
더 나은 구조:
예시:
이러한 분리는 테스트와 유지보수성을 향상시킵니다.
Instagram이 구조화되는 방식
Instagram은 단순해 보입니다.
하지만 내부적으로는 여러 시스템을 포함하고 있습니다.
피드 시스템 (Feed System)
다음 사항을 담당합니다:
- 게시물 (Posts)
- 좋아요 (Likes)
- 댓글 (Comments)
- 미디어 (Media)
가능한 기능 구조:
features/feed/
├── api/
...
미디어 시스템 (Media System)
다음 항목을 처리합니다:
- 이미지 업로드 (Image uploads)
- 비디오 업로드 (Video uploads)
- 압축 (Compression)
- 캐싱 (Caching)
완전히 분리된 도메인 (domain)입니다.
알림 시스템 (Notification System)
다음 항목을 처리합니다:
- 좋아요 (Likes)
- 댓글 (Comments)
- 팔로우 (Follows)
- 언급 (Mentions)
보통 별도의 기능 (feature)으로 구현됩니다.
WhatsApp의 구조 방식
WhatsApp은 근본적으로 실시간 (realtime) 시스템입니다.
복잡성은 화면 (screens)이 아니라,
메시징 (messaging)에서 발생합니다.
채팅 아키텍처 (Chat Architecture)
실시간 레이어 (realtime layer)가 메시지를 지속적으로 동기화합니다.
실시간 메시징 흐름 (Realtime Messaging Flow)
이 흐름은 매일 수백만 번 반복됩니다.
오프라인 메시징 (Offline Messaging)
메시지는 다음 상황에서도 유지되어야 합니다:
- 인터넷 연결 없음
- 앱 충돌 (App crashes)
- 기기 재시작
아키텍처:
오프라인 우선 (Offline-first) 사고방식이 필수적이 됩니다.
Uber의 구조 방식
Uber는 다른 차원의 과제를 제시합니다.
실시간 위치 (Realtime location) 정보입니다.
차량 추적 아키텍처 (Ride Tracking Architecture)
업데이트가 지속적으로 발생합니다.
상태 관리 과제 (State Management Challenges)
Uber는 다음을 관리합니다:
- 사용자 위치 (User location)
- 드라이버 위치 (Driver location)
- 경로 데이터 (Route data)
- 탑승 상태 (Ride status)
이 모든 것이 동시에 변경됩니다.
효율적인 상태 업데이트 (state updates)가 매우 중요해집니다.
실시간 위치 업데이트 (Realtime Location Updates)
전형적인 흐름:
이 아키텍처는 수천 개의 동시 주행(concurrent rides) 상황에서도 효율성을 유지해야 합니다.
Netflix는 어떻게 구조화될 것인가
Netflix는 또 다른 과제를 제시합니다.
콘텐츠 전송 (Content delivery).
콘텐츠 탐색 (Content Discovery)
기능:
홈 피드 (Home Feed)
카테고리 (Categories)
검색 (Search)
...
각각은 독립적인 모듈로 작동합니다.
미디어 성능 (Media Performance)
비디오 스트리밍 (Video streaming)이 아키텍처 결정의 주도권을 잡습니다.
집중 영역:
- 캐싱 (Caching)
- 프리페칭 (Prefetching)
- 지연 로딩 (Lazy loading)
- 메모리 관리 (Memory management)
과제는 UI를 렌더링하는 것이 아닙니다.
과제는 콘텐츠를 효율적으로 전달하는 것입니다.
오프라인 다운로드 (Offline Downloads)
Netflix 스타일의 다운로드는 다음을 필요로 합니다:
보통 전용 서브시스템 (dedicated subsystem)이 필요합니다.
오프라인 우선 아키텍처 (Offline-First Architecture)
많은 성공적인 모바일 앱들은 인터넷 연결 없이도 작동합니다.
예시:
- Uber
- Spotify
- Netflix
캐시 동기화 흐름 (Cache Synchronization Flow)
사용자는 즉각적인 피드백을 경험합니다.
앱 시작 최적화 (App Startup Optimization)
대규모 애플리케이션은 시작 단계에서 너무 많은 작업이 발생하기 때문에 종종 느리게 느껴집니다.
일반적인 시작 문제 (Common Startup Problems)
인증 확인 (Authentication Check)
API 호출 (API Calls)
...
모두가 리소스를 차지하기 위해 경쟁합니다.
최적화된 시작 흐름 (Optimized Startup Flow)
우선순위 설정 (Prioritization)이 중요합니다.
성능 고려 사항 (Performance Considerations)
프로덕션 애플리케이션은 끊임없이 최적화합니다.
리렌더링 (Re-renders) 감소
불필요한 업데이트를 피하세요.
코드 분할 (Code Splitting)
필요할 때만 기능을 로드하세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기









