🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

Riverpod 내부 아키텍처 분석 - 슬라이드 1/7
A

AI Generated

2025. 11. 30. · 21 Views

Riverpod 내부 아키텍처 분석

Flutter 상태 관리의 핵심인 Riverpod의 내부 동작 원리를 파헤칩니다. 패키지 구조부터 ProviderElement, 구독 메커니즘, 리빌드 최적화, autoDispose까지 깊이 있게 살펴보고, 오픈소스 기여 방법까지 안내합니다.


목차

  1. 패키지_구조_이해
  2. ProviderElement의_역할
  3. 상태_구독_메커니즘
  4. 리빌드_최적화_원리
  5. autoDispose_구현_분석
  6. 코드_기여_가이드

1. 패키지 구조 이해

김개발 씨는 Riverpod을 6개월째 사용하고 있습니다. 어느 날 동료가 물었습니다.

"Riverpod 내부가 어떻게 돌아가는지 알아?" 김개발 씨는 말문이 막혔습니다. 매일 쓰면서도 정작 속을 들여다본 적이 없었던 것입니다.

Riverpod은 단일 패키지가 아니라 여러 패키지로 구성된 모노레포 구조입니다. 마치 레고 블록처럼 핵심 엔진과 확장 기능이 분리되어 있습니다.

이 구조를 이해하면 필요한 기능만 정확히 선택해서 사용할 수 있습니다.

다음 코드를 살펴봅시다.

// Riverpod 패키지 구조 살펴보기
// pubspec.yaml에서 필요한 패키지 선택

dependencies:
  # 핵심 엔진 - Flutter 없이도 동작
  riverpod: ^2.4.0

  # Flutter 위젯 통합
  flutter_riverpod: ^2.4.0

  # 코드 생성 지원 (선택)
  riverpod_annotation: ^2.3.0

dev_dependencies:
  # 코드 제너레이터
  riverpod_generator: ^2.3.0
  build_runner: ^2.4.0

김개발 씨는 퇴근 후 집에서 Riverpod GitHub 저장소를 열어보았습니다. packages 폴더 안에 여러 개의 하위 패키지가 나란히 놓여 있었습니다.

"아, 이렇게 나뉘어 있었구나." Riverpod의 패키지 구조는 크게 네 가지로 나눌 수 있습니다. 가장 밑바닥에는 riverpod 패키지가 있습니다.

이것은 순수 Dart로만 작성된 핵심 엔진입니다. Flutter가 없어도 독립적으로 동작합니다.

왜 이렇게 분리했을까요? 마치 자동차 엔진과 차체를 따로 만드는 것과 같습니다.

엔진만 있으면 다른 차체에도 장착할 수 있듯이, riverpod 코어는 Flutter뿐 아니라 순수 Dart 서버나 CLI 도구에서도 사용할 수 있습니다. 그 위에 flutter_riverpod 패키지가 올라갑니다.

이 패키지는 riverpod 코어를 Flutter 위젯 시스템에 연결해주는 다리 역할을 합니다. ProviderScope, ConsumerWidget, Consumer 같은 친숙한 위젯들이 여기에 정의되어 있습니다.

개발 생산성을 위한 riverpod_annotationriverpod_generator도 있습니다. 이 패키지들은 @riverpod 어노테이션을 사용해 보일러플레이트 코드를 자동 생성해줍니다.

마치 요리사가 직접 재료를 손질하는 대신 전처리된 재료를 받아 쓰는 것과 같습니다. 실제 패키지 내부 구조를 살펴보면 더 흥미롭습니다.

riverpod 코어의 src 폴더에는 framework.dart라는 핵심 파일이 있습니다. 이 파일 하나에 ProviderContainer, ProviderElement, Ref 등 핵심 클래스가 모두 정의되어 있습니다.

flutter_riverpod은 src 폴더 아래 consumer.dart, framework.dart, provider_scope.dart로 나뉩니다. 각각 소비자 위젯, 프레임워크 연동, 스코프 관리를 담당합니다.

패키지 간 의존성도 명확합니다. flutter_riverpod은 riverpod에 의존하고, riverpod_generator는 riverpod_annotation에 의존합니다.

이런 단방향 의존성 덕분에 각 패키지를 독립적으로 테스트하고 배포할 수 있습니다. 김개발 씨는 고개를 끄덕였습니다.

"그래서 문서에서 순수 Dart 프로젝트는 riverpod만 쓰라고 했던 거구나." 구조를 이해하니 어떤 상황에 어떤 패키지를 선택해야 하는지 명확해졌습니다.

실전 팁

💡 - Flutter 앱이라면 flutter_riverpod만 추가하면 됩니다. riverpod 코어는 자동으로 포함됩니다.

  • 코드 생성을 원한다면 riverpod_annotation과 riverpod_generator를 함께 추가하세요.

2. ProviderElement의 역할

박시니어 씨가 김개발 씨에게 질문했습니다. "Provider를 만들면 실제로 상태는 어디에 저장될까요?" 김개발 씨는 Provider 객체 자체에 저장된다고 생각했지만, 박시니어 씨는 고개를 저었습니다.

"Provider는 그냥 레시피일 뿐이에요."

ProviderElement는 Provider의 실제 상태를 보관하고 생명주기를 관리하는 핵심 객체입니다. 마치 요리 레시피(Provider)와 실제 요리(Element)의 관계와 같습니다.

Provider는 상태를 어떻게 만들지 정의하고, Element는 그 상태를 실제로 생성하고 보관합니다.

다음 코드를 살펴봅시다.

// ProviderElement의 핵심 구조 이해하기
abstract class ProviderElementBase<State> {
  // 실제 상태값이 저장되는 곳
  State? _state;

  // 이 Element가 속한 컨테이너
  final ProviderContainer _container;

  // 이 Element를 구독하는 리스너들
  final _listeners = <ProviderSubscription>[];

  // 상태 생성 메서드 - 하위 클래스에서 구현
  State build();

  // 상태 변경 시 호출
  void setState(State newState) {
    _state = newState;
    // 모든 리스너에게 변경 알림
    for (final listener in _listeners) {
      listener._notify();
    }
  }
}

김개발 씨는 처음에 혼란스러웠습니다. 분명 final counterProvider = StateProvider((ref) => 0)라고 선언했는데, 이게 레시피라니요?

박시니어 씨가 화이트보드에 그림을 그리며 설명했습니다. "Provider 객체는 컴파일 타임에 만들어지는 상수예요.

앱이 시작할 때 이미 메모리에 올라가 있죠. 하지만 상태값 0은 런타임에 필요할 때 만들어져야 해요." 여기서 ProviderElement가 등장합니다.

ref.watch(counterProvider)를 처음 호출하면, Riverpod은 counterProvider에 해당하는 Element를 생성합니다. 이 Element가 실제로 (ref) => 0을 실행해서 초기 상태 0을 만들고 보관합니다.

마치 붕어빵 틀과 붕어빵의 관계와 같습니다. Provider는 붕어빵 틀이고, Element는 그 틀로 찍어낸 실제 붕어빵입니다.

틀 하나로 여러 붕어빵을 만들 수 있듯이, 테스트 환경에서는 같은 Provider로 여러 Element를 만들 수도 있습니다. Element 내부를 들여다보면 핵심 필드가 보입니다.

_state는 실제 상태값을 저장합니다. _listeners는 이 상태를 구독하는 위젯이나 다른 Provider들의 목록입니다.

_container는 이 Element가 속한 ProviderContainer에 대한 참조입니다. 상태가 변경되면 어떤 일이 일어날까요?

setState 메서드가 호출되면 먼저 _state 필드가 새 값으로 업데이트됩니다. 그다음 _listeners 목록을 순회하면서 각 리스너에게 변경을 알립니다.

이것이 Riverpod의 반응형 업데이트 핵심입니다. Element의 생명주기도 중요합니다.

Element는 처음 구독될 때 생성되고, build 메서드를 실행해 초기 상태를 만듭니다. 상태가 무효화되면 다시 build를 실행합니다.

더 이상 구독자가 없으면 autoDispose Provider의 경우 Element가 파괴됩니다. 실제 소스 코드를 보면 StateProviderElement, FutureProviderElement 등 다양한 하위 클래스가 있습니다.

각각 자신만의 build 로직과 상태 관리 방식을 구현합니다. 하지만 기본 원리는 모두 같습니다.

김개발 씨가 물었습니다. "그럼 ProviderContainer는 뭔가요?" 박시니어 씨가 답했습니다.

"Element들의 집합소예요. 모든 Element를 보관하고 관리하는 창고 같은 거죠." 이제 김개발 씨는 ref.watch를 쓸 때마다 Element가 열심히 일하고 있다는 걸 알게 되었습니다.

실전 팁

💡 - Provider는 상수처럼 전역에 선언해도 됩니다. 실제 상태는 Element에 저장되니까요.

  • 디버깅할 때 ProviderContainer.debugState를 출력하면 모든 Element 상태를 확인할 수 있습니다.

3. 상태 구독 메커니즘

김개발 씨는 ref.watch와 ref.read의 차이를 알고 있었습니다. 하지만 "watch가 어떻게 위젯을 자동으로 리빌드시키는 거지?"라는 의문이 생겼습니다.

마법처럼 느껴지는 이 동작의 비밀을 파헤쳐 봅시다.

Riverpod의 구독 시스템은 옵저버 패턴을 기반으로 합니다. ref.watch를 호출하면 현재 위젯이나 Provider가 대상 Provider의 구독자로 등록됩니다.

상태가 변경되면 등록된 모든 구독자에게 알림이 전달되어 리빌드가 트리거됩니다.

다음 코드를 살펴봅시다.

// 구독 메커니즘의 핵심 구조
class ProviderSubscription<T> {
  final ProviderElementBase _source;  // 구독 대상
  final void Function(T? prev, T next) _listener;  // 변경 시 콜백

  // 구독 해제
  void close() {
    _source._listeners.remove(this);
  }
}

// ConsumerWidget에서 watch 호출 시
class _ConsumerState extends State<Consumer> {
  final _subscriptions = <ProviderSubscription>[];

  T watch<T>(ProviderBase<T> provider) {
    final element = _container.readProviderElement(provider);
    // 구독 등록
    final subscription = element.addListener((prev, next) {
      setState(() {});  // 위젯 리빌드 트리거
    });
    _subscriptions.add(subscription);
    return element.state;
  }
}

박시니어 씨가 칠판에 그림을 그렸습니다. 가운데 원 하나와 그 주위를 둘러싼 여러 개의 작은 원들.

"가운데가 Provider이고, 주변이 구독자들이에요." 구독의 시작점은 ref.watch 호출입니다. ConsumerWidget 안에서 ref.watch(counterProvider)를 호출하면 내부적으로 여러 단계가 진행됩니다.

먼저 ProviderContainer에서 counterProvider에 해당하는 Element를 찾습니다. 아직 Element가 없다면 새로 생성합니다.

그다음 이 Element에 ProviderSubscription을 등록합니다. Subscription 객체에는 두 가지 핵심 정보가 담깁니다.

하나는 구독 대상인 Element에 대한 참조이고, 다른 하나는 상태 변경 시 실행할 콜백 함수입니다. ConsumerWidget의 경우 이 콜백이 setState를 호출해서 위젯을 리빌드시킵니다.

마치 유튜브 구독과 비슷합니다. 채널(Provider)을 구독하면 새 영상(상태 변경)이 올라올 때 알림(콜백)을 받습니다.

구독을 취소하면 더 이상 알림이 오지 않습니다. 상태가 변경되면 Element의 setState가 호출됩니다.

이 메서드는 _listeners 목록을 순회하면서 각 Subscription의 콜백을 실행합니다. 콜백이 실행되면 ConsumerWidget은 Flutter의 setState를 통해 리빌드를 예약합니다.

중요한 점은 구독이 자동으로 관리된다는 것입니다. ConsumerWidget이 dispose될 때 모든 Subscription이 자동으로 해제됩니다.

메모리 누수 걱정 없이 안전하게 사용할 수 있습니다. ref.read는 다릅니다.

현재 상태값만 가져오고 구독을 등록하지 않습니다. 일회성 조회가 필요할 때 사용합니다.

버튼 클릭 핸들러에서 상태를 읽을 때가 대표적인 예입니다. Provider 간 구독도 같은 원리입니다.

Provider A가 ref.watch(providerB)를 호출하면 A의 Element가 B의 Element를 구독합니다. B가 변경되면 A도 재계산됩니다.

이것이 파생 상태의 핵심입니다. 김개발 씨가 물었습니다.

"구독자가 100개면 100번 콜백이 호출되나요?" 박시니어 씨가 고개를 끄덕였습니다. "네, 하지만 Flutter가 프레임 단위로 리빌드를 배칭하니까 걱정 마세요."

실전 팁

💡 - 빌드 메서드 안에서만 ref.watch를 사용하세요. 이벤트 핸들러에서는 ref.read를 씁니다.

  • 불필요한 리빌드를 줄이려면 select를 사용해 특정 필드만 구독하세요.

4. 리빌드 최적화 원리

김개발 씨의 앱이 느려졌습니다. 프로파일러를 열어보니 한 Provider가 변경될 때마다 수십 개의 위젯이 리빌드되고 있었습니다.

"분명 Riverpod이 최적화를 해준다고 했는데..." 무엇이 문제였을까요?

Riverpod은 선택적 구독동등성 비교를 통해 불필요한 리빌드를 방지합니다. select를 사용하면 객체의 특정 필드만 구독할 수 있고, 이전 값과 새 값이 같으면 리빌드를 건너뜁니다.

이 메커니즘을 이해하면 성능을 크게 개선할 수 있습니다.

다음 코드를 살펴봅시다.

// 리빌드 최적화 기법들
class UserState {
  final String name;
  final int age;
  final String email;
  UserState(this.name, this.age, this.email);
}

final userProvider = StateProvider((ref) => UserState('Kim', 25, 'kim@test.com'));

// 나쁜 예: 전체 객체 구독 - email만 바뀌어도 리빌드
Widget build(context, ref) {
  final user = ref.watch(userProvider);  // 모든 필드 변경에 반응
  return Text(user.name);
}

// 좋은 예: select로 필요한 필드만 구독
Widget build(context, ref) {
  final name = ref.watch(userProvider.select((u) => u.name));
  return Text(name);  // name이 바뀔 때만 리빌드
}

박시니어 씨가 김개발 씨의 코드를 보더니 문제점을 짚었습니다. "UserState 객체 전체를 구독하고 있네요.

email이 바뀌어도 name만 표시하는 위젯이 리빌드돼요." Riverpod의 기본 동작은 단순합니다. 상태가 변경되면 구독자에게 알립니다.

하지만 "변경"의 판단 기준이 중요합니다. 기본적으로 **동등성 연산자(==)**를 사용해 이전 값과 새 값을 비교합니다.

문제는 복잡한 객체에서 발생합니다. UserState의 email만 바뀌어도 객체 자체는 새로 생성됩니다.

== 연산자를 오버라이드하지 않았다면 항상 다른 객체로 판단됩니다. 결과적으로 모든 구독자가 리빌드됩니다.

첫 번째 해결책은 select입니다. ref.watch(userProvider.select((u) => u.name))은 UserState가 아닌 name 문자열만 구독합니다.

email이 바뀌어도 name이 그대로면 리빌드하지 않습니다. select의 내부 동작을 살펴봅시다.

select는 원본 Provider를 감싸는 새로운 구독을 만듭니다. 원본 상태가 변경되면 selector 함수를 실행해 관심 있는 값을 추출합니다.

이전에 추출한 값과 비교해서 다를 때만 실제 리빌드를 트리거합니다. 두 번째 해결책은 Equatable 또는 freezed 사용입니다.

이 패키지들은 == 연산자를 자동으로 구현해줍니다. 모든 필드가 같으면 같은 객체로 판단합니다.

세 번째 해결책은 Provider 분리입니다. 하나의 큰 Provider 대신 여러 개의 작은 Provider로 나눕니다.

각 Provider는 독립적으로 변경되므로 영향 범위가 줄어듭니다. 실제로 Riverpod 내부에서는 shouldNotify 메서드가 이 판단을 담당합니다.

기본 구현은 이전 값과 새 값의 동등성을 비교합니다. 이 메서드를 커스터마이징할 수도 있습니다.

김개발 씨는 코드를 수정했습니다. 모든 ref.watch에 select를 추가하니 리빌드 횟수가 10분의 1로 줄었습니다.

프로파일러의 빨간 막대가 초록색으로 바뀌었습니다. 박시니어 씨가 덧붙였습니다.

"하지만 select를 과도하게 쓰면 코드가 복잡해져요. 성능 문제가 실제로 있을 때만 최적화하세요."

실전 팁

💡 - 리스트 아이템을 표시할 때는 각 아이템을 별도 Provider로 분리하는 것이 효과적입니다.

  • Dart DevTools의 Widget Rebuild Stats로 리빌드 횟수를 측정하세요.
  • freezed 패키지를 사용하면 불변 객체와 동등성 비교를 자동으로 얻을 수 있습니다.

5. autoDispose 구현 분석

김개발 씨는 메모리 누수 경고를 받았습니다. 화면을 여러 번 왔다 갔다 하니 메모리 사용량이 계속 증가했습니다.

원인을 찾아보니 더 이상 사용하지 않는 Provider 상태가 메모리에 남아 있었습니다. autoDispose의 필요성을 절실히 느낀 순간이었습니다.

autoDispose는 Provider가 더 이상 구독되지 않을 때 자동으로 상태를 정리하는 기능입니다. 마치 사용하지 않는 전등을 자동으로 꺼주는 센서등과 같습니다.

이 기능이 어떻게 구현되어 있는지 내부를 살펴봅시다.

다음 코드를 살펴봅시다.

// autoDispose 내부 구현 원리
abstract class AutoDisposeProviderElementBase<State>
    extends ProviderElementBase<State> {

  // 유지 상태 플래그
  bool _maintainState = false;

  // 구독 해제 시 호출
  @override
  void onRemoveListener() {
    super.onRemoveListener();

    // 구독자가 0이고 유지 플래그가 false면 dispose 예약
    if (_listeners.isEmpty && !_maintainState) {
      _scheduleDispose();
    }
  }

  void _scheduleDispose() {
    // 마이크로태스크로 dispose 예약 (동기 재구독 허용)
    Future.microtask(() {
      if (_listeners.isEmpty && !_maintainState) {
        dispose();
      }
    });
  }

  // ref.keepAlive()로 유지 상태 제어
  KeepAliveLink keepAlive() {
    _maintainState = true;
    return KeepAliveLink(() => _maintainState = false);
  }
}

박시니어 씨가 설명을 시작했습니다. "autoDispose가 없던 시절에는 개발자가 직접 상태를 정리해야 했어요.

화면이 사라져도 Provider 상태가 메모리에 남아 있었죠." autoDispose의 핵심 아이디어는 간단합니다. 구독자 수를 추적하다가 0이 되면 상태를 정리합니다.

마치 공유 오피스에서 마지막 사람이 나갈 때 불을 끄는 것과 같습니다. 내부 구현을 살펴봅시다.

AutoDisposeProviderElementBase는 일반 Element를 상속받아 자동 정리 기능을 추가합니다. onRemoveListener 메서드가 핵심입니다.

구독이 해제될 때마다 이 메서드가 호출됩니다. onRemoveListener는 두 가지를 확인합니다.

첫째, 남은 구독자가 있는지. 둘째, _maintainState 플래그가 true인지.

둘 다 아니라면 dispose를 예약합니다. 왜 "예약"일까요?

즉시 dispose하지 않는 이유가 있습니다. 화면 전환 중에 잠깐 구독이 해제되었다가 다시 구독되는 경우가 있습니다.

예를 들어 탭을 전환할 때 이전 탭의 위젯이 dispose되고 새 탭의 같은 Provider를 구독할 수 있습니다. Future.microtask를 사용해 dispose를 다음 마이크로태스크로 미룹니다.

동기적으로 재구독이 일어나면 dispose가 취소됩니다. 이렇게 불필요한 상태 재생성을 방지합니다.

keepAlive는 이 자동 정리를 제어합니다. ref.keepAlive()를 호출하면 _maintainState가 true가 됩니다.

구독자가 0이 되어도 dispose되지 않습니다. 반환되는 KeepAliveLink를 close하면 다시 자동 정리가 활성화됩니다.

언제 keepAlive를 쓸까요? HTTP 요청 결과를 캐시하고 싶을 때가 대표적입니다.

화면을 나갔다 돌아와도 다시 요청하지 않고 캐시된 데이터를 사용할 수 있습니다. 김개발 씨가 물었습니다.

"그럼 모든 Provider를 autoDispose로 만들어야 하나요?" 박시니어 씨가 답했습니다. "전역 설정이나 인증 상태처럼 앱 전체에서 유지해야 하는 건 일반 Provider를 쓰세요.

화면별 상태는 autoDispose가 적합해요." 메모리 누수 문제를 겪은 김개발 씨는 이제 Provider 생성 시 생명주기를 먼저 고민하게 되었습니다.

실전 팁

💡 - 화면별 상태는 autoDispose를 기본으로 사용하세요. 메모리 누수를 예방합니다.

  • HTTP 캐싱이 필요하면 ref.keepAlive()와 타이머를 조합해 시간 기반 캐시를 구현할 수 있습니다.
  • Riverpod 2.0부터는 @riverpod 어노테이션 사용 시 기본이 autoDispose입니다.

6. 코드 기여 가이드

김개발 씨는 Riverpod을 사용하다가 작은 버그를 발견했습니다. "이슈만 올리지 말고 직접 고쳐볼까?" 하는 생각이 들었습니다.

하지만 어디서부터 시작해야 할지 막막했습니다. 오픈소스 기여, 생각보다 어렵지 않습니다.

Riverpod은 활발한 오픈소스 프로젝트입니다. 버그 수정, 문서 개선, 새 기능 제안 등 다양한 방식으로 기여할 수 있습니다.

개발 환경 설정부터 PR 제출까지의 과정을 단계별로 살펴봅시다.

다음 코드를 살펴봅시다.

# Riverpod 기여를 위한 개발 환경 설정

# 1. 저장소 포크 및 클론
git clone https://github.com/YOUR_USERNAME/riverpod.git
cd riverpod

# 2. melos 설치 (모노레포 관리 도구)
dart pub global activate melos

# 3. 의존성 설치 (모든 패키지)
melos bootstrap

# 4. 테스트 실행
melos run test

# 5. 코드 분석
melos run analyze

# 6. 특정 패키지만 테스트
cd packages/riverpod
dart test

# 7. 변경 후 PR 전 체크리스트
melos run analyze && melos run test

박시니어 씨가 김개발 씨를 격려했습니다. "오픈소스 기여는 최고의 학습 방법이에요.

Riverpod처럼 잘 정리된 프로젝트는 특히 좋은 시작점이죠." 첫 단계는 저장소 포크입니다. GitHub에서 riverpod 저장소의 Fork 버튼을 클릭합니다.

이제 자신의 계정에 복사본이 생겼습니다. 이 복사본을 로컬에 클론합니다.

Riverpod은 melos라는 도구로 모노레포를 관리합니다. melos는 여러 패키지를 한 번에 관리하고 테스트할 수 있게 해줍니다.

dart pub global activate melos로 설치하고, melos bootstrap으로 모든 의존성을 설치합니다. 개발 환경이 준비되면 코드를 살펴봅니다.

packages 폴더 아래 각 패키지가 있습니다. 대부분의 핵심 로직은 packages/riverpod/lib/src에 있습니다.

framework.dart 파일이 진입점입니다. 버그를 수정하거나 기능을 추가했다면 테스트를 작성해야 합니다.

Riverpod은 높은 테스트 커버리지를 유지합니다. 기존 테스트를 참고해서 새 테스트를 추가하세요.

melos run test로 전체 테스트를 실행합니다. 코드 스타일도 중요합니다.

melos run analyze로 정적 분석을 실행합니다. 린트 에러가 있으면 수정해야 합니다.

Riverpod은 엄격한 린트 규칙을 사용합니다. PR을 올리기 전에 CHANGELOG를 업데이트해야 합니다.

각 패키지의 CHANGELOG.md 파일에 변경사항을 기록합니다. 버전 번호는 메인테이너가 결정하므로 Unreleased 섹션에 추가합니다.

이제 Pull Request를 생성합니다. 변경사항을 커밋하고 포크한 저장소에 푸시합니다.

GitHub에서 원본 저장소로 PR을 생성합니다. PR 템플릿을 따라 설명을 작성합니다.

PR이 제출되면 CI 파이프라인이 자동으로 실행됩니다. 테스트, 분석, 포맷팅 검사가 진행됩니다.

모두 통과해야 리뷰를 받을 수 있습니다. 메인테이너인 Remi Rousselet이 리뷰를 진행합니다.

피드백이 오면 수정하고 다시 푸시합니다. 승인되면 머지됩니다.

축하합니다, 이제 Riverpod 컨트리뷰터입니다! 김개발 씨는 작은 문서 오타 수정으로 첫 PR을 올렸습니다.

며칠 후 머지되었고, Contributors 목록에 이름이 올라갔습니다. 작은 시작이었지만 큰 성취감을 느꼈습니다.

실전 팁

💡 - 처음에는 문서 개선이나 오타 수정 같은 작은 기여로 시작하세요.

  • 이슈 트래커에서 good first issue 라벨이 붙은 이슈를 찾아보세요.
  • Discord 채널에서 다른 컨트리뷰터들과 소통하면 많은 도움을 받을 수 있습니다.

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#Flutter#Riverpod#StateManagement#Provider#Architecture#Flutter,State Management

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.

함께 보면 좋은 카드 뉴스