Flutter에서 대두되는 두가지 아키텍처 패러다임이 있다. 이 두가지 아키텍처에 대해서 기술한 글들을 리뷰해 어떤 아키텍처를 선택하는 것이 좋을지 알아보자.
기술할 글의 선정 기준은 구글에 feature first를 검색했을 때 상단에 위치한 글이다.
https://codewithandrea.com/articles/flutter-project-structure/
Codewithandrea 의 글이다. 위 글은 두 아키텍처의 차이점과 장단점을 설명하고 직접 구성해본 Feature first 구조를 설명한다.
먼저 기능은 위 4개의 레이어로 구현한다.
- Presentation Layer: 실질적인 UI를 나타내는 레이어이다. Widgets에서 화면을 보여주고 States에서 화면에 존재하는 값의 상태를 관리하며 Controllers 에서 값의 변경을 요청한다.
- Application Layer: 비즈니스 로직이 있는 레이어이다. Services에선 각 컨트롤러에서 요청한 서비스로직이 실행된다.
- Domain Layer: 도메인 모델 레이어이다. 각 도메인에서 공통적으로 사용되는 스키마가 있다.
- Data Layer: 실제로 데이터를 로컬/서버에서 불러오고 저장한다. Repositories에선 각 모델의 CRUD를 실행하며 DTOs는 실제 DB 테이블과 매핑 돼 데이터를 Data Sources에서 가져온다.
먼저 Layer-first 구조를 살펴보자
Layer-first (features inside layers)
Layer-first는 위 4단계의 각 레이어 안에 feature들이 나눠져 들어간다. 구조는 아래와 같다.
‣ lib
‣ src
‣ presentation
‣ feature1
‣ feature2
‣ application
‣ feature1
‣ feature2
‣ domain
‣ feature1
‣ feature2
‣ data
‣ feature1
‣ feature2
위 구조는 각 레이어 별로 나뉘어 있기 때문에 배우기가 쉽다. 각 단계의 분리가 명확하고 모든 features들을 같은 단계 안에서 모아 볼 수 있다. 하지만 위 구조에서 feature가 추가 되었다고 생각해보자.
‣ lib
‣ src
‣ presentation
‣ feature1
‣ feature2
‣ feature3 <--
‣ application
‣ feature1
‣ feature2
‣ feature3 <-- only create this when needed
‣ domain
‣ feature1
‣ feature2
‣ feature3 <--
‣ data
‣ feature1
‣ feature2
‣ feature3 <--
feature3를 추가하기 위해서 4단계의 레이어를 돌아다니면서 작업을 해야 한다. 3개의 feature 정도는 괜찮아 보인다. 하지만 일반적으로 앱은 3개의 기능에서 끝나지 않는다. 20개의 feature가 있는 모습을 상상해보자. feature 하나를 추가하려 하기만 해도 각 레이어 안의 19개 feature를 지나 개발을 진행해야 한다. 이것은 규모가 커질 수록 앱의 유지보수성을 극도로 망치게 된다.
Feature-first (layers inside features)
Feature-first는 각 Feature 안에 4단계의 레이어가 들어간 구조이다.
‣ lib
‣ src
‣ features
‣ feature1
‣ presentation
‣ application
‣ domain
‣ data
‣ feature2
‣ presentation
‣ application
‣ domain
‣ data
위 구조를 보면 각 feature 폴더 안에 4단계의 레이어가 있는 모습을 볼 수 있다. 새로운 feature를 추가하기 위해선 feature3 폴더를 추가한 후 하위 폴더에 4단계 레이어를 추가하기만 하면 된다. Feature-first 구조는 Layer-first 구조보다 얻는 이점은 다음과 같다.
- 한 feature를 추가하거나 기능을 수정할 때 해당 feature의 폴더만 보면 된다
- feature를 삭제할 때 해당 feature의 폴더 1개만 삭제해도 된다. 단 tests 폴더와 같이 종속 되어 있는 폴더가 있다면 해당 폴더의 코드도 삭제해야 하지만 이것은 모든 Layer-first에서 각 레이어의 feature들을 찾아 삭제하는 것보다 훨씬 단순하다.
공통 코드
그러면 tests, utils와 같이 모든 기능에서 공유하는 폴더는 어떻게 해야 할까? 해당 폴더는 하나의 feature로 보고 features 아래에 shared 폴더를 만든 후 하위에 tests, utils와 같이 공통적인 폴더 안에 정리한다. 이렇게 하면 각 feature들에서 쉽게 공통 기능을 주입 받을 수 있다. GetIt 같은 라이브러리를 사용하면 DI는 더욱 용이해진다.
Feature-first는 UI에 대한 것이 아니다.
Feature-first는 DDD(Domain Driven Design)과 닮아 있다. 사실 거의 같은 개념으로 보인다. 각 기능 별로 정리가 되고, 기능 안에서 레이어가 분리되어 기능을 구현한다. 하지만 그 기능이 한 가지 UI를 말하는 것은 아니다. UI는 그 기능 안에 들어있는 구성 요소 일 뿐이다. 한 Feature는 다른 여러 Screen의 View에서 사용할 수 있고 재활용 될 수 있다. 예를 들어 Search라는 Feature는 Post라는 Feature에서 Post를 검색하는데 사용될 수 있다. Feature를 UI에 고립시켜 생각하면 나중에 "unbalanced" 한 프로젝트 구조를 만나게 될 수도 있다.
Feature-first 구조로 개발하는 올바른 방법
결과적으로 아래 네가지로 정리할 수 있다.
- 도메인 레이어에 모델 클래스와 비즈니스 로직을 기획한다.
- 각 모델을 위한 폴더를 함께 만든다.
- 해당 폴더 하위에 presentaion, application, domain, data 폴더를 만든다.
- 각 폴더에 필요한 파일들을 추가한다.
추가로 Flutter 앱을 빌드할 때 UI코드와 비즈니스 로직 코드의 코드 비율은 5대1 정도 된다. presentation 폴더 하위에 많은 파일이 생겼다면, 다시 각 feature로 분리하도록 하자.
결과적으로, 아래와 같은 구조를 얻을 수 있다.
‣ lib
‣ src
‣ common_widgets
‣ constants
‣ exceptions
‣ features
‣ address
‣ authentication
‣ cart
‣ checkout
‣ orders
‣ products
‣ reviews
‣ localization
‣ routing
‣ utils
각 feature들을 나누고 정말 공통으로 나뉘어야 하는 요소들을 추가로 만들어 준다. 이제 수평적으로 확장할 수 있는 구조를 얻었다!
테스트
test 폴더는 lib 폴더의 구조와 비슷하게 가져간다.
lib 폴더 하위의 파일에서 우클릭을 하면 다음과 같이 Go to Tests 항목을 볼 수 있다. 해당 항목을 클릭하면 test 폴더 하위에 file_name_test.dart 라는 이름으로 새로운 파일이 생성된다.
위 사진을 보면 같은 폴더 구조 아래에 테스트가 생긴 것을 볼 수 있다.
'App > Flutter' 카테고리의 다른 글
Flutter SafeArea 알아보기 (0) | 2024.03.22 |
---|---|
Flutter GetIt으로 Dependency Injection하기 (0) | 2024.03.22 |
Flutter로 어떤 UI든 구현하는 방법 (0) | 2024.03.21 |
Flutter Bloc과 Cubit (0) | 2024.03.21 |
Flutter Operator와 Equatable로 객체 비교하기 (0) | 2024.03.21 |