Home [Flutter] State Management - Provider
Post
Cancel

[Flutter] State Management - Provider

Provider

Provider 패키지는 Flutter의 상태 관리를 단순화하고 향상시키는 데 사용된다. Provider를 사용하면 데이터와 위젯을 분리하고, 상태의 변경을 효율적으로 관리할 수 있으며, 앱 전체에서 쉽게 해당 모델에 접근할 수 있게 한다.

Provider 종류:


Provider 사용의 주요 이점:

  • 상태 분리: 데이터 모델과 UI 로직을 분리하여, 각각의 코드를 더 쉽게 관리하고 유지할 수 있다.

  • 효율적인 리소스 관리: 필요한 위젯만 리스너를 통해 재빌드되므로, 불필요한 리소스 낭비를 줄일 수 있다.

  • 코드 재사용성: Provider를 통해 생성한 데이터 객체는 앱 전체에서 재사용할 수 있다.


Provider상태관리

Provider을 사용하기위해 main.dart 최상위 위젯을 ChangeNotifierProvider로 감싸준다. (Provider가 여러개 사용될 경우, MultiProvider를 사용)

create?

create 콜백은 Provider 패키지에서 ChangeNotifierProvider와 같은 provider 위젯을 사용할 때 필수적이다. 이 콜백은 provider에 의해 관리되는 데이터 모델의 인스턴스를 생성하는 역할을 하고, 여기에는 몇 가지 중요한 이유가 있다:

  1. 초기화: create는 데이터 모델이나 ChangeNotifier 객체를 초기화하는 데 사용된다. 즉, 위젯 트리가 빌드될 때 한 번 호출되어 객체를 생성하고, 이 객체는 Provider에 의해 트리 전체에 제공된다.

  2. 소유권과 생명주기 관리: create에 의해 생성된 객체는 Provider에 의해 소유되며, Provider가 범위에서 벗어날 때 적절히 제거된다. 이렇게 함으로써 메모리 누수를 방지하고 객체의 생명주기를 효과적으로 관리할 수 있다.

  3. 성능 최적화: create는 객체를 필요할 때만 생성한다. 즉, 위젯이 화면에 표시되지 않는 경우 객체가 불필요하게 메모리를 차지하지 않도록 합니다.

  4. 컨텍스트 접근성: create 함수는 BuildContext를 매개변수로 받기 때문에, 생성되는 객체가 컨텍스트를 기반으로 추가적인 초기화 작업을 수행할 수 있다. 예를 들어, 다른 Provider에서 제공하는 데이터에 의존하는 객체를 생성할 때 유용하다.

  5. 유연성: Provider를 사용하면 단일 객체 뿐만 아니라 객체의 리스트나 맵 등 다양한 종류의 데이터를 관리할 수 있으며, create를 통해 이러한 데이터 구조를 초기화할 수 있다.



“그렇다면, 그 값은 어떻게 들을 수 있을까? 다른 화면에서 ChangeNotifier에 어떻게 접근 할 수 있을까?”

위 예시 코드 context.watch<VideoConfig>().isMuted처럼 ‘< >’부분에 접근하고자 하는 ChangeNotifierProvider 타입을 적어주면, 해당 데이터에 접근 할 수 있다.


ChangeNotifierProvider의 타입을 참조하는 이유?

BuildContext를 통해 특정 타입의 ChangeNotifier를 올바르게 찾아 리스닝하도록 하기 위함이다. 제네릭 타입 T를 사용하여 Provider의 타입을 지정함으로써, Provider 트리에서 해당 타입의 가장 가까운 인스턴스를 찾을 수 있다.

1
2
3
4
5
6
7
8
9
*제네릭 타입?

제네릭 타입 T는 프로그래밍에서 사용되는 일종의 플레이스홀더 또는 매개변수화된 타입을 의미한다. 제네릭을 사용하면 같은 코드를 다양한 데이터 타입에 대해 재사용할 수 있다. 즉, 타입을 직접 명시하는 대신에, 타입 매개변수 T를 사용하여 코드를 보다 일반적이고 유연하게 작성할 수 있다.

예를 들어, Dart에서 List<T>는 T라는 타입 매개변수를 사용하는 제네릭 리스트이다. 여기서 T는 리스트에 들어갈 요소의 타입을 나타낸다. List<int>는 정수 타입의 요소만을 갖는 리스트를, List<String>은 문자열 타입의 요소만을 갖는 리스트를 의미한다.

Flutter에서 Provider를 사용할 때 ChangeNotifierProvider<T> 같은 형태로 T를 사용하는 것도 같은 원리이다. 여기서 T는 ChangeNotifierProvider가 제공할 데이터 모델의 타입을 지정한다. 이렇게 함으로써 Provider 트리에서 해당 타입의 객체를 찾을 수 있고, context.watch<T>() 또는 context.read<T>()와 같은 메서드를 사용하여 타입 안전성을 보장하며 해당 데이터에 접근할 수 있다.

이렇게 제네릭 타입을 사용함으로써 타입 안전성을 확보하고, 코드의 재사용성을 높이며, 개발자의 의도를 명확하게 전달할 수 있다.


ChangeNotifierProvider의 타입을 참조하지 않으면?

타입 T를 지정하지 않으면, Provider는 기본적으로 dynamic 타입으로 처리되고, 컴파일 타임에 타입 체크를 수행할 수 없다. 이는 다음과 같은 문제를 일으킬 수 있다:

  • 타입 안정성 부족: 특정 타입을 기대하는데, 다른 타입이 반환되면 런타임 오류가 발생한다.

  • 디버깅 어려움: 오류가 발생했을 때 문제의 원인을 찾기 어렵다.

  • 코드 가독성 저하: 타입이 명시되지 않으면 코드를 읽는 다른 개발자에게 혼란을 줄 수 있다.


context.watch() / context.read()

Flutter의 provider 패키지에서 context.watch<T>()context.read<T>()Provider로부터 데이터를 가져오는 두 가지 다른 방법이다. 각각의 메서드는 상이한 사용 시나리오와 목적을 가지고 있다.

context.watch()

  • 목적: watch는 위젯을 해당 타입 T의 데이터에 대한 리스너로 등록한다. 즉, 데이터 T가 변경될 때마다 위젯이 재빌드된다.

  • 사용 시나리오: watch는 주로 build 메서드 내부에서 사용되며, 해당 데이터의 변경을 UI에 반영해야 할 때 사용된다.

  • 동작 방식: 위젯이 Provider의 데이터를 “구독”하게 되고, 데이터가 변경될 때마다 ProvidernotifyListeners() 호출을 통해 watch를 사용하는 위젯에게 데이터 변경을 알린다. 그러면 watch를 사용하는 위젯은 자동으로 재빌드된다.

context.read()

  • 목적: read는 단순히 데이터를 한 번 읽어오는 데 사용된다. watch와 달리 위젯을 데이터의 리스너로 만들지 않는다.

  • build 메서드 외부에서 사용될 때가 많으며, 주로 이벤트 핸들러나 생명주기 메서드 내에서 데이터에 접근할 때 사용된다.

  • 사용 시나리오: read는 위젯이 데이터를 단순히 읽기만 하고, 그 데이터의 변경에 의해 위젯이 재빌드될 필요가 없을 때 사용된다.

  • 동작 방식: read를 호출할 때, Provider는 데이터 인스턴스를 반환하지만, 이를 호출한 위젯을 데이터 변경에 대한 리스너로 등록하지는 않는다. 따라서 데이터가 변경되어도 read를 호출한 위젯은 재빌드되지 않는다.


watch는 데이터의 변화를 감시하고 변화가 있을 때마다 위젯을 재빌드하기 위해 사용되며, read는 데이터를 한 번만 읽어올 때 사용되어 위젯의 재빌드 없이 데이터에 접근하고자 할 때 적합하다.”

setting 화면 설정에 따른 VideoScreen 상태변화




This post is licensed under CC BY 4.0 by the author.