(Flutter) Riverpod 상태 불변성(Immutability)

반응형

불변성(Immutability)이란?

불변성(Immutability)은 Object의 모든 필드가 final 또는 late final인 경우입니다. 객체 생성할 때 정확히 한 번만 설정됩니다.

불변성은 여러 가지 이유로 바람직합니다.

  • 참조 평등(reference equality)보다 가치 평등(value equality)
  • 코드 조각에 대한 로컬 추론
    • 멀리 떨어진 코드 조각은 참조(reference)를 통해 바로 아래에서 객체를 변경할 수 없습니다.
  • 비동기 및 병렬 작업에 대한 추론이 더 쉬워집니다.
    • 다른 코드는 작업 중간에 객체를 변경할 수 없습니다.
  • API의 안전성
    • 메서드에 전달하는 내용은 호출자/수신자가 변경할 수 없습니다.

copyWith 메서드는 몇 가지만 변경하여 새로운 객체를 만들 때 장황한 내용을 줄이는 데 도움이 됩니다.

다트는 변경되지 않은 하위 객체에 대한 모든 참조를 재사용 할 수 있기 때문에 복사(copy)가 생각보다 효율적입니다.

[!danger] 경고
객체가 완전한 불변인지 확인하세요. 그렇지 않으면 일종의 깊은 복사 메커니즘을 구현해야 합니다.

모범 사례

불변 상태를 생성하는 패키지는 무엇이든 사용할 수 있습니다.

불변 객체의 경우:

불변 컬렉션의 경우(Map, Set, List):

freezed을 사용하는 것을 적극 권장합니다. freezed에는 단순히 불변 객체를 만드는 것 외에도 다음과 같은 몇 가지 멋진 추가 기능이 있기 때문입니다.

  • 미리 생성된 copyWith 메소드
  • 깊은(deep) 복사(중첩되고 고정된 객체에 대한 copyWith)
  • 유니온 타입
  • 유니온 매핑 함수

불변 상태로 작업하기 위해 코드 생성을 사용할 필요는 없지만 훨씬 더 쉽게 작업 할 수 있습니다.

[!danger] 경고
기본 제공 컬렉션을 사용하려면 컬렉션을 업데이트할 때 컬렉션 복사본을 만드는 규칙을 적용해야 합니다. 컬렉션을 복사하지 않을 때의 문제점은 Riverpod가 객체에 대한 참조가 변경되었는지 여부에 따라 새 상태를 내보낼지 여부를 결정한다는 것입니다. 객체를 변경하는 메서드만 호출하기만 하면 참조는 동일합니다.

불변 상태 사용하기

불변 상태는 StateNotifierStateNotifierProvider와 함께 사용하는데 가장 적합합니다.  StateNotifier를 사용하면 상태를 ‘변경’ 할 수 있는 인터페이스를 노출할 수 있습니다. StateNotifier를 확장하는 정의한 클래스 외부에서 상태를 변경할 수 없습니다 . 이를 통해 관심사를 분리하고 비즈니스 로직을 UI 외부에 유지합니다.

다음은 앱 테마를 변경하기 위한 간단한 불변 설정 클래스의 예입니다.

final themeProvider = StateNotifierProvider((ref) => ThemeNotifier());

class ThemeNotifier extends StateNotifier<ThemeSettings> {
  ThemeNotifier(): super(
      ThemeSettings(
        mode: ThemeMode.system,
        primaryColor: Colors.blue,
      ));

  void toggle() {
    state = state.copyWith(mode: state.mode.toggle);
  }
  void setDarkTheme() {
    state = state.copyWith(mode: ThemeMode.dark);
  }
  void setLightTheme() {
    state = state.copyWith(mode: ThemeMode.light);
  }
  void setSystemTheme() {
    state = state.copyWith(mode: ThemeMode.system);
  }
  void setPrimaryColor(Color color) {
    state = state.copyWith(primaryColor: color);
  }
}

@freezed
class ThemeSettings with _$ThemeSettings {
  const factory ThemeSettings({ThemeMode mode, Color primaryColor}) = _ThemeSettings;
}

extension ToggleTheme on ThemeMode {
  ThemeMode get toggle {
    switch (this){
      case ThemeMode.dark:
        return ThemeMode.light;
      case ThemeMode.light:
        return ThemeMode.dark;
      case ThemeMode.system:
        return ThemeMode.system;
    }
  }
}

이 코드를 사용하려면 freezed_annotation을 임포트한 후 part 지시문을 추가하고, build_runner를 실행하여 freezed 클래스를 생성하세요.



or

[카카오페이로 후원하기] [토스페이로 후원하기]

반응형