SwiftUI에서 모듈형 기능 아키텍처
Source: Dev.to
🧩 1. 기능 모듈이란?
A feature module is a self‑contained unit representing one functional chunk of your app:
Home/
Profile/
Settings/
Feed/
Auth/
OfflineSync/
Notifications/
Each module contains:
- 뷰
- 뷰모델
- 모델
- 서비스
- 라우팅 정의
- 프리뷰
- 목업
A feature should be removable without breaking the app. If you can delete a folder and nothing else breaks → it is modular.
📁 2. 권장 폴더 구조
Here is a clean, scalable pattern:
AppName/
│
├── App/
│ ├── AppState.swift
│ ├── AppEntry.swift
│ └── RootView.swift
│
├── Modules/
│ ├── Home/
│ │ ├── HomeView.swift
│ │ ├── HomeViewModel.swift
│ │ ├── HomeService.swift
│ │ ├── HomeRoute.swift
│ │ └── HomeMocks.swift
│ │
│ ├── Profile/
│ │ ├── ProfileView.swift
│ │ ├── ProfileViewModel.swift
│ │ ├── ProfileService.swift
│ │ ├── ProfileRoute.swift
│ │ └── ProfileMocks.swift
│ │
│ ├── Settings/
│ │ ├── SettingsView.swift
│ │ ├── SettingsViewModel.swift
│ │ └── SettingsMocks.swift
│ │
│ └── Shared/
│ ├── Components/
│ ├── Models/
│ ├── Utilities/
│ └── Styles/
│
├── Services/
│
└── Resources/
No more “God folders”—each module is its own world.
🔌 3. 모듈 경계에서의 의존성 주입
Each module declares what it needs via protocols:
protocol ProfileServiceProtocol {
func fetchProfile(id: String) async throws -> Profile
}
The module does not know about concrete implementations (real API client, offline cache, mock data source, testing environment). The app injects the concrete service:
ProfileViewModel(
service: appServices.profileService
)
장점
- 테스트 용이성
- 유연성
- 쉬운 교체
- 격리
🧭 4. 모듈 간 라우팅
Each module defines its own route type:
enum ProfileRoute: Hashable {
case details(id: String)
case followers(id: String)
}
The root view aggregates module routes:
enum AppRoute: Hashable {
case profile(ProfileRoute)
case home
case settings
}
Central navigation handling:
.navigationDestination(for: AppRoute.self) { route in
switch route {
case .profile(let pr): ProfileRouter.view(for: pr)
case .home: HomeView()
case .settings: SettingsView()
}
}
Module‑specific router:
struct ProfileRouter {
@ViewBuilder static func view(for route: ProfileRoute) -> some View {
switch route {
case .details(let id):
ProfileView(userID: id)
case .followers(let id):
FollowersView(userID: id)
}
}
}
Modules stay independent.
🧪 5. 기능 모듈은 완전한 프리뷰가 가능해야 함
Example preview:
#Preview("Profile Details") {
ProfileView(
viewModel: ProfileViewModel(
service: MockProfileService()
)
)
}
자체 포함 프리뷰
- 개발 속도 향상
- 회귀 방지
- 인지 부하 감소
The previews folder becomes a mini design system per module.
🏎 6. 모듈화로 얻는 성능 향상
A modular codebase brings:
- 빌드 시간 단축 – 변경된 모듈만 다시 컴파일됩니다.
- 안전한 리팩토링 – 모듈이 내부 세부 정보를 누출하지 않습니다.
- 테스트 격리 향상 – 모듈별로 테스트를 실행합니다.
- 인지 부하 감소 – 새로운 기여자가 기능을 빠르게 이해합니다.
- CI 병렬화 개선 – 각 모듈을 별도 테스트 타깃으로 사용할 수 있습니다.
These gains are especially noticeable in apps with 10+ screens.
🔄 7. 기능 통신 패턴
Modules should not import each other. Use one of these patterns instead:
- AppRoute (most common) – root coordinates navigation. →
AppRoute(가장 일반적) – 루트가 네비게이션을 조정합니다. - Services layer – modules communicate through shared service protocols. →
서비스 레이어– 모듈이 공유 서비스 프로토콜을 통해 통신합니다. - Event bus – for global side effects like analytics. →
이벤트 버스– 분석과 같은 전역 부수 효과에 사용합니다. - Shared models – placed explicitly under
Modules/Shared. →공유 모델–Modules/Shared에 명시적으로 배치합니다.
Key rule: 📌 Modules talk “upward”, not sideways.
🧵 8. 각 모듈 내부에 비즈니스 로직 격리
All module logic belongs in the module:
Profile/
ProfileViewModel.swift
ProfileService.swift
ProfileValidation.swift
ProfileFormatter.swift
Avoid:
- 전역 유틸리티
- 공유 상태
- 관련 없는 모듈 임포트
This keeps code ownership clean.
🧱 9. 앱 전반에 걸친 공유 모듈
Your Modules/Shared folder should contain:
- 기본 컴포넌트
- 타이포그래피
- 테마 시스템
- 전역 모델
- 네트워킹 유틸리티
- 애니메이션 헬퍼
Never place feature‑specific logic here.
🧭 10. 언제 모듈화를 해야 할까?
Use this checklist:
- 앱에 6개 이상의 화면이 있다
- 두 명 이상의 개발자가 코드베이스에서 작업한다
- 기능이 독립적으로 배포된다
- 빠른 프리뷰가 필요하다
- 안전한 리팩토링이 필요하다
- 딥 링크를 사용한다
- 오프라인 모드를 지원한다
- DI + 전역 AppState를 사용한다
If you checked 3 or more → modularize.
🚀 최종 생각
Modularizing a SwiftUI app transforms your codebase:
- 작업이 더 쉬워진다
- 테스트가 더 쉬워진다
- 확장이 더 쉬워진다
- 리팩토링이 더 쉬워진다
- 협업이 더 쉬워진다
It’s one of the biggest upgrades you can make to your architecture.