Communitygithub.com

flutter-dart-code-review

라이브러리에 구애받지 않는 Flutter/Dart 코드 리뷰 체크리스트로, 위젯 모범 사례, 상태 관리 패턴(BLoC, Riverpod, Provider, GetX, MobX, Signals), Dart 관용구, 성능, 접근성, 보안 및 클린 아키텍처를 다룹니다. 프로젝트에서 사용하는 상태 관리 라이브러리에 관계없이 Flutter 또는 Dart 코드를 리뷰할 때 사용하세요.

flutter-dart-code-review란 무엇인가요?

이 스킬은 Flutter 및 Dart 애플리케이션을 위한 포괄적이고 라이브러리 중립적인 코드 리뷰 체크리스트를 제공합니다. BLoC, Riverpod, Provider, GetX, MobX, Signals 등 어떤 상태 관리 라이브러리를 사용하든 일관된 리뷰 기준을 적용할 수 있습니다. 체크리스트는 일반 프로젝트 건강 상태, Dart 언어 함정, 위젯 모범 사례, 상태 관리 아키텍처, 성능 최적화, 테스트, 접근성, 플랫폼 특화 문제, 보안 등 9개 주요 영역을 다룹니다. 각 항목은 구체적인 검토 포인트와 함께 좋은 예와 나쁜 예를 Dart 코드 스니펫으로 포함하여 개발자가 실수를 쉽게 식별하고 수정할 수 있도록 돕습니다. 이 스킬은 CI/CD 파이프라인, 코드 리뷰 도구(Cursor, Codex 등), 또는 개발자 간의 수동 리뷰에서 사용되어 Flutter/Dart 프로젝트의 코드 품질을 일관되게 유지하는 데 기여합니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/flutter-dart-code-review

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

Flutter/Dart 코드 리뷰 모범 사례

Flutter/Dart 애플리케이션을 검토하기 위한 포괄적이고 라이브러리에 구애받지 않는 체크리스트입니다. 이러한 원칙은 사용하는 상태 관리 솔루션, 라우팅 라이브러리 또는 DI 프레임워크에 관계없이 적용됩니다.


1. 일반 프로젝트 상태

  • 프로젝트가 일관된 폴더 구조(기능 우선 또는 계층 우선)를 따릅니다.
  • 적절한 관심사 분리: UI, 비즈니스 로직, 데이터 계층
  • 위젯에 비즈니스 로직이 없으며, 위젯은 순전히 프레젠테이션용입니다.
  • pubspec.yaml이 깔끔합니다. 사용하지 않는 종속성이 없고, 버전이 적절하게 고정되어 있습니다.
  • analysis_options.yaml에 엄격한 lint 세트와 엄격한 분석기 설정이 포함되어 있습니다.
  • 프로덕션 코드에 print() 문이 없습니다. dart:developer의 log() 또는 로깅 패키지를 사용합니다.
  • 생성된 파일(.g.dart, .freezed.dart, .gr.dart)이 최신 상태이거나 .gitignore에 포함되어 있습니다.
  • 플랫폼별 코드가 추상화 뒤에 격리되어 있습니다.

2. Dart 언어 함정

  • 암시적 동적 타입: 타입 어노테이션이 누락되어 dynamic이 발생합니다. strict-casts, strict-inference, strict-raw-types를 활성화하세요.
  • 널 안전성 오용: 적절한 널 검사 또는 Dart 3 패턴 매칭(if (value case var v?)) 대신 과도한 !(뱅 연산자) 사용.
  • 타입 승격 실패: 로컬 변수 승격이 작동할 때 this.field 사용.
  • 너무 광범위하게 예외 캐치: on 절 없이 catch (e)만 사용. 항상 예외 유형을 지정하세요.
  • Error 캐치: Error 하위 유형은 버그를 나타내며 캐치되어서는 안 됩니다.
  • 사용되지 않는 async: await 없이 async로 표시된 함수. 불필요한 오버헤드입니다.
  • late 과도 사용: nullable 또는 생성자 초기화가 더 안전한 곳에 late 사용. 오류를 런타임으로 지연시킵니다.
  • 루프 내 문자열 연결: 반복적인 문자열 빌드에는 + 대신 StringBuffer를 사용하세요.
  • const 컨텍스트에서의 가변 상태: const 생성자 클래스의 필드는 변경 가능해서는 안 됩니다.
  • Future 반환값 무시: await를 사용하거나 의도를 명확히 하기 위해 명시적으로 unawaited()를 호출하세요.
  • final이 작동하는 곳에 var 사용: 지역 변수에는 final을, 컴파일 타임 상수에는 const를 선호하세요.
  • 상대 가져오기: 일관성을 위해 package: 가져오기를 사용하세요.
  • 가변 컬렉션 노출: 공개 API는 원시 List/Map이 아닌 수정 불가능한 뷰를 반환해야 합니다.
  • Dart 3 패턴 매칭 누락: 장황한 is 검사 및 수동 캐스팅보다는 switch 표현식과 if-case를 선호하세요.
  • 여러 반환값을 위한 일회용 클래스: 단일 용도 DTO 대신 Dart 3 레코드 (String, int)를 사용하세요.
  • 프로덕션 코드에서 print() 사용: dart:developer의 log() 또는 프로젝트의 로깅 패키지를 사용하세요. print()에는 로그 레벨이 없어 필터링할 수 없습니다.

3. 위젯 모범 사례

위젯 분해:

  • build() 메서드가 약 80-100줄을 초과하는 단일 위젯이 없습니다.
  • 캡슐화와 변경 방식(재빌드 경계)에 따라 위젯이 분할됩니다.
  • 위젯을 반환하는 private _build*() 헬퍼 메서드는 별도의 위젯 클래스로 추출됩니다(요소 재사용, const 전파 및 프레임워크 최적화 활성화).
  • 가변 로컬 상태가 필요하지 않은 경우 Stateful 위젯보다 Stateless 위젯을 선호합니다.
  • 재사용 가능한 경우 추출된 위젯은 별도의 파일에 있습니다.

Const 사용:

  • 가능한 모든 곳에 const 생성자가 사용됩니다. 불필요한 재빌드를 방지합니다.
  • 변경되지 않는 컬렉션에 대한 const 리터럴 (const [], const {}).
  • 모든 필드가 final인 경우 생성자는 const로 선언됩니다.

Key 사용:

  • 목록/그리드에서 재정렬 시 상태를 유지하기 위해 ValueKey가 사용됩니다.
  • GlobalKey는 신중하게 사용됩니다. 트리 전체에서 상태에 접근하는 것이 정말로 필요할 때만 사용합니다.
  • build()에서 UniqueKey는 피합니다. 매 프레임 재빌드를 강제합니다.
  • 정체성이 단일 값이 아닌 데이터 객체에 기반할 때 ObjectKey가 사용됩니다.

테마 및 디자인 시스템:

  • 색상은 Theme.of(context).colorScheme에서 가져옵니다. 하드코딩된 Colors.red 또는 16진수 값이 없습니다.
  • 텍스트 스타일은 Theme.of(context).textTheme에서 가져옵니다. 원시 글꼴 크기가 포함된 인라인 TextStyle이 없습니다.
  • 다크 모드 호환성이 확인되었습니다. 밝은 배경을 가정하지 않습니다.
  • 간격 및 크기 조정은 일관된 디자인 토큰이나 상수를 사용하며, 매직 넘버를 사용하지 않습니다.

Build 메서드 복잡성:

  • build()에서 네트워크 호출, 파일 I/O 또는 무거운 계산이 없습니다.
  • build()에서 Future.then() 또는 async 작업이 없습니다.
  • build()에서 구독 생성(.listen())이 없습니다.
  • setState()는 가능한 가장 작은 하위 트리로 제한됩니다.

4. 상태 관리 (라이브러리 중립적)

이러한 원칙은 모든 Flutter 상태 관리 솔루션(BLoC, Riverpod, Provider, GetX, MobX, Signals, ValueNotifier 등)에 적용됩니다.

아키텍처:

  • 비즈니스 로직은 위젯 계층 외부, 즉 상태 관리 컴포넌트(BLoC, Notifier, Controller, Store, ViewModel 등)에 있습니다.
  • 상태 관리자는 내부에서 직접 생성하는 것이 아니라 주입을 통해 종속성을 받습니다.
  • 서비스 또는 리포지토리 계층이 데이터 소스를 추상화합니다. 위젯과 상태 관리자는 API나 데이터베이스를 직접 호출해서는 안 됩니다.
  • 상태 관리자는 단일 책임을 가집니다. 관련 없는 문제를 처리하는 "신" 관리자는 없습니다.
  • 컴포넌트 간 종속성은 솔루션의 규칙을 따릅니다.
    • Riverpod에서: ref.watch를 통해 다른 프로바이더에 의존하는 것은 예상되며, 순환 또는 지나치게 복잡한 체인만 플래그 지정합니다.
    • BLoC에서: bloc은 다른 bloc에 직접 의존해서는 안 됩니다. 공유 리포지토리 또는 프레젠테이션 계층 조정을 선호합니다.
    • 기타 솔루션: 컴포넌트 간 통신에 대한 문서화된 규칙을 따릅니다.

불변성 및 값 동등성 (불변 상태 솔루션: BLoC, Riverpod, Redux):

  • 상태 객체는 불변입니다. copyWith() 또는 생성자를 통해 새 인스턴스가 생성되며, 제자리에서 변경되지 않습니다.
  • 상태 클래스는 == 및 hashCode를 적절히 구현합니다(비교에 모든 필드 포함).
  • 메커니즘은 프로젝트 전반에 걸쳐 일관됩니다. 수동 재정의, Equatable, freezed, Dart 레코드 또는 기타.
  • 상태 객체 내의 컬렉션은 원시 가변 List/Map으로 노출되지 않습니다.

반응성 규율 (반응형 변이 솔루션: MobX, GetX, Signals):

  • 상태는 솔루션의 반응형 API(MobX의 @action, Signals의 .value , GetX의 .obs)를 통해서만 변경됩니다. 직접 필드 변이는 변경 추적을 우회합니다.
  • 파생된 값은 중복 저장되지 않고 솔루션의 계산된 메커니즘을 사용합니다.
  • 반응 및 디스포저는 적절히 정리됩니다(MobX의 ReactionDisposer, Signals의 효과 정리).

상태 모양 설계:

  • 상호 배타적인 상태는 부울 플래그(isLoading, isError, hasData)가 아닌 봉인된 유형, 유니온 변형 또는 솔루션의 내장 async 상태 유형(예: Riverpod의 AsyncValue)을 사용합니다.
  • 모든 비동기 작업은 로딩, 성공 및 오류를 별개의 상태로 모델링합니다.
  • 모든 상태 변형은 UI에서 철저히 처리됩니다. 조용히 무시되는 경우가 없습니다.
  • 오류 상태는 표시를 위해 오류 정보를 전달합니다. 로딩 상태는 오래된 데이터를 전달하지 않습니다.
  • Nullable 데이터는 로딩 표시기로 사용되지 않습니다. 상태는 명시적입니다.
// BAD — 부울 플래그 혼합으로 불가능한 상태 허용
class UserState {
  bool isLoading = false;
  bool hasError = false; // isLoading && hasError가 표현 가능!
  User? user;
}

// GOOD (불변 접근 방식) — 봉인된 유형이 불가능한 상태를 표현 불가능하게 만듦
sealed class UserState {}
class UserInitial extends UserState {}
class UserLoading extends UserState {}
class UserLoaded extends UserState {
  final User user;
  const UserLoaded(this.user);
}
class UserError extends UserState {
  final String message;
  const UserError(this.message);
}

// GOOD (반응형 접근 방식) — 관찰 가능한 enum + 데이터, 반응형 API를 통한 변이
// enum UserStatus { initial, loading, loaded, error }
// 솔루션의 observable/signal을 사용하여 상태와 데이터를 별도로 래핑

재빌드 최적화:

  • 상태 소비자 위젯(Builder, Consumer, Observer, Obx, Watch 등)은 가능한 한 좁은 범위로 지정됩니다.
  • 특정 필드가 변경될 때만 재빌드하도록 선택기가 사용됩니다. 모든 상태 방출 시 재빌드하지 않습니다.
  • const 위젯을 사용하여 트리를 통한 재빌드 전파를 중지합니다.
  • 계산/파생된 상태는 반응적으로 계산되며, 중복 저장되지 않습니다.

구독 및 폐기:

  • 모든 수동 구독(.listen())은 dispose() / close()에서 취소됩니다.
  • 더 이상 필요하지 않으면 스트림 컨트롤러가 닫힙니다.
  • 타이머는 폐기 수명 주기에서 취소됩니다.
  • 수동 구독보다 프레임워크 관리 수명 주기가 선호됩니다(선언적 빌더 vs .listen()).
  • 비동기 콜백에서 setState 전에 mounted 확인.
  • context.mounted 확인 없이 await 후에 BuildContext를 사용하지 않습니다(Flutter 3.7+). 오래된 컨텍스트는 충돌을 유발합니다.
  • 위젯이 여전히 마운트되었는지 확인하지 않고 비동기 갭 후에 탐색, 대화상자 또는 스캐폴드 메시지가 없습니다.
  • BuildContext는 싱글톤, 상태 관리자 또는 정적 필드에 저장되지 않습니다.

로컬 vs 전역 상태:

  • 임시 UI 상태(체크박스, 슬라이더, 애니메이션)는 로컬 상태(setState, ValueNotifier)를 사용합니다.
  • 공유 상태는 필요한 만큼만 높이 올립니다. 과도하게 전역화하지 않습니다.
  • 기능 범위 상태는 기능이 더 이상 활성화되지 않을 때 적절히 폐기됩니다.

5. 성능

불필요한 재빌드:

  • setState()가 루트 위젯 수준에서 호출되지 않습니다. 상태 변경을 지역화합니다.
  • const 위젯을 사용하여 재빌드 전파를 중지합니다.
  • 독립적으로 다시 칠하는 복잡한 하위 트리 주위에 RepaintBoundary가 사용됩니다.
  • 애니메이션과 독립적인 하위 트리에 대해 AnimatedBuilder 자식 매개변수가 사용됩니다.

build()에서의 비용이 많이 드는 작업:

  • build()에서 대규모 컬렉션 정렬, 필터링 또는 매핑이 없습니다. 상태 관리 계층에서 계산합니다.
  • build()에서 정규식 컴파일이 없습니다.
  • MediaQuery.of(context) 사용은 구체적입니다(예: MediaQuery.sizeOf(context)).

이미지 최적화:

  • 네트워크 이미지는 캐싱을 사용합니다(프로젝트에 적합한 모든 캐싱 솔루션).
  • 대상 기기에 적절한 이미지 해상도(썸네일에 4K 이미지를 로드하지 않음).
  • Image.asset은 cacheWidth/cacheHeight를 사용하여 표시 크기로 디코딩합니다.
  • 네트워크 이미지에 대해 플레이스홀더 및 오류 위젯이 제공됩니다.

지연 로딩:

  • 크거나 동적인 목록의 경우 ListView(children: [...]) 대신 ListView.builder / GridView.builder를 사용합니다(작고 정적인 목록에는 구체적인 생성자가 적합합니다).
  • 대규모 데이터 세트에 대해 페이지네이션이 구현되었습니다.
  • 웹 빌드에서 무거운 라이브러리에 대해 지연 로딩(deferred as)이 사용됩니다.

기타:

  • 애니메이션에서 Opacity 위젯을 피합니다. AnimatedOpacity 또는 FadeTransition을 사용합니다.
  • 애니메이션에서 클리핑을 피합니다. 이미지를 미리 클립합니다.
  • 위젯에서 operator ==를 재정의하지 않습니다. 대신 const 생성자를 사용합니다.
  • 내부 치수 위젯(IntrinsicHeight, IntrinsicWidth)은 드물게 사용합니다(추가 레이아웃 패스).

6. 테스트

테스트 유형 및 기대:

  • 단위 테스트: 모든 비즈니스 로직(상태 관리자, 리포지토리, 유틸리티 함수)을 다룹니다.
  • 위젯 테스트: 개별 위젯 동작, 상호 작용 및 시각적 출력을 다룹니다.
  • 통합 테스트: 중요한 사용자 흐름을 종단 간 다룹니다.
  • 골든 테스트: 디자인에 중요한 UI 구성 요소에 대한 픽셀 완벽한 비교.

적용 범위 목표:

  • 비즈니스 로직에서 80% 이상의 라인 적용 범위를 목표로 합니다.
  • 모든 상태 전환에 해당 테스트가 있습니다(로딩 → 성공, 로딩 → 오류, 재시도 등).
  • 엣지 케이스 테스트: 빈 상태, 오류 상태, 로딩 상태, 경계 값.

테스트 격리:

  • 외부 종속성(API 클라이언트, 데이터베이스, 서비스)은 모의 또는 가짜로 대체됩니다.
  • 각 테스트 파일은 정확히 하나의 클래스/단위를 테스트합니다.
  • 테스트는 구현 세부 사항이 아닌 동작을 검증합니다.
  • 스텁은 각 테스트에 필요한 동작만 정의합니다(최소 스텁).
  • 테스트 케이스 간에 공유 가변 상태가 없습니다.

위젯 테스트 품질:

  • pumpWidget 및 pump은 비동기 작업에 올바르게 사용됩니다.
  • find.byType, find.text, find.byKey가 적절히 사용됩니다.
  • 타이밍에 의존하는 불안정한 테스트가 없습니다. pumpAndSettle 또는 명시적 pump(Duration)을 사용합니다.
  • 테스트는 CI에서 실행되고 실패 시 병합이 차단됩니다.

7. 접근성

시맨틱 위젯:

  • 자동 레이블이 충분하지 않은 경우 Semantics 위젯을 사용하여 스크린 리더 레이블을 제공합니다.
  • 순전히 장식적인 요소에는 ExcludeSemantics를 사용합니다.
  • 관련 위젯을 단일 접근 가능한 요소로 결합하려면 MergeSemantics를 사용합니다.
  • 이미지에는 semanticLabel 속성이 설정되어 있습니다.

스크린 리더 지원:

  • 모든 대화형 요소는 포커스 가능하고 의미 있는 설명이 있습니다.
  • 포커스 순서는 논리적입니다(시각적 읽기 순서를 따름).

시각적 접근성:

  • 배경에 대한 텍스트의 대비 비율 >= 4.5:1.
  • 탭 가능한 대상은 최소 48x48 픽셀입니다.
  • 색상이 상태의 유일한 표시자가 아닙니다(아이콘/텍스트를 함께 사용).
  • 텍스트는 시스템 글꼴 크기 설정에 따라 크기가 조정됩니다.

상호 작용 접근성:

  • no-op onPressed 콜백이 없습니다. 모든 버튼은 작업을 수행하거나 비활성화됩니다.
  • 오류 필드는 수정 사항을 제안합니다.
  • 사용자가 데이터를 입력하는 동안 컨텍스트가 예기치 않게 변경되지 않습니다.

8. 플랫폼별 고려 사항

iOS/Android 차이점:

  • 적절한 경우 플랫폼 적응형 위젯이 사용됩니다.
  • 뒤로 탐색이 올바르게 처리됩니다(Android 뒤로 버튼, iOS 스와이프하여 뒤로 가기).
  • 상태 표시줄 및 안전 영역은 SafeArea 위젯을 통해 처리됩니다.
  • 플랫폼별 권한이 AndroidManifest.xml 및 Info.plist에 선언되어 있습니다.

반응형 디자인:

  • 반응형 레이아웃을 위해 LayoutBuilder 또는 MediaQuery가 사용됩니다.
  • 중단점이 일관되게 정의됩니다(휴대폰, 태블릿, 데스크탑).
  • 작은 화면에서 텍스트가 오버플로되지 않습니다. Flexible, Expanded, FittedBox를 사용합니다.
  • 가로 방향이 테스트되었거나 명시적으로 잠겨 있습니다.
  • 웹 전용: 마우스/키보드 상호 작용 지원, 호버 상태 존재.

9. 보안

안전한 저장소:

  • 민감한 데이터...

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

관련 스킬