Feature-based Clean Architecture으로 API 통신하기 - 1

개발렬·2024년 11월 3일

Flutter

목록 보기
2/10
post-thumbnail

회사에서 여러 개발자들과 프로젝트를 진행할 때, 효율적인 협업과 유지보수를 위해 서로 약속된 규칙과 구조를 갖추는 것이 중요하다. 이번 프로젝트에서는 기존 앱에 다른 회사의 앱을 통합하는 작업을 진행하게 되면서, 통합 코드를 기존 프로젝트에 어떻게 잘 녹여낼지 고민했다.

우선, 기존 프로젝트의 코드와 혼합되지 않도록 새로운 앱을 위한 코드는 별도의 파일로 분리하고, 기능 중심으로 나눈 Feature-based Clean Architecture 구조를 적용해 시작했다.

Feature-based Clean Architecture란?

Feature-based Clean Architecture는 말 그대로 각 "기능(feature)"을 기준으로 코드를 분리하고, Clean Architecture의 원칙에 따라 각 레이어를 독립적으로 관리하는 구조입니다. 이 아키텍처의 핵심 목표는 각 기능이 다른 기능과 최대한 독립적이며, 데이터와 비즈니스 로직, UI를 명확히 구분하는 것입니다.

이 방식은 다음과 같은 장점이 있습니다:

  • 확장성: 각 기능은 독립적으로 확장될 수 있습니다. 새로운 기능을 추가할 때 기존 코드에 영향을 미치지 않고 추가할 수 있습니다.
  • 유지보수성: 각 기능이 분리되어 있어 디버깅이나 유지보수가 더 용이합니다.
  • 테스트 용이성: 레이어 간의 의존성을 낮추어 테스트가 용이해집니다. 각 레이어를 독립적으로 테스트할 수 있어 코드의 신뢰성을 높일 수 있습니다.

Feature-based Clean Architecture 파일 구조

아래는 Feature-based Clean Architecture에 따른 파일 구조 예시입니다.


feature_a/
  ├── data/
  │    ├── remote/
  │    │    ├── feature_repository_impl.dart     // 리포지토리 구현체: 원격 API 호출을 통해 데이터를 가져오고, 가공하여 도메인 레이어로 전달
  │    │    ├── api_service.dart                 // API 호출을 위한 서비스 클래스, HTTP 요청을 처리하며 원격 데이터와 통신
  │    │    └── dto/                             // Data Transfer Object: API 응답을 애플리케이션 내부에서 사용할 수 있는 형태로 변환하는 데이터 객체
  │    │         └── feature_dto.dartfea
  │    ├── local/
  │    │    ├── local_database.dart              // 로컬 데이터베이스 연결 및 초기화 처리
  │    │    └── dao/                             // 데이터베이스 액세스 객체(DAO): 로컬 데이터베이스에 대한 CRUD 작업을 수행
  │    │         └── feature_dao.dart
  │    └── datasources/                          // 원격 및 로컬 데이터 소스를 통합 관리
  │         └── feature_data_source.dart
  │
  ├── domain/
  │    ├── entities/                             // 도메인 엔터티: 애플리케이션 내에서 사용되는 핵심 데이터 구조 (비즈니스 규칙을 포함)
  │    │    └── feature_entity.dart
  │    ├── repositories/                         // 도메인 레이어에서 사용하는 리포지토리 인터페이스 정의
  │    │    └── feature_repository.dart
  │    └── usecases/                             // 유스케이스: 도메인 비즈니스 로직을 캡슐화하고, 애플리케이션이 해야 할 일(행동)을 정의
  │         └── get_feature_use_case.dart
  │
  ├── presentations/
  │    ├── widgets/                              // 재사용 가능한 UI 컴포넌트
  │    │    └── feature_widget.dart
  │    ├── providers/                            // Riverpod 상태 관리 관련 코드: 상태를 읽고 업데이트하며, UI와 비즈니스 로직을 연결
  │    │    └── feature_provider.dart
  │    └── pages/                                // UI 페이지: 화면 단위로 분리된 UI 구성 코드
  │         └── feature_page.dart
  │
  ├── utils/                                     // 공통 유틸리티 함수 및 상수
  │    └── validators.dart
  │
  ├── exceptions/                                // 각 레이어에서 발생하는 예외 처리
  │    └── feature_exception.dart
  │
  └── mappers/                                   // 데이터 매핑: DTO와 엔티티 간 데이터 변환 로직
       └── feature_mapper.dart

위 구조에서 features 폴더 안에 기능을 기준으로 코드를 분리합니다. 예를 들어, feature_a와 feature_b는 각각 pairing_feature와 content_streaming_feature로 나눌 수 있으며, 이는 페어링 관련 기능과 콘텐츠 재생 기능을 담당합니다.

이러한 구조적 접근은 프로젝트의 복잡성을 줄이고, 팀원 간의 협업을 더 원활하게 만들어 줍니다. 각 팀원이 특정 기능에만 집중할 수 있도록 하여, 코드의 가독성과 유지보수성을 높이는 효과를 가져옵니다.

이번 포스팅을 통해 Feature-based Clean Architecture의 기본 개념과 파일 구조를 이해하는 데 도움이 되길 바랍니다. 다음 포스팅에서는 API 통신의 구체적인 구현 방법에 대해 다뤄보겠습니다.

profile
Flutter

0개의 댓글