공부 일기/TIL

Flutter 창업반 6주차 TIL3-1 RiverPod-책검색 앱 만들기, 웹 개발 관련

oosuhada 2024. 12. 4. 12:37

Flutter 창업반 6주차 TIL3 RiverPod-책검색 앱 만들기, 웹 개발에 대한 인사이트

 

1) 네이버 API를 이용, 책검색 앱을 구현함

https://github.com/oosuhada/flutter_book_search_app

 

 

 

2) 모바일 환경 최적화를 위해 lib/ui/pages/detail/detail_page.dart에서 코드를 수정

import 'package:flutter/material.dart';
import 'package:flutter_book_search_app/data/model/book.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';

class DetailPage extends StatelessWidget {
  DetailPage(this.book);

  Book book;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(book.title),
      ),
      body: InAppWebView(
        initialSettings: InAppWebViewSettings(
          mediaPlaybackRequiresUserGesture: true,
          javaScriptEnabled: true,
          userAgent:
              'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
        ),
        initialUrlRequest: URLRequest(
          url: WebUri(book.link),
        ),
      ),
    );
  }
}
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter_book_search_app/data/model/book.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';

class DetailPage extends StatelessWidget {
  DetailPage(this.book);

  final Book book;

  bool isMobile() {
    return !kIsWeb;
  }

  String getAppropriateLink() {
    if (Platform.isAndroid || Platform.isIOS) {
      return book.link.replaceFirst('https://search.shopping.naver.com',
          'https://msearch.shopping.naver.com');
    }
    return book.link;
  }

  String getAppropriateUserAgent() {
    if (isMobile()) {
      return 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1';
    } else {
      return 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36';
    }
  }

  @override
  Widget build(BuildContext context) {
    String appropriateLink = getAppropriateLink();
    String appropriateUserAgent = getAppropriateUserAgent();

    return Scaffold(
      appBar: AppBar(
        title: Text(book.title),
      ),
      body: InAppWebView(
        initialSettings: InAppWebViewSettings(
          mediaPlaybackRequiresUserGesture: true,
          javaScriptEnabled: true,
          userAgent: appropriateUserAgent,
        ),
        initialUrlRequest: URLRequest(
          url: WebUri(appropriateLink),
        ),
      ),
    );
  }
}

 

 

 

 

3) Chrome 웹브라우저에서는 ClientException: XMLHttpRequest error., uri=https://openapi.naver.com/v1/search/book.json?query=%ED%95%B4%EB%A6%AC 에러와 함께 API 연동이 안되고 있음

 

 

4) 위 내용 확인 후 튜터님 찾아가서 Flutter 개발, SEO, 웹개발 관련 여러 이야기를 나눔

 

1. SEO와 플러터 개발
플러터는 공식적으로 SEO를 지원하지 않습니다
SEO 작업을 위해서는 웹 프론트엔드 지식이 필요합니다.
앱 개발에서 SEO는 특수한 경우이며, 대부분의 회사에서는 앱에 대한 SEO 니즈가 없습니다.

특히 플러터와 같은 SPA(Single Page Application)는 기본적으로 SEO에 적합하지 않습니다.

 

2. 백엔드 개발과 서비스 구축
초기 단계에서는 백엔드 개발이 필수적이지 않을 수 있습니다.
MVP(Minimum Viable Product) 개발 시 모든 기능을 완벽하게 구현할 필요는 없습니다.
에스크로 서비스와 같은 결제 시스템은 직접 개발하기보다 외부 서비스를 이용하는 것이 초기에는 비용 효율적일 수 있습니다.

 

3. 웹과 앱 개발의 차이점
웹과 앱 개발은 파일 처리, 보안 등에서 차이가 있습니다.
웹은 설치형이 아닌 서버 제공 페이지라는 특수성이 있습니다.
내부 데이터베이스 사용에 있어 웹과 앱은 차이가 있습니다.

 

4. 플러터를 이용한 웹과 앱 동시 개발
웹과 앱을 동시에 개발하는 것은 생각보다 어려울 수 있습니다.
UI/UX가 플랫폼별로 다르기 때문에 동시 개발 시 고려해야 할 사항이 많습니다.
초기에는 웹과 앱 프로젝트를 분리하여 개발하는 것이 추천됩니다.

 

5. 개발 전략
MVP 개발에 집중하고, 점진적으로 기능을 추가해 나가는 것이 좋습니다.
초기 서비스는 완벽하지 않아도 되며, 시간이 지나면서 개선해 나갈 수 있습니다.
플러터는 앱 개발 시 생산성이 높은 기술 스택 중 하나입니다.
이 멘토링 세션은 개발 초보자에게 실용적인 조언을 제공하며, 특히 MVP 개발과 점진적 서비스 확장의 중요성을 강조하고 있습니다.

 

5) Flutter Web 개발 관련 추가적 조사 내용

 

## Flutter Web과 SPA의 결합: 현대 웹 개발의 새로운 지평

### 서론

웹 개발 환경이 빠르게 변화하면서, 다양한 플랫폼에서 효율적으로 동작하는 강력한 솔루션에 대한 수요가 증가하고 있습니다. 이러한 맥락에서 Google의 Flutter와 Single Page Application(SPA) 개념의 결합은 주목할 만한 접근 방식입니다.

### Flutter Web 소개

Flutter Web은 Dart 언어 기반의 UI 프레임워크로, 모바일, 웹, 데스크톱 플랫폼에서 일관된 사용자 인터페이스를 제공합니다. 주요 특징으로는:

- 다중 플랫폼 호환성
- 위젯 기반 UI
- Hot Reload 기능

### SPA(Single Page Application) 개요

SPA는 단일 HTML 페이지를 기반으로 동적으로 콘텐츠를 업데이트하는 웹 애플리케이션 형태입니다. 

주요 특징은:

- 향상된 사용자 경험
- 빠른 로딩 속도
- 개발 생산성 향상


### Flutter Web과 SPA의 실제 적용

AI VOUCHER 사업의 산출물인 ABC diet 웹 솔루션에서 Flutter Web과 SPA 개념이 어떻게 적용되었는지 살펴보겠습니다.

1. 전통적인 MPA vs SPA 접근 방식

 

화면은 AI VOUCHER 사업의 산출물로 개발된 웹 솔루션(ABC diet)의 대시보드 화면이다. URL Path의 끝이 Template임을 확인할 수 있다. 만약 여기서 '대시보드'가 아닌 '식단 처방' 탭을 누른다면, 전통적인 MPA 방식으로 구현된 웹 애플리케이션일 경우 '식단 처방'페이지의 뷰를 가지고 있는 HTML을 서버에 요청하고 반환된 화면을 보여줄 것이다.

그림: MPA(Multiple Page Application) 라이프사이클

출처: https://velog.io/@sangyoung23/MPA-SPA-CSR-SSR-%EA%B0%84%EB%8B%A8-%EC%A0%95%EB%A6%AC

 

2. GetX 상태관리 라이브러리를 활용한 SPA 구현

 

어떻게 이런 일이 가능하게 했을까? SPA의 라이프 사이클을 보겠다.

그림: SPA(Single Page Application) 라이프사이클

출처: https://velog.io/@sangyoung23/MPA-SPA-CSR-SSR-%EA%B0%84%EB%8B%A8-%EC%A0%95%EB%A6%AC

 

통상 페이지의 전체가 아닌 일부 뷰의 변화시켜 줄 때는 Javascript의 Ajax를 사용한다. 서버에 GET이나 POST 등을 요청하여 JSON 데이터를 받아오면, 페이지 전체가 아닌 화면의 일부를 변화시켜서 불필요한 비용을 줄일 수 있다. 쉽게 말하면 페이지에서 상태가 변하지 않는 부분까지 깜빡거리는 것을 방지할 수 있다.

 

3. 뷰 모델의 효율적인 의존성 주입 방법

GetX 상태관리 라이브러리를 이용하여 이를 구현했다. 

 

혹자는 Template 페이지의 컨트롤러에 모든 비즈니스 로직이 있는 것은 아닐까하여 리소스의 낭비를 우려할 수 있을 것이다.

이에 필자는 해당 뷰가 보일 때 뷰와 관련된 뷰 모델의 의존성을 주입하는 방법으로 리소스의 최적화가 가능하게 하였다. 즉, 뷰가 바뀌면 기존의 뷰 모델을 소멸시키며, 데이터가동기화 되기 전까지 로딩 화면을 보여준다.

 

### 결론

Flutter Web과 GetX 라이브러리를 활용한 SPA 구현은 강력한 상태 관리와 편리한 라우팅을 제공합니다. 이 접근 방식은 빠르게 변화하는 웹 개발 환경에서 높은 생산성과 성능을 달성할 수 있게 해줍니다.

 

5) GetX 개발 관련 추가적 조사 내용

Flutter의 구조

Flutter에서 상태관리가 필요한 배경이 무엇인지 알아보겠습니다. Flutter는 모든 것이 ‘Widget’으로 이루어져 있습니다. Java에서 모든 객체가 Object라는 클래스를 상속받듯이 Flutter에서 UI와 관련된 모든 것은 Widget입니다. 화면 역시 두가지의 Widget으로 분류할 수 있습니다.

1.StatelessWidget

2.StatefulWidget

StatelessWidget은 변하지 않는 정적인 화면을 구성할때 사용합니다. 단순 텍스트나 앱에 대한 정보 화면 등이 이 경우에 해당합니다. StatefulWidget보다 성능이 좋습니다.

StatefulWidget은 화면에 변화가 있는 동적인 화면을 구성할때 사용합니다. 체크박스나 라디오박스, 사용자가 텍스트를 입력하는 필드, 게임의 점수 등 실시간으로 업데이트되는 UI가 포함된 화면의 경우 StatefulWidget을 사용하게 됩니다.

코드로 보면 다음과 같습니다.

StatelessWidget

class StatelessExample extends StatelessWidget {
  const StatelessExample({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return  Container(
      color: Colors.blue,
      child: const Text('StatelessWidget Example'),
    );
  }
}

StatefulWidget

 import 'package:flutter/material.dart';
 
 class StatefulExample extends StatefulWidget {
   const StatefulExample({Key? key}) : super(key: key);
 
   @override
   State<StatefulExample> createState() => _StatefulExampleState();
 }
 
 class _StatefulExampleState extends State<StatefulExample> { // setState() 의 영향을 받는 영역
     int _counter = 0;
 
   @override
   Widget build(BuildContext context) { // UI가 렌더링 되는 영역
     return Scaffold(
       appBar: AppBar(
         title: const Text('StatefulWidget Example'),
       ),
       body: Center(
         child: ElevatedButton(
           child: Text(
             '현재 숫자: $_counter',
           ),
           onPressed: () {
             setState(() { // 누르는 순간 재렌더링
               _counter++;
             });
           },
         ),
       ),
     );
   }
 }

StatelessWidget은 클래스 내에 생성자와 build() 함수 하나만 있습니다. 하지만 StatefulWidget은 StatelessWidget과 달리 State 형식의 또다른 서브클래스가 존재하는데 이 서브클래스가 동적인 화면을 렌더링하게 됩니다. 위의 예시는 버튼을 누를때마다 카운터가 증가되는 동적인 화면의 예시입니다. setState() 함수를 통해 화면 변경된 데이터로 화면을 재렌더링(build() 함수)하게 됩니다.

Stateful Widget 코드예시 결과

상태 관리란

상태관리는 UI에서 실시간으로 변하는 여러 데이터들의 상태를 효율적으로 관리하기 위한 개념입니다. 예시를 들어 설명해보겠습니다.

위의 화면을 보면 여러 데이터를 확인할 수 있습니다.

  • 글쓴이 프로필 이미지
  • 글쓴이 닉네임
  • 글의 작성 시간
  • 글의 카테고리
  • 글의 좋아요
  • 댓글 갯수
  • 댓글 쓴 사람들의 목록
  • 나의 글/댓글 좋아요 눌렀는지 여부 등

한 화면에도 여러 데이터들이 있는 것을 확인할 수 있습니다. 이 화면에서 상태관리가 필요한 이유는 크게 두가지가 있습니다.

1.특정 데이터가 바뀔 때 마다 화면 전체를 재렌더링 하기에는 애플리케이션에서의 자원의 낭비가 너무 크다.

2.특정 데이터가 바뀔 때 다른 화면에서도 해당 데이터의 변화가 동일하게 이루어져야 하는 경우가 있다.

1번의 예시는 다음과 같습니다. 사용자가 댓글을 입력하고 업로드 하는 순간 댓글 목록에 새로운 댓글이 보여져야 합니다. 화면의 관점에서 새로운 데이터가 생기고 그에 따라 새로운 UI를 그려주어야 하기 때문에 Flutter는 StatefulWidget으로 해당 화면을 재렌더링 하게 됩니다. 그 외에도 글이나 댓글의 하트를 누를 경우에도, 댓글을 삭제하고 수정할때도 Flutter는 화면의 일부를 변경하기 위해 화면 전체를 재렌더링 하게 됩니다. 하지만 화면의 일부분의 변경을 적용하기 위해 화면 전체를 재렌더링 하는 방식은 너무 비효율적입니다.

2번의 예시는 다음과 같습니다. 사용자가 해당 글에 하트를 눌러서 하트 UI가 노란색 하트로 변경이 되었습니다. 만약에 해당 글에 하트를 눌렀는지의 여부 데이터를 다른 페이지에서도 참고하고 있다면 해당 페이지에서도 하트가 노란색 하트로 변경이 되어야 합니다.

Flutter가 디폴트로 제공하는 StatefulWidget을 통해서도 기능은 구현이 되지만 애플리케이션이 복잡해질수록 setState() 로 전체 화면을 재렌더링 하는 방식은 비효율성이 애플리케이션에 규모에 비례하여 더 커지게 됩니다.

상태관리 기술을 사용하게 되면 1번 처럼 실시간으로 변화하는 데이터에 대한 처리와 2번처럼 여러 컴포넌트에서 공통적으로 사용하는 데이터의 동기화를 아주 쉽고 효율적으로 해결할 수 있습니다.

GetX 상태 관리 라이브러리

Flutter에서 상태관리의 필요성에 대해 알아보았으니 이제 상태관리를 적용하는 방법을 알아보겠습니다. React에서 대표적인 상태관리 라이브러리가 Redux가 있다면 Flutter에서는 GetX를 주로 사용합니다.

라이브러리 사용 설정

  • 라이브러리 import
    • 프로젝트 경로에서 위의 커맨드로 라이브러리를 import 합니다.
    • 명령어 대신에 pubspec.yaml 파일에 직접 입력하여 추가하는 방법도 있습니다. 이 경우 버전 명시도 가능합니다.
  •  $ flutter pub add get
    
  • 라이브러리 동기화
    • 프로젝트 경로에서 위의 커맨드로 라이브러리를 현재 프로젝트와 동기화 합니다.
    • 명령어 대신에 Android Studio 에디터에서 pubspec.yaml 파일을 열고 오른쪽 상단에 ‘Packeges get’ 버튼을 클릭하는 방법도 있습니다.
  •  $ flutter pub get
    
  • 라이브러리 사용
    • 라이브러리를 사용하는 dart 파일에서 import합니다.
    void main() {
      runApp(const GetMaterialApp(home: MyApp()));
    }
    
    • 프로젝트 main() 함수안에 첫페이지를 시작하는 부분을 GetMaterialApp() 으로 감싸줍니다.
  • import 'package:get/get.dart';
    

사용법

GetX를 통한 상태관리 방식은 크게 두가지가 있습니다.

1.단순 상태 관리

2.반응형 상태 관리

단순 상태 관리와 반응형 상태 관리의 차이는 반응형 상태 관리의 경우 데이터가 변화가 있을 때만 재랜더링을 하게 되는 반면에 단순 상태 관리는 기존의 데이터와 변경되는 데이터가 같은지 확인하지 않습니다. 더 나아가 반응형 상태관리는 workers라는 추가 기능도 있습니다. 두가지 방식의 상태 관리를 코드로 확인해보겠습니다.

1. 단순 상태 관리

import 'package:get/get.dart';

class SimpleController extends GetxController {
  int counter = 0;

  void increase() {
    counter++;
    update();
  }
}
  • 단순 상태 관리를 위한 controller를 만들어줍니다. 이 controller는 counter라는 변수와 counter의 값을 1씩 증가해주는 increase() 함수를 가지고 있습니다. increase() .함수 안에 update()는 이 controller을 바라보고있는 모든 코드에 업데이트를 알리는 역할을 합니다.
class MyHomePage extends StatelessWidget {
  MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    Get.put(SimpleController()); // controller 등록
    return Scaffold(
      appBar: AppBar(
        title: const Text("단순 상태관리"),
      ),
      body: Center(
        child: GetBuilder<SimpleController>( // 실시간 렌더링
          builder: (controller) {
            return ElevatedButton(
              child: Text(
                '현재 숫자: ${controller.counter}',
              ),
              onPressed: () {
                controller.increase();
                // Get.find<SimpleController>().increase();
              },
            );
          },
        ),
      ),
    );
  }
}
  • 위에서 만들어준 controller를 사용하는 화면 클래스입니다. 먼저 controller를 사용하기 위해 Get.put()으로 controller를 등록해줍니다. GetBuilder()아래의 모든 위젯은 controller에서 변경되는 데이터를 실시간으로 반영할 수 있는 상태가 됩니다. controller.counter는 controller의 변수를 실시간으로 반영하게 되고 controller.increase()는 controller의 counter 데이터를 실시간으로 증가시키게 됩니다. 만약 GetBuilder를 사용하지 않을 경우 Get.find<[Controller종류]>().[변수 혹은 함수] 로 컨트롤러의 데이터를 실시간 변경 혹은 반영할 수 있습니다.

결과

2. 반응형 상태 관리

import 'package:get/get.dart';

class ReactiveController extends GetxController {
  RxInt counter = 0.obs;

  void increase() {
    counter++;
  }
}
  • 반응형 상태 관리를 위한 Controller를 만들어줍니다. 이 Controller는 counter라는 변수와 counter의 값을 1씩 증가해주는 increase() 함수를 가지고 있습니다.
  • 단순 상태관리와 비교하면 변수를 선언하는 방식과 업데이트 함수 부분이 다릅니다. 변수를 선언하는 방식은 변수의 타입을 RxInt, RxString등 Rx{타입}의 방식으로 선언하고 변수의 값은 ‘.obs’를 붙이게 됩니다. 업데이트의 경우 update() 함수를 부르지 않아도 됩니다.
class MyHomePage extends StatelessWidget {
  MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    Get.put(SimpleController()); // 단순 상태 관리 controller 등록
    Get.put(ReactiveController()); // 반응형 상태 관리 controller 등록
    return Scaffold(
      appBar: AppBar(
        title: const Text("단순 / 반응형 상태관리"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            GetBuilder<SimpleController>( // 단순 상태 관리
              builder: (controller) {
                return ElevatedButton(
                  child: Text(
                    '[단순]현재 숫자: ${controller.counter}',
                  ),
                  onPressed: () {
                    controller.increase();
                    // Get.find<SimpleController>().increase();
                  },
                );
              },
            ),
            GetX<ReactiveController>( // 반응형 상태관리 - 1
              builder: (controller) {
                return ElevatedButton(
                  child: Text(
                    '반응형 1 / 현재 숫자: ${controller.counter.value}', // .value 로 접근
                  ),
                  onPressed: () {
                    controller.increase();
                    // Get.find<ReactiveController>().increase();
                  },
                );
              },
            ),
            Obx( // 반응형 상태관리 - 2
                  () {
                    return ElevatedButton(
                      child: Text(
                        '반응형 2 / 현재 숫자: ${Get.find<ReactiveController>().counter.value}', // .value 로 접근
                      ),
                      onPressed: () {
                        Get.find<ReactiveController>().increase();
                      },
                    );
              },
            ),
          ],
        ),
      ),
    );
  }
}
  • 단순 상태 관리를 테스트했던 화면에 그대로 반응형 상태관리 테스트를 위한 위젯을 추가한 코드입니다. 먼저 단순 상태 관리와 동일하게 controller를 사용하기 위해 Get.put()으로 controller를 등록해줍니다. 반응형 상태 관리에서 데이터를 실시간으로 반영하는 방식에는 두가지가 있습니다.
    1. GetX() - GetX() 아래의 모든 위젯은 controller에서 변경되는 데이터를 실시간으로 반영할 수 있는 상태가 됩니다. controller.counter.value (단순 상태 관리와 다르게 .value 를 추가해 주어야 합니다) 는 controller의 변수를 실시간으로 반영하게 되고 controller.increase()는 controller의 counter 데이터를 실시간으로 증가시키게 됩니다. 만약 GetX를 사용하지 않을 경우 Get.find<[Controller종류]>().[변수 혹은 함수] 로 컨트롤러의 데이터를 실시간 변경 혹은 반영할 수 있습니다.
    2. Obx() - Obx() 아래의 모든 위젯은 GetX()와 마찬가지로 controller에서 변경되는 데이터를 실시간으로 반영할 수 있는 상태가 됩니다. 사용 방식은 거의 동일하지만 차이가 있다면 GetX()와 달리 controller의 이름을 지정할 수가 없어서 Get.find() 방식으로 접근해야 합니다.

결과

반응형 상태 관리의 추가 기능 - worker

반응형 상태관리에서는 worker라는 추가 기능이 있습니다. Worker는 controller 안에서 onInit() 함수를 override하고 그 안에 추가해서 사용하게 되는데 아래의 4가지 종류가 있습니다.

  • Ever : 매번 변경 될 때 실행
  • Once : 처음 변경 되었을 때만 실행
  • Interval : 계속 변경이 있는 동안 특정 지정 시간 인터벌이 지나면 실행
  • Debounce : 인터벌이 끝나고 나서 특정 지정 시간 이후에 한번만 실행
import 'package:get/get.dart';

class ReactiveController extends GetxController {
  static ReactiveController get to => Get.find();
  RxInt counter = 0.obs;

  @override
  void onInit() {
    once(counter, (_) {
      print('once : $_이 처음으로 변경되었습니다.');
    });
    ever(counter, (_) {
      print('ever : $_이 변경되었습니다.');
    });
    debounce(
      counter,
          (_) {
        print('debounce : $_가 마지막으로 변경된 이후, 1초간 변경이 없습니다.');
      },
      time: Duration(seconds: 1),
    );
    interval(
      counter,
          (_) {
        print('interval $_가 변경되는 중입니다.(1초마다 호출)');
      },
      time: Duration(seconds: 1),
    );
    super.onInit();
  }


  void increase() {
    counter++;
  }
}
  • 반응형 controller 내부에 worker들을 추가한 모습입니다.

결과

Get.find() 를 보다 간단하게 사용하는 방법

1. Getter 사용

  • Get.find<[Controller종류]>().[변수 혹은 함수]를 보다 간단하게 사용하기 위해서는 아래와 같이 controller 내부에 getter를 생성해주면 됩니다.
class SimpleController extends GetxController {
  static SimpleController get to => Get.find();
  ...
 }
  • Get.find()를 기존보다 더 짧은 코드로 사용할 수 있게 됩니다.
// 전
Get.find<SimpleController>().increase();
// 후
SimpleController.to.increase();

2. GetView 사용

  • Get.find()를 사용하는 클래스에 StatelessWidget 대신 GetView를 상속받는 방식입니다.
// 전
class SimpleState extends StatelessWidget{}
// 후
class SimpleState extends GetView<SimpleController>{}
  • Get.find()를 기존보다 더 짧은 코드로 사용할 수 있게 됩니다.
// 전
Get.find<SimpleController>().increase();
// 후
controller.increase();

GetX 적용 코드

SSOK앱에 GetX를 적용한 코드의 일부분입니다.

  • 달력 페이지의 데이터를 관리하는 controller입니다.

  • 달력 페이지에서 controller를 구독하고 있는 모습입니다.

결과

정리

Flutter에서 GetX 라이브러리를 통한 상태관리에 대해서 알아보았습니다. GetX말고도 Provider나 Bloc 패턴이 존재하지만 현재 시점에서 GetX가 간단한 문법과 여러가지 기능들을 가지고 있어서 가장 인기가 많은 것 같습니다.