Freezed 정리

원장·2025년 6월 16일

플러터 기초

목록 보기
29/36

강의는 들었는데 팰놈이 없다.

위기는 기회, Freezed를 패보자.

무엇인가?

불변 데이터 클래스(Immutable Data Class)를 쉽게 만들어주는 패키지다.

풀어서 설명하면 Dart에서 immutable, copyWith, ==, hashCode, toString, union/sealed class 등을 자동 생성해주는 코드 생성 패키지

문득 생각이 든게 DB데이터 변환하고 하다보면 킹받는데 얘로 다 끝낼 수 있을 것 같다.

그래서 봤지만 한계는 명확하다고 한다.

@freezed
class Car with _$Car {
  const factory Car({
    required String model,
    required int year,
    required Engine engine,
  }) = _Car;

  factory Car.fromJson(Map<String, dynamic> json) => _$CarFromJson(json);
}
final car = Car.fromJson(jsonMap);
final updated = car.copyWith(year: 2025);

print(updated); // 디버깅도 깔끔

대충 위처럼 편하게 데이터 처리 가능.

freezed + Riverpod

나는 riverpod이 좋다 flutter하면서 제일 처음 배웠기 때문. 첫사랑같은 느낌.

그래서 freezed + riverpod 조합에 대해 파보려고함.

사용자 상태 관리

import 'package:freezed_annotation/freezed_annotation.dart';

part 'user_model.freezed.dart';
part 'user_model.g.dart';

@freezed
class User with _$User {
  const factory User({
    required String name,
    required int age,
  }) = _User;

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}

모델을 위처럼 freezed로 예쁘게 만들어주고,

import 'package:freezed_annotation/freezed_annotation.dart';
import 'user_model.dart';

part 'user_state.freezed.dart';

@freezed
class UserState with _$UserState {
  const factory UserState.initial() = _Initial;
  const factory UserState.loading() = _Loading;
  const factory UserState.data(User user) = _Data;
  const factory UserState.error(String message) = _Error;
}

상태 클래스도 만들어준다.

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'user_state.dart';
import 'user_model.dart';

class UserNotifier extends StateNotifier<UserState> {
  UserNotifier() : super(const UserState.initial());

  Future<void> fetchUser() async {
    state = const UserState.loading();

    try {
      // 실제 API 호출이 있다고 가정
      await Future.delayed(const Duration(seconds: 2));
      final user = User(name: '장원', age: 30);
      state = UserState.data(user);
    } catch (e) {
      state = UserState.error('사용자 정보를 불러오지 못했습니다');
    }
  }
}

final userProvider = StateNotifierProvider<UserNotifier, UserState>((ref) {
  return UserNotifier();
});

상태 Notifier도 만들어준다.

final userState = ref.watch(userProvider);

userState.when(
  initial: () => const Text("초기 상태"),
  loading: () => const CircularProgressIndicator(),
  data: (user) => Text("안녕하세요 ${user.name}님"),
  error: (msg) => Text("에러: $msg"),
);

그럼 화면에서 위와 같이 사용할 수 있다.

화면은 Notifier에 의해 변경되고, 미리 정의한 상태와 모델을 통해 데이터가 깔끔해짐.

이 때, 역할

하지만 위의 예제로는 Notifier를 쓸 필요는 없다

유저와의 상호작용이 없는 1회용 호출이기 때문이다.

이럴 때는 그냥 FutureProvider를 통해 불러오면 깔끔하다고함.

한 김에, AsyncNotifier

FutureProvider + Notifier의 장점만 결합한 최신 상태 관리 방식이라고함.

대충 봤는데, 너무 복잡하다.

편하게 만들어줘 누가 ㅠㅠ

난 못해..

freezed 심화

freezed 기본 세부 옵션들

@Freezed(
  toJson: true, // toJson 자동 생성
  fromJson: true, // fromJson 자동 생성
  makeCollectionsUnmodifiable: true, // List, Map을 unmodifiable하게
  equal: true, // == 및 hashCode 자동 생성 (기본 true)
)

default 설정

const factory User({
  @Default('이름없음') String name,
  @Default(0) int age,
}) = _User;

쓰다보니 엄청 당연한 것만 있는데.. 이걸 안쓰면 얼마나 개차반으로 써야하는거지?

freezed 이전에는 어떻게 썼을까?

freezed가 너무 당연하게 느껴질 정도로 편한 이유는, 이전 방식이 고통 그 자체였기 때문이라고한다.

대충 다 만들었어야 한다고 한다.

수작업한 버전

class User {
  final String name;
  final int age;

  const User({required this.name, required this.age});

  User copyWith({String? name, int? age}) {
    return User(
      name: name ?? this.name,
      age: age ?? this.age,
    );
  }

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is User &&
          runtimeType == other.runtimeType &&
          name == other.name &&
          age == other.age;

  @override
  int get hashCode => name.hashCode ^ age.hashCode;

  @override
  String toString() => 'User(name: $name, age: $age)';
}

막 더럽진 않은데..?

중복 코드도 심하고, 실수할 여지가 있어서 freezed로 생성하는게 맘 편하다고함.

freezed 만든 사람은 어떤 사람일까?

Remi Rousselet이라는 사람이라고 한다.

이력을 찾아봤다.

Invertase 소속의 프로그래머고, 프랑스 파리에 산다고 한다.

42(프랑스의 코딩스쿨)를 졸업했고, 7년 이상의 경력이 있다고 한다.

주요 기여는 freezed, riverpod, provider, flutter_hooks 등을 개발 및 유지하고 있다고한다.

Deeply in love with Flutter 라고 말할 정도로 플러터를 사랑한다고함.

작고 강력한 코드 모델을 만드는 것이 큰 의미가 있다를 반복해서 쓴다고 한다.

이 사람이 riverpod에서는 도대체 어떤걸 한건지 궁금해짐.

Riverpod 저장소의 많은 기능 구조(Provider composition, scoping, Future/Stream 지원 등)는 Remi가 직접 설계하고 구현했다고 함.

https://youtu.be/QS3XtFoFN1I

유튜브도 나오고

https://youtu.be/BJtQ0dfI-RA

flutter 공식 채널에도 나오셨군..

이런 개발자가 되도록 열심히 해야겠다..

profile
나 원장이 아니다

0개의 댓글