Recent Posts
Recent Comments
반응형
«   2025/08   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
Archives
Today
Total
관리 메뉴

오늘도 공부

Flutter에서 실무·학습에서 자주 쓰이는 디자인 패턴 본문

스터디/Flutter

Flutter에서 실무·학습에서 자주 쓰이는 디자인 패턴

행복한 수지아빠 2025. 8. 19. 12:09
반응형

Flutter에서 자주 사용하는 디자인 패턴 정리

Flutter 개발에서 자주 쓰이는 디자인 패턴을 UI 트리, 상태 관리, 아키텍처, 객체지향 패턴의 네 가지 축으로 정리했습니다.
각 패턴은 **왜 쓰는지(Why), 언제 쓰는지(When), 어떻게 쓰는지(How)**를 짧은 예제와 함께 담았습니다.


1. UI 트리 패턴

1-1. Composite 패턴

  • 개념: Flutter 위젯 트리 자체가 Composite. 전체와 부분을 동일하게 다룸.
  • 장점: UI를 재귀적으로 구성/관리하기 쉬움.
Column(
  children: [
    Text("안녕"),
    Row(children: [Icon(Icons.star), Text("별")]),
  ],
)

1-2. Decorator 패턴

  • 개념: 기능을 감싸면서 꾸밈을 추가.
  • 예시:
DecoratedBox(
  decoration: BoxDecoration(borderRadius: BorderRadius.circular(12)),
  child: Padding(
    padding: const EdgeInsets.all(12),
    child: Text('안녕'),
  ),
)

1-3. Builder 패턴

  • 개념: 런타임 시점에 콜백으로 위젯 생성.
  • 예시:
FutureBuilder<String>(
  future: Future.delayed(Duration(seconds: 2), () => "완료"),
  builder: (context, snapshot) {
    if (!snapshot.hasData) return CircularProgressIndicator();
    return Text(snapshot.data!);
  },
)

2. 상태 관리 패턴

비교표

패턴 난이도 규모 장점 단점

setState 낮음 소형 간단함 위젯 트리 전파 불편
Provider 낮음 소~중 표준적, 간단 대규모 시 복잡
Riverpod 중간 중~대 전역/지역 유연, 테스트 용이 개념 학습 필요
BLoC / Cubit 높음 중~대 단방향 데이터 흐름, 테스트 용이 이벤트/상태 설계 부담
Redux 중간 대형 예측 가능, 디버깅 용이 보일러플레이트 많음

2-1. Provider 예시

class Counter extends ChangeNotifier {
  int value = 0;
  void inc() { value++; notifyListeners(); }
}

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (_) => Counter(),
      child: MyApp(),
    ),
  );
}

class MyHome extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counter = context.watch<Counter>();
    return Column(
      children: [
        Text('${counter.value}'),
        ElevatedButton(
          onPressed: () => context.read<Counter>().inc(),
          child: Text('plus'),
        ),
      ],
    );
  }
}

2-2. Riverpod 예시

final counterProvider = StateNotifierProvider<CounterNotifier, int>(
  (ref) => CounterNotifier(),
);

class CounterNotifier extends StateNotifier<int> {
  CounterNotifier() : super(0);
  void inc() => state++;
}

class MyHome extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    return Column(
      children: [
        Text('$count'),
        ElevatedButton(
          onPressed: () => ref.read(counterProvider.notifier).inc(),
          child: Text('plus'),
        ),
      ],
    );
  }
}

2-3. Cubit 예시

class CounterCubit extends Cubit<int> {
  CounterCubit() : super(0);
  void inc() => emit(state + 1);
}

BlocProvider(
  create: (_) => CounterCubit(),
  child: BlocBuilder<CounterCubit, int>(
    builder: (_, count) => Column(
      children: [
        Text('$count'),
        ElevatedButton(
          onPressed: () => context.read<CounterCubit>().inc(),
          child: Text('plus'),
        ),
      ],
    ),
  ),
)

3. 아키텍처 패턴

MVVM 구조

lib/
  data/          // API, DB
  domain/        // Entity, UseCase
  presentation/  // View, ViewModel

Clean Architecture

  • 레이어: presentation → domain → data
  • 원칙: 안쪽 레이어는 바깥 의존 없음. 의존성 역전.

4. Repository 패턴

// domain
abstract class TodoRepo {
  Future<List<Todo>> fetchTodos();
}

// data
class TodoRepoImpl implements TodoRepo {
  final RemoteApi api;
  final LocalDb db;
  TodoRepoImpl(this.api, this.db);

  @override
  Future<List<Todo>> fetchTodos() async {
    final cached = await db.read();
    if (cached.isNotEmpty) return cached;
    final remote = await api.fetch();
    await db.save(remote);
    return remote;
  }
}

5. 객체지향 패턴 예시

  • Strategy: 알고리즘 교체
abstract class PriceStrategy { double calc(double base); }

class Discount implements PriceStrategy {
  @override double calc(double b) => b * 0.9;
}

class Premium implements PriceStrategy {
  @override double calc(double b) => b * 1.2;
}
  • Observer: ChangeNotifier, Stream, ValueNotifier
  • Factory: 플랫폼별 다른 위젯 반환

6. 네비게이션 패턴

go_router 예시

final router = GoRouter(
  routes: [
    GoRoute(
      path: '/',
      builder: (_, __) => HomePage(),
    ),
    GoRoute(
      path: '/detail/:id',
      builder: (_, s) => DetailPage(id: s.pathParameters['id']!),
    ),
  ],
);

7. 비동기 패턴

FutureBuilder<User>(
  future: api.getUser(),
  builder: (context, snapshot) {
    if (!snapshot.hasData) return CircularProgressIndicator();
    return Text(snapshot.data!.name);
  },
)

8. 성능 패턴 (Best Practice)

  • const 위젯 활용
  • 리빌드 최소화 (Selector, Consumer)
  • RepaintBoundary로 복잡 위젯 격리
  • ValueKey로 리스트 안정화

9. 상황별 추천 로드맵

  1. 프로토타입: setState
  2. 중형 앱: Provider / Riverpod
  3. 대형 앱: Clean Architecture + Riverpod/BLoC
  4. 웹/딥링크: go_router

✍️ 정리
Flutter에서의 디자인 패턴은 UI 트리(Composite/Decorator), 상태 관리(Riverpod/BLoC/Provider), 아키텍처(Clean/MVVM) 세 축이 핵심입니다.

 

반응형