1달간 플러터 프로젝트를 진행하면서 아래 조합을 사용하고 있습니다.
1. GetX 상태/라우트/의존성 관리자
2. GetX의 장점을 극대화 시키기위한 GetX Pattern
3. 서버와 통신을 위한 GraphQL
GetX 패턴과 라이브러리를 사용하면서 각자가 주는 장단점과 함께 시너지 효과가 나는 부분이 있는가 하면, 반대로 같이 사용해서 더 불편하게 느껴진 점도 있었습니다.
GetX의 최대 장점은 코드가 간결해진다는 점입니다. 단순히 다이얼로그를 띄우거나 MediaQuery를 사용할 때 코드뿐만 아니라, 뷰에서 데이터를 받아오기 위한 코드 자체가 확실히 줄어듭니다. 이전에 사용했던 BloC 패턴과 비교해보면
// BloC
BlocBuilder<CounterCubit, int>(
builder: (context, count) => Center(child: Text('$count'),),),
// GetX
Obx(() => Center(child: Text(‘${controller.to.count}’),),
이렇게 특정 스트림을 가져올 필요도 없고, builder를 별도로 만들어줄 필요도 없습니다.
상태관리뿐만 아니라 라우팅, 의존성 주입을 할 때에도 훨씬 코드가 간결해집니다. 예시로 아래 코드는 detailScreen
으로 이동하기 위한 코드입니다.
// 기존 방식
Navigator.push(context,
MaterialPageRoute(builder: (context) => DetailScreen()),);
// GetX
Get.to(DetailScreen());
이렇게 GetX를 사용하면 boilerplate code 보일러플레이트 코드를 줄일 수 있습니다.
![getx_pattern]
다른 상태관리 라이브러리에 비해 GetX의 최대 장점은 ‘코드의 간결성’ 인데, 이를 극대화시키기 위해서는 적절한 구조를 선택하는게 좋죠. 올해 6월에 나온 GetX pattern은 GetX 사용자를 위해 새로운 프로젝트 구조화 방식을 제안하고 있습니다.
provider - repository - controller - UI 로 이어지는 데이터 흐름은 이해하기 어렵지도 않고, 무엇보다 관심사 분리가 확실히 된다는 장점이 있습니다. 한가지 기능을 위해 적어야 할 여러 종류의 코드가 서로 다른 폴더 내에 존재하기 때문입니다.
예를 들어, 유저 프로필 정보를 받아오는 기능을 위해
1. graphQL 쿼리문 작성
2. 쿼리문을 HTTP request에 담아서 전송->response를 받아오는 코드 작성
3. 받아온 response를 model에 맵핑하는 코드 작성
4. 모델로 맵핑된 데이터를 적절히 가공해서 위젯에 전달하는 코드 작성
5. 데이터를 보여주기 위한 위젯 코드 작성
...이런 일을 해야 합니다. 이 모든 작업을 파일 하나에서 처리하고 있다면, 나중에 코드를 다시 보면 각 부분이 어떤 역할을 하는지 구분하기 어렵고 시간도 꽤 오래 걸릴 것입니다. GetX Pattern에서는 다음과 같이 각 코드를 분리하기 때문에 이후 수정 작업이 한결 편해집니다.
- provider: 1, 2번
- repository: 3번
- controller: 4번
- widget: 5번
프로젝트 내에서 자체적으로 만든 기능이 여러군데에서 쓰일 때 GetX의 편리함이 더 크게 느껴집니다.
예를 들어 서버에 이미지를 업로드하는 기능이 있어서 관련된 provider, repository, controller를 모두 작성했다고 합시다. 그런데 이 기능은 글을 작성하는 화면, 프로필 사진을 편집하는 화면, 이미지 댓글을 다는 화면 등 서로 다른 화면에서 필요한 기능입니다. 이럴 때 GetX를 사용하고 있으면, HTTP request 전송 코드나 모델로 맵핑하는 코드 등 일련의 과정을 반복해서 적을 필요 없이 아래처럼 한 줄만 적어주면 됩니다.
ImageController.to.uploadImage();
물론 ImageController
라는 컨트롤러가 메모리상에 남아있어야겠죠. 그 설정도 간단합니다.
Get.put(ImageController(), permanent: true);
위 코드를 바인딩에 한번만 적어주면, 메모리상에 ImageController가 글로벌하게 등록되어서 언제 어디서든 컨트롤러를 호출해서 사용할 수 있습니다.
GetX 패턴(관심사 분리) 과 graphQL(원하는 데이터만 받아오기) 각자의 장점이 있지만 시너지효과를 내는 것 같지는 않습니다.
앞서 살펴본 예시를 다시 가져오면 서버로부터 응답을 받기 위해 이런 과정을 거치게 됩니다. 그런데 GraphQL을 사용하면 1번 과정부터 만만치 않습니다.
1. graphQL 쿼리문 작성
2. 쿼리문을 HTTP request에 담아서 전송->response를 받아오는 코드 작성
3. 받아온 response를 model에 맵핑하는 코드 작성
4. 모델로 맵핑된 데이터를 적절히 가공해서 위젯에 전달하는 코드 작성
5. 데이터를 보여주기 위한 위젯 코드 작성
가장 큰 이유는 다트나 플러터에서 사용할 수 있는 graphql 쿼리문 formatter가 없기 때문입니다. 리액트 프로젝트를 할 때는 쿼리문 formatter가 있어서 들여쓰기나 일부 오타 수정은 쉽게 할 수 있었지만 플러터 프로젝트에서는 사정이 좀 다릅니다. 들여쓰기는 모두 일일이 해줘야하고, $
, ,
과 같은 기호에서 오타가 나버리면 그냥 QueryError
라는 에러만 내뱉을 뿐입니다.
두번째 이유는 코드의 중복이 생기는 것입니다. graphQL 쿼리문에서 적는 코드와 response를 맵핑하는 모델에서 적는 코드가 거의 같습니다.
만약 String name, int id, Gender gender, String address
라는 정보를 받고 싶다면 쿼리문에 name, id, gender, address를,
모델에 String name, int id, Gender gender, String address
를 적어줘야 하는 것이죠.
그래서 같은 작업을 2번 하는 느낌을 지울 수 없습니다. 한 API에서 받고자하는 데이터 양이 많을수록 더 많은 코드가 중복됩니다. 이 부분이 GetX와 graphQL을 같이 사용했을 때 서로 마이너스 효과를 내는 부분입니다.
서버에 post 요청을 보내면서 file 데이터도 함께 보낼 일이 있었습니다. 아무리 해도 서버에서 input file을 제대로 받지 못해, 결국 http 라이브러리를 추가로 깔아서 해결한 적이 있습니다.
또한 TextController와도 약간의 충돌이 있습니다. GetX 방식대로 debounce를 구현해놓고보면, TextController를 달지 않았기 때문에 InputField 내부에 적힌 텍스트를 조절할 수 없는 문제가 생깁니다. 결국에는 GetX에서 제시한 방법을 쓰면서도 controller를 추가로 달아줘야 하는 상황이 생깁니다.
아직까지 깃허브 저장소에 올라온 이슈 개수도 꽤 되는 편이고, 아무래도 나온지 얼마 되지 않아서 세세한 부분까지 완벽하게 커버하지는 못하는 것 같습니다.
편리합니다. GetView
, Get.argument
, Get.dialog
등 직관적이고 간결한 방식을 제공하기 때문에 코드 적기가 매우 수월해집니다. 여기에서 오는 장점이 크기 때문에 다른 프로젝트를 하더라도 GetX를 계속 사용할 것 같습니다.
여담으로, GetX pattern은 GetX를 만든 사람이 공식적으로 인정한 표준은 아니닙니다. GetX의 깃허브 저장소와 GetX Pattern의 깃허브 저장소를 보면 딱히 관련있는 사람이 만든 것 같지 않습니다. GetX pattern의 리드미에 적혀있듯이 이 패턴은 GetX를 사용하는 사람들을 위해 만든 ‘제안’이니, 무조건 이 패턴을 따를 필요는 없어보입니다.
하지만 아무것도 없는 백지 상태에서 폴더 구조를 하나씩 만들어가기는 쉽지 않은 일이고 우리에겐 그럴 시간도 충분하지 않죠.🥲 다행히 GetX 패턴에 대한 문서화가 워낙 잘 되어있고, 기존에 써봤던 BloC 패턴과 유사한 부분도 있어 큰 어려움 없이 getX pattern을 사용하고 있습니다.
잘 읽었습니다~