[GetX] GetX Pattern 을 적용한 플러터 폴더 구조

Broccolism·2022년 8월 9일
7
post-thumbnail

GetX + GetX Pattern

GetX: https://github.com/jonataslaw/getx
getx_pattern: https://github.com/kauemurakami/getx_pattern

/lib 폴더 구조

lib
  ├── binding       각 스크린별 필요한 의존성 주입
  ├── controller    상태관리
  ├── model         데이터 타입 정의
  ├── provider      서버 api, 로컬 DB api 등 호출
  ├── repository    응답으로 받은 데이터 가공
  ├── src           상수값 정의
  ├── util          편의성 함수 정의
  ├── view          화면에 보이는 UI 모음
  ├── main.dart
  ├── router.dart   각 스크린별 라우팅 정의
  └── test_screen.dart  새 컴포넌트 개발 시 테스트 스크린에서 하면 편하다 ^,^

서버로부터 받아오는 데이터의 흐름

= provider -> repository -> controller -> view

provider: 서버에 request를 보내고 response를 받아온다.

  • 프론트 내부 로직이 바뀌었을 때, 이 부분은 바뀌지 않는다.
const sampleUrl = "http://ko.gravatar.com/beaua06344c126b.json";
class SampleApi extends GetConnect {
Future<Response> getSampleUserProfile() async {
    return await get(sampleUrl);
}
}

repository: 받아온 response를 사용하기 편리하게 형변환

  • 어떤 API를 사용할 수 있는지 한눈에 보기 위해 abstract 클래스를 정의
abstract class AbstractSampleRepository {
  Future<SampleUserModel> getSampleUserProfile();
}

class SampleReopsitory extends GetxService implements AbstractSampleRepository {
  final SampleApi sampleApi;

  SampleReopsitory({required this.sampleApi});
  
  Future<SampleUserModel> getSampleUserProfile() async {
    try {
      Response res = await sampleApi.getSampleUserProfile();
      return SampleUserModel.fromJson(res.body);
    } catch (e) {
      debugPrint("ERROR: $e");
      rethrow;
    }
  }
}

controller: 레포지토리의 함수를 사용해 뷰에 뿌려줄 상태 관리

  • 상태를 저장하는 변수는 private 처리
  • 변수에 접근하거나 값을 바꾸는 함수는 public 처리
class SampleController extends GetxController {
  final SampleReopsitory sampleReopsitory;

  SampleController({required this.sampleReopsitory});

  final Rx<SampleUserModel?> _user = Rx<SampleUserModel?>(null);
  final Rx<int> _buttonPressCount = 0.obs;
  SampleUserModel? get user => _user.value;
  int get buttonPressCount => _buttonPressCount.value;

  Future<void> getProfileAndIncreaseCount() async {
    await _getSampleUserProfile();
    _increaseCount();
  }

  void resetCount() {
    _buttonPressCount.value = 0;
  }

  void _increaseCount() {
    _buttonPressCount.value += 1;
  }

  Future<void> _getSampleUserProfile() async {
    _user.value = await sampleReopsitory.getSampleUserProfile();
  }
}

view 폴더 구조

view
  ├── common
  │   ├── button
  │   ├── layout
  │   └── widgets.dart
  ├── sample
  │   ├── screen
  │   └── widget
  ├── home_screen.dart
  └── screens.dart
  • common: 공통 컴포넌트 위젯 모음
  • 나머지 폴더는 스크린별, 혹은 기능별로 하나씩 생성
    • 예) 회원가입 관련 스크린: signup 폴더에 first_signup_screen.dart, second_signup_screen.dart, ... 생성
  • 다른 파일에서 깔끔하게 import할 수 있게 screens.dart 파일 생성
    • 이는 다른 모든 폴더(binding, provider, repository 등)에서도 마찬가지다.

repository와 provider의 차이점을 자꾸 까먹어서 적어봤다. 폴더 구조가 늘 그렇듯 정해진 정답은 없지만 이렇게 작업했을 때 상당히 편했던 기억이 있다. (내가 처음부터 구축한건 아니고 사수님이 도입했었다. 이 글을 발견하셨나요,,?) 새 플러터 프로젝트를 시작하는 누군가에게 도움이 되면 좋겠다. 😇

profile
설계를 좋아합니다. 코드도 적고 그림도 그리고 글도 씁니다. 넓고 얕은 경험을 쌓고 있습니다.

0개의 댓글