<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Flutter Explorer</title>
    <link>https://flutter-explorer.tistory.com/</link>
    <description>flutter-explorer 님의 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Thu, 18 Jun 2026 12:32:18 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>플러터를 배워보자</managingEditor>
    <image>
      <title>Flutter Explorer</title>
      <url>https://tistory1.daumcdn.net/tistory/7369118/attach/92e0895e789b4b6cb711ab2d7e9ad3eb</url>
      <link>https://flutter-explorer.tistory.com</link>
    </image>
    <item>
      <title>Flutter에서 탭, 드래그, 텍스트 입력 테스트하기: 위젯 상호작용 완벽 가이드</title>
      <link>https://flutter-explorer.tistory.com/entry/tap-drag</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter 애플리케이션 개발 시, 사용자와의 상호작용은 앱의 핵심 요소 중 하나입니다. 따라서 탭, 드래그, 텍스트 입력과 같은 제스처가 올바르게 동작하는지 테스트하는 것은 매우 중요합니다. 이번 블로그에서는 이러한 상호작용을 테스트하는 방법과 주요 기능에 대해 자세히 알아보겠습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고. &lt;a href=&quot;https://docs.flutter.dev/cookbook/testing/widget/tap-drag&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Tap,&amp;nbsp;drag,&amp;nbsp;and&amp;nbsp;enter&amp;nbsp;text&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;위젯 테스트의 중요성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위젯 테스트는 Flutter의 고유한 테스트로, 격리된 방식으로 각 위젯을 테스트할 수 있습니다. 이를 통해 앱의 UI와 상호작용을 검증하며, 사용자 경험을 향상시킬 수 있습니다. 위젯 테스트는 앱을 빌드하고 실행하는 과정 없이 바로 테스트가 가능하며, UI 요소의 동작을 검증할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Flutter에서의 상호작용 테스트 설정하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter에서는 &lt;code&gt;flutter_test&lt;/code&gt; 패키지를 사용하여 위젯 테스트를 작성할 수 있습니다. 이를 위해 &lt;code&gt;pubspec.yaml&lt;/code&gt; 파일의 &lt;code&gt;dev_dependencies&lt;/code&gt; 섹션에 &lt;code&gt;flutter_test&lt;/code&gt; 종속성을 포함해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;dev_dependencies:
  flutter_test:
    sdk: flutter&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주요 상호작용 테스트 메서드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;WidgetTester&lt;/code&gt; 클래스는 다양한 상호작용을 시뮬레이션할 수 있는 메서드를 제공합니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;tap&lt;/code&gt;&lt;/b&gt;: 특정 위젯에 대한 탭 동작을 시뮬레이션합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;drag&lt;/code&gt;&lt;/b&gt;: 특정 위치에서 드래그 동작을 시뮬레이션합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;enterText&lt;/code&gt;&lt;/b&gt;: 텍스트 필드에 텍스트 입력을 시뮬레이션합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;상호작용 테스트 예제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 버튼을 탭하고, 드래그 동작을 수행하며, 텍스트를 입력하는 테스트 예제입니다:&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets('탭, 드래그, 텍스트 입력 테스트', (WidgetTester tester) async {
    // 테스트용 앱 위젯 생성
    await tester.pumpWidget(MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('테스트 앱')),
        body: Column(
          children: [
            ElevatedButton(
              onPressed: () {},
              child: Text('탭 버튼'),
            ),
            GestureDetector(
              onPanUpdate: (details) {},
              child: Container(
                key: Key('드래그 박스'),
                width: 100,
                height: 100,
                color: Colors.blue,
              ),
            ),
            TextField(
              key: Key('텍스트 필드'),
            ),
          ],
        ),
      ),
    ));

    // '탭 버튼'을 찾고 탭 동작 시뮬레이션
    final tapButtonFinder = find.text('탭 버튼');
    await tester.tap(tapButtonFinder);
    await tester.pump();

    // '드래그 박스'를 찾고 드래그 동작 시뮬레이션
    final dragBoxFinder = find.byKey(Key('드래그 박스'));
    await tester.drag(dragBoxFinder, Offset(0, 100));
    await tester.pump();

    // '텍스트 필드'를 찾고 텍스트 입력 시뮬레이션
    final textFieldFinder = find.byKey(Key('텍스트 필드'));
    await tester.enterText(textFieldFinder, '테스트 입력');
    await tester.pump();

    // 입력된 텍스트가 올바른지 확인
    expect(find.text('테스트 입력'), findsOneWidget);
  });
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서는 &lt;code&gt;tap&lt;/code&gt;, &lt;code&gt;drag&lt;/code&gt;, &lt;code&gt;enterText&lt;/code&gt; 메서드를 사용하여 각각의 상호작용을 시뮬레이션하고, 해당 동작이 올바르게 수행되었는지 확인합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;상호작용 테스트 시 주의사항&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;적절한 키 사용&lt;/b&gt;: 각 위젯에 고유한 키를 부여하여 정확한 식별이 가능하도록 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;애니메이션 처리&lt;/b&gt;: 상호작용 후 &lt;code&gt;pumpAndSettle()&lt;/code&gt;을 호출하여 모든 애니메이션이 완료될 때까지 기다립니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;테스트 환경 설정&lt;/b&gt;: 테스트 환경에서의 제약 사항을 고려하여 테스트를 설계합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter에서 탭, 드래그, 텍스트 입력과 같은 사용자 상호작용을 테스트하는 것은 앱의 신뢰성과 사용자 경험을 향상시키는 데 필수적입니다. &lt;code&gt;flutter_test&lt;/code&gt; 패키지와 &lt;code&gt;WidgetTester&lt;/code&gt;의 다양한 메서드를 활용하여 이러한 상호작용을 철저히 검증하고, 앱의 품질을 높이세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734908751338&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets('Add and remove a todo', (tester) async {
    // Build the widget.
    await tester.pumpWidget(const TodoList());

    // Enter 'hi' into the TextField.
    await tester.enterText(find.byType(TextField), 'hi');

    // Tap the add button.
    await tester.tap(find.byType(FloatingActionButton));

    // Rebuild the widget with the new item.
    await tester.pump();

    // Expect to find the item on screen.
    expect(find.text('hi'), findsOneWidget);

    // Swipe the item to dismiss it.
    await tester.drag(find.byType(Dismissible), const Offset(500, 0));

    // Build the widget until the dismiss animation ends.
    await tester.pumpAndSettle();

    // Ensure that the item is no longer on screen.
    expect(find.text('hi'), findsNothing);
  });
}

class TodoList extends StatefulWidget {
  const TodoList({super.key});

  @override
  State&amp;lt;TodoList&amp;gt; createState() =&amp;gt; _TodoListState();
}

class _TodoListState extends State&amp;lt;TodoList&amp;gt; {
  static const _appTitle = 'Todo List';
  final todos = &amp;lt;String&amp;gt;[];
  final controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: _appTitle,
      home: Scaffold(
        appBar: AppBar(
          title: const Text(_appTitle),
        ),
        body: Column(
          children: [
            TextField(
              controller: controller,
            ),
            Expanded(
              child: ListView.builder(
                itemCount: todos.length,
                itemBuilder: (context, index) {
                  final todo = todos[index];

                  return Dismissible(
                    key: Key('$todo$index'),
                    onDismissed: (direction) =&amp;gt; todos.removeAt(index),
                    background: Container(color: Colors.red),
                    child: ListTile(title: Text(todo)),
                  );
                },
              ),
            ),
          ],
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            setState(() {
              todos.add(controller.text);
              controller.clear();
            });
          },
          child: const Icon(Icons.add),
        ),
      ),
    );
  }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Flutter Cookbook</category>
      <category>dart</category>
      <category>flutter</category>
      <category>개발자팁</category>
      <category>모바일개발</category>
      <category>사용자상호작용</category>
      <category>소프트웨어테스트</category>
      <category>앱개발</category>
      <category>위젯테스트</category>
      <category>테스트자동화</category>
      <category>프로그래밍</category>
      <author>플러터를 배워보자</author>
      <guid isPermaLink="true">https://flutter-explorer.tistory.com/75</guid>
      <comments>https://flutter-explorer.tistory.com/entry/tap-drag#entry75comment</comments>
      <pubDate>Tue, 24 Dec 2024 11:00:58 +0900</pubDate>
    </item>
    <item>
      <title>Flutter에서 스크롤 테스트하기: 위젯 테스트의 핵심 가이드</title>
      <link>https://flutter-explorer.tistory.com/entry/scrolling</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter 애플리케이션 개발 시, 스크롤 기능은 사용자 경험에 큰 영향을 미칩니다. 따라서 스크롤 동작이 의도한 대로 작동하는지 확인하는 것은 매우 중요합니다. 이번 블로그에서는 Flutter에서 스크롤을 테스트하는 방법과 주요 기능에 대해 자세히 알아보겠습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고. &lt;a href=&quot;https://docs.flutter.dev/cookbook/testing/widget/scrolling&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Handle&amp;nbsp;scrolling&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;스크롤 테스트의 중요성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 리스트나 페이지를 스크롤하여 콘텐츠를 탐색합니다. 스크롤이 원활하지 않거나 예상치 못한 동작을 한다면, 사용자 만족도가 크게 저하될 수 있습니다. 따라서 스크롤 기능을 철저히 테스트하여 앱의 품질을 높이는 것이 중요합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Flutter에서 스크롤 테스트 설정하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter에서는 &lt;code&gt;flutter_test&lt;/code&gt; 패키지를 활용하여 스크롤 동작을 테스트할 수 있습니다. 이를 위해 &lt;code&gt;pubspec.yaml&lt;/code&gt; 파일에 해당 패키지를 추가해야 합니다:&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;dev_dependencies:
  flutter_test:
    sdk: flutter&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;스크롤 테스트를 위한 주요 메서드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter의 &lt;code&gt;WidgetTester&lt;/code&gt; 클래스는 스크롤 테스트를 지원하는 다양한 메서드를 제공합니다. 그 중 주요 메서드는 다음과 같습니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;drag&lt;/code&gt;&lt;/b&gt;: 특정 위치에서 드래그 동작을 시뮬레이션합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;fling&lt;/code&gt;&lt;/b&gt;: 빠른 스와이프 동작을 시뮬레이션합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;scrollUntilVisible&lt;/code&gt;&lt;/b&gt;: 특정 위젯이 보일 때까지 스크롤합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;스크롤 테스트 예제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 &lt;code&gt;ListView&lt;/code&gt; 위젯의 스크롤 동작을 테스트하는 예제입니다:&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets('ListView 스크롤 테스트', (WidgetTester tester) async {
    // 테스트용 앱 위젯 생성
    await tester.pumpWidget(MaterialApp(
      home: Scaffold(
        body: ListView.builder(
          itemCount: 100,
          itemBuilder: (context, index) {
            return ListTile(
              key: Key('item_$index'),
              title: Text('아이템 $index'),
            );
          },
        ),
      ),
    ));

    // 'item_50' 키를 가진 위젯이 존재하지 않음을 확인
    expect(find.byKey(Key('item_50')), findsNothing);

    // 스크롤하여 'item_50'을 화면에 표시
    await tester.scrollUntilVisible(
      find.byKey(Key('item_50')),
      500.0,
    );
    await tester.pumpAndSettle();

    // 'item_50' 키를 가진 위젯이 화면에 표시되었음을 확인
    expect(find.byKey(Key('item_50')), findsOneWidget);
  });
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서는 &lt;code&gt;ListView&lt;/code&gt;를 생성하고, &lt;code&gt;scrollUntilVisible&lt;/code&gt; 메서드를 사용하여 특정 아이템이 화면에 나타날 때까지 스크롤합니다. 그런 다음 해당 아이템이 실제로 화면에 표시되었는지 확인합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;스크롤 테스트 시 주의사항&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;적절한 키 사용&lt;/b&gt;: 각 아이템에 고유한 키를 부여하여 정확한 식별이 가능하도록 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;애니메이션 처리&lt;/b&gt;: 스크롤 후 &lt;code&gt;pumpAndSettle()&lt;/code&gt;을 호출하여 모든 애니메이션이 완료될 때까지 기다립니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;화면 크기 고려&lt;/b&gt;: 다양한 디바이스 크기에서 테스트하여 스크롤 동작이 일관되게 작동하는지 확인합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter에서 스크롤 기능을 테스트하는 것은 사용자 경험을 향상시키는 데 필수적입니다. &lt;code&gt;flutter_test&lt;/code&gt; 패키지와 &lt;code&gt;WidgetTester&lt;/code&gt;의 다양한 메서드를 활용하여 스크롤 동작을 철저히 검증하고, 앱의 품질을 높이세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734908600958&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility that Flutter provides. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

import 'package:scrolling/main.dart';

void main() {
  testWidgets('finds a deep item in a long list', (tester) async {
    // Build our app and trigger a frame.
    await tester.pumpWidget(MyApp(
      items: List&amp;lt;String&amp;gt;.generate(10000, (i) =&amp;gt; 'Item $i'),
    ));

    final listFinder = find.byType(Scrollable);
    final itemFinder = find.byKey(const ValueKey('item_50_text'));

    // Scroll until the item to be found appears.
    await tester.scrollUntilVisible(
      itemFinder,
      500.0,
      scrollable: listFinder,
    );

    // Verify that the item contains the correct text.
    expect(itemFinder, findsOneWidget);
  });
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Flutter Cookbook</category>
      <category>dart</category>
      <category>flutter</category>
      <category>개발자팁</category>
      <category>모바일개발</category>
      <category>소프트웨어테스트</category>
      <category>스크롤테스트</category>
      <category>앱개발</category>
      <category>위젯테스트</category>
      <category>테스트자동화</category>
      <category>프로그래밍</category>
      <author>플러터를 배워보자</author>
      <guid isPermaLink="true">https://flutter-explorer.tistory.com/74</guid>
      <comments>https://flutter-explorer.tistory.com/entry/scrolling#entry74comment</comments>
      <pubDate>Mon, 23 Dec 2024 11:00:36 +0900</pubDate>
    </item>
    <item>
      <title>Flutter 위젯 테스트: Finder 사용법 완벽 가이드</title>
      <link>https://flutter-explorer.tistory.com/entry/widgetfinders</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter 애플리케이션 개발에서 위젯 테스트는 코드의 안정성과 품질을 보장하는 데 필수적입니다. 특히, Finder는 테스트 중 특정 위젯을 식별하고 상호작용하는 데 중요한 역할을 합니다. 이번 블로그에서는 Finder의 주요 기능과 사용 방법을 심도 있게 살펴보겠습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고. &lt;a href=&quot;https://docs.flutter.dev/cookbook/testing/widget/finders&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Find&amp;nbsp;widgets&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Finder란 무엇인가요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Finder는 테스트 중 원하는 위젯을 찾기 위해 사용되는 Flutter의 도구입니다. 이를 통해 특정 위젯을 식별하고, 해당 위젯에 대한 다양한 테스트를 수행할 수 있습니다. 주요 Finder 메서드는 다음과 같습니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;findByType&lt;/b&gt;: 위젯의 타입을 기반으로 검색합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;findByKey&lt;/b&gt;: 위젯에 할당된 키를 기반으로 검색합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;findByWidget&lt;/b&gt;: 특정 위젯 인스턴스를 직접 검색합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Finder의 주요 메서드와 사용법&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. &lt;code&gt;find.text&lt;/code&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;텍스트 내용을 기반으로 위젯을 찾습니다. 예를 들어, &quot;로그인&quot;이라는 텍스트를 포함한 위젯을 찾으려면 다음과 같이 사용합니다:&lt;/p&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;find.text('로그인');&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. &lt;code&gt;find.byType&lt;/code&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위젯의 타입을 기반으로 검색합니다. 예를 들어, &lt;code&gt;ElevatedButton&lt;/code&gt; 타입의 모든 위젯을 찾으려면 다음과 같이 사용합니다:&lt;/p&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;find.byType(ElevatedButton);&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. &lt;code&gt;find.byKey&lt;/code&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 키를 가진 위젯을 찾습니다. 키는 위젯에 고유하게 부여되므로, 이를 통해 정확한 위젯을 식별할 수 있습니다:&lt;/p&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;final key = Key('login_button');
find.byKey(key);&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Finder를 활용한 테스트 예시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Finder를 활용하여 위젯을 찾은 후, 해당 위젯에 대한 다양한 테스트를 수행할 수 있습니다. 예를 들어, &quot;로그인&quot; 버튼을 찾아 탭하는 테스트는 다음과 같이 작성할 수 있습니다:&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;// '로그인' 텍스트를 가진 ElevatedButton 위젯을 찾습니다.
final loginButtonFinder = find.widgetWithText(ElevatedButton, '로그인');

// 해당 버튼이 실제로 존재하는지 확인합니다.
expect(loginButtonFinder, findsOneWidget);

// 버튼을 탭합니다.
await tester.tap(loginButtonFinder);

// 애니메이션이 완료될 때까지 기다립니다.
await tester.pumpAndSettle();&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Finder 사용 시 주의사항&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;고유성 보장&lt;/b&gt;: &lt;code&gt;find.byKey&lt;/code&gt;를 사용할 때, 키는 애플리케이션 내에서 고유해야 합니다. 이를 통해 정확한 위젯을 식별할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;위젯 계층 구조 이해&lt;/b&gt;: 복잡한 레이아웃에서는 위젯의 계층 구조를 정확히 파악하여 원하는 위젯을 정확하게 찾는 것이 중요합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;적절한 Finder 선택&lt;/b&gt;: 상황에 따라 가장 적합한 Finder 메서드를 선택하여 테스트의 정확성과 효율성을 높이세요.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter에서 Finder는 위젯 테스트의 핵심 도구로서, 다양한 메서드를 통해 원하는 위젯을 정확하게 찾고 상호작용할 수 있습니다. 이를 통해 애플리케이션의 품질을 높이고, 안정적인 코드를 유지할 수 있습니다. 위에서 소개한 Finder 메서드와 사용법을 숙지하여, 효과적인 테스트 코드를 작성해 보시기 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734571580035&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets('finds a Text widget', (tester) async {
    // Build an App with a Text widget that displays the letter 'H'.
    await tester.pumpWidget(const MaterialApp(
      home: Scaffold(
        body: Text('H'),
      ),
    ));

    // Find a widget that displays the letter 'H'.
    expect(find.text('H'), findsOneWidget);
  });

  testWidgets('finds a widget using a Key', (tester) async {
    // Define the test key.
    const testKey = Key('K');

    // Build a MaterialApp with the testKey.
    await tester.pumpWidget(MaterialApp(key: testKey, home: Container()));

    // Find the MaterialApp widget using the testKey.
    expect(find.byKey(testKey), findsOneWidget);
  });

  testWidgets('finds a specific instance', (tester) async {
    const childWidget = Padding(padding: EdgeInsets.zero);

    // Provide the childWidget to the Container.
    await tester.pumpWidget(Container(child: childWidget));

    // Search for the childWidget in the tree and verify it exists.
    expect(find.byWidget(childWidget), findsOneWidget);
  });
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Flutter Cookbook</category>
      <category>dart</category>
      <category>Finder</category>
      <category>flutter</category>
      <category>개발자팁</category>
      <category>모바일개발</category>
      <category>소프트웨어테스트</category>
      <category>앱개발</category>
      <category>위젯테스트</category>
      <category>테스트자동화</category>
      <category>프로그래밍</category>
      <author>플러터를 배워보자</author>
      <guid isPermaLink="true">https://flutter-explorer.tistory.com/73</guid>
      <comments>https://flutter-explorer.tistory.com/entry/widgetfinders#entry73comment</comments>
      <pubDate>Thu, 19 Dec 2024 11:00:33 +0900</pubDate>
    </item>
    <item>
      <title>Flutter 위젯 테스트 완벽 가이드: 안정적인 UI를 위한 첫걸음</title>
      <link>https://flutter-explorer.tistory.com/entry/widgetintroduction</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter 앱 개발에서 &lt;b&gt;위젯 테스트&lt;/b&gt;는 안정적이고 버그 없는 UI를 구축하는 데 필수적인 과정입니다. 이 글에서는 Flutter에서 위젯 테스트를 수행하는 방법과 그 중요성, 그리고 실질적인 구현 예제를 통해 개발자가 알아야 할 핵심 포인트를 자세히 설명합니다.&lt;/p&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고. &lt;a href=&quot;https://docs.flutter.dev/cookbook/testing/widget/introduction&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;An&amp;nbsp;introduction&amp;nbsp;to&amp;nbsp;widget&amp;nbsp;testing&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;위젯 테스트란 무엇인가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위젯 테스트는 개별 위젯의 동작을 검증하여 UI의 기능성과 안정성을 확인하는 테스트 방법입니다. 단위 테스트보다 상위 수준이며, 통합 테스트보다 하위 수준에 위치합니다. 위젯 테스트를 통해 다음과 같은 이점을 얻을 수 있습니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;UI 동작 검증&lt;/b&gt;: 사용자 인터페이스가 예상대로 동작하는지 확인합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;빠른 피드백&lt;/b&gt;: 테스트 실행 속도가 빨라 코드 변경 시 즉각적인 피드백을 받을 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;버그 조기 발견&lt;/b&gt;: 개발 초기 단계에서 UI 관련 버그를 발견하여 수정 비용을 절감합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Flutter에서 위젯 테스트 설정하기&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 테스트 환경 설정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;flutter_test&lt;/code&gt; 패키지는 Flutter 프로젝트 생성 시 기본적으로 포함됩니다. 별도의 설치 없이 바로 사용할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 테스트 파일 생성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트의 &lt;code&gt;test&lt;/code&gt; 디렉토리에 테스트 파일을 생성합니다. 예를 들어, &lt;code&gt;widget_test.dart&lt;/code&gt; 파일을 만들 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;my_project/
├── lib/
│   └── main.dart
└── test/
    └── widget_test.dart&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기본 위젯 테스트 작성하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 간단한 카운터 앱의 위젯 테스트 예제입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 필요한 패키지 임포트&lt;/h4&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/main.dart';&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 테스트 함수 작성&lt;/h4&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;void main() {
  testWidgets('Counter increments smoke test', (WidgetTester tester) async {
    // 앱 위젯 빌드 및 렌더링
    await tester.pumpWidget(MyApp());

    // 초기 값이 '0'인지 확인
    expect(find.text('0'), findsOneWidget);
    expect(find.text('1'), findsNothing);

    // '+' 아이콘을 탭
    await tester.tap(find.byIcon(Icons.add));
    await tester.pump();

    // 값이 '1'로 증가했는지 확인
    expect(find.text('0'), findsNothing);
    expect(find.text('1'), findsOneWidget);
  });
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주요 함수와 개념 이해하기&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. &lt;code&gt;testWidgets&lt;/code&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위젯 테스트를 정의하는 함수로, 비동기식으로 동작합니다.&lt;/li&gt;
&lt;li&gt;첫 번째 인자로 테스트 설명 문자열, 두 번째 인자로 테스트 콜백 함수를 받습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. &lt;code&gt;WidgetTester&lt;/code&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위젯을 빌드하고 상호 작용을 모방하는 데 사용되는 유틸리티 클래스입니다.&lt;/li&gt;
&lt;li&gt;주요 메서드:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;pumpWidget()&lt;/code&gt;: 위젯을 빌드하고 렌더링합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tap()&lt;/code&gt;: 특정 위젯을 탭하는 동작을 시뮬레이션합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pump()&lt;/code&gt;: 위젯 트리를 다시 빌드하여 상태 변화를 반영합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. &lt;code&gt;find&lt;/code&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위젯 트리에서 특정 위젯을 찾는 데 사용됩니다.&lt;/li&gt;
&lt;li&gt;주요 메서드:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;find.text()&lt;/code&gt;: 텍스트 위젯을 찾습니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;find.byIcon()&lt;/code&gt;: 아이콘 위젯을 찾습니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;find.byType()&lt;/code&gt;: 특정 타입의 위젯을 찾습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. &lt;code&gt;expect&lt;/code&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 값이 예상 값과 일치하는지 검증합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;고급 위젯 테스트 기법&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 상태 변경 테스트&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위젯의 상태 변화에 따른 동작을 테스트합니다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;testWidgets('Checkbox toggles test', (WidgetTester tester) async {
  await tester.pumpWidget(MyApp());

  // 체크박스 초기 상태 확인
  expect(find.byType(Checkbox), findsOneWidget);
  expect(tester.widget&amp;lt;Checkbox&amp;gt;(find.byType(Checkbox)).value, false);

  // 체크박스 탭하여 상태 변경
  await tester.tap(find.byType(Checkbox));
  await tester.pump();

  // 상태 변경 확인
  expect(tester.widget&amp;lt;Checkbox&amp;gt;(find.byType(Checkbox)).value, true);
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 비동기 동작 테스트&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 요청이나 타이머와 같은 비동기 동작을 테스트합니다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;testWidgets('Async data load test', (WidgetTester tester) async {
  await tester.pumpWidget(MyApp());

  // 로딩 인디케이터 확인
  expect(find.byType(CircularProgressIndicator), findsOneWidget);

  // 비동기 작업 완료까지 대기
  await tester.pumpAndSettle();

  // 데이터 로드 후 위젯 확인
  expect(find.text('Loaded Data'), findsOneWidget);
});&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;위젯 테스트 작성 시 고려사항&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;테스트의 독립성 유지&lt;/b&gt;: 각 테스트는 독립적으로 실행되어야 하며, 다른 테스트에 영향을 주지 않아야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;UI 변화에 대한 유연성 확보&lt;/b&gt;: UI 레이아웃 변경에 너무 민감하지 않도록 테스트를 작성합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;실제 사용자 시나리오 반영&lt;/b&gt;: 테스트는 실제 사용자의 행동을 모방해야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;적절한 &lt;code&gt;pump&lt;/code&gt; 사용&lt;/b&gt;: 상태 변화나 애니메이션 후에는 &lt;code&gt;pump()&lt;/code&gt; 또는 &lt;code&gt;pumpAndSettle()&lt;/code&gt;을 사용하여 위젯 트리를 업데이트합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;위젯 테스트의 이점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;버그 예방&lt;/b&gt;: 코드 변경 시 발생할 수 있는 UI 버그를 미리 발견합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;리팩토링 지원&lt;/b&gt;: UI를 변경하더라도 기능이 유지되는지 확인할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;개발 효율성 향상&lt;/b&gt;: 수동 테스트에 소요되는 시간을 절약합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter에서 위젯 테스트는 안정적이고 신뢰할 수 있는 앱을 개발하는 데 필수적인 과정입니다. 위에서 소개한 방법과 예제를 따라 위젯 테스트를 작성하면 앱의 품질을 크게 향상시킬 수 있습니다. 지속적인 테스트를 통해 사용자에게 최상의 경험을 제공하는 앱을 만들어 보세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734483930192&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  // Define a test. The TestWidgets function also provides a WidgetTester
  // to work with. The WidgetTester allows building and interacting
  // with widgets in the test environment.
  testWidgets('MyWidget has a title and message', (tester) async {
    // Create the widget by telling the tester to build it.
    await tester.pumpWidget(const MyWidget(title: 'T', message: 'M'));

    // Create the Finders.
    final titleFinder = find.text('T');
    final messageFinder = find.text('M');

    // Use the `findsOneWidget` matcher provided by flutter_test to
    // verify that the Text widgets appear exactly once in the widget tree.
    expect(titleFinder, findsOneWidget);
    expect(messageFinder, findsOneWidget);
  });
}

class MyWidget extends StatelessWidget {
  const MyWidget({
    super.key,
    required this.title,
    required this.message,
  });

  final String title;
  final String message;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: Scaffold(
        appBar: AppBar(
          title: Text(title),
        ),
        body: Center(
          child: Text(message),
        ),
      ),
    );
  }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Flutter Cookbook</category>
      <category>flutter</category>
      <category>flutter개발</category>
      <category>flutter테스팅</category>
      <category>flutter튜토리얼</category>
      <category>개발자팁</category>
      <category>모바일개발</category>
      <category>앱개발</category>
      <category>위젯테스트</category>
      <category>코드품질</category>
      <category>테스트자동화</category>
      <author>플러터를 배워보자</author>
      <guid isPermaLink="true">https://flutter-explorer.tistory.com/72</guid>
      <comments>https://flutter-explorer.tistory.com/entry/widgetintroduction#entry72comment</comments>
      <pubDate>Wed, 18 Dec 2024 11:00:38 +0900</pubDate>
    </item>
    <item>
      <title>Flutter 단위 테스트에서 Mocking 활용하기: 테스트 코드의 핵심 가이드</title>
      <link>https://flutter-explorer.tistory.com/entry/mocking</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter 개발에서 &lt;b&gt;단위 테스트&lt;/b&gt;는 앱의 기능을 검증하는 필수 과정입니다. 하지만 때때로 특정 함수나 외부 의존성을 테스트할 수 없을 때 &lt;b&gt;Mocking&lt;/b&gt; 기법이 필요합니다. 이번 포스트에서는 &lt;b&gt;Flutter에서 Mocking을 활용하는 방법&lt;/b&gt;을 이해하기 쉽게 설명하고, 테스트 코드 작성 시 유용한 팁과 사례를 공유하겠습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고. &lt;a href=&quot;https://docs.flutter.dev/cookbook/testing/unit/mocking&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Mock&amp;nbsp;dependencies&amp;nbsp;using&amp;nbsp;Mockito&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Mocking이란 무엇인가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Mocking&lt;/b&gt;은 단위 테스트를 실행할 때 &lt;b&gt;외부 의존성이나 복잡한 객체를 가짜(Mock) 객체로 대체하는 기법&lt;/b&gt;입니다. 예를 들어 네트워크 요청, 데이터베이스 호출, API 응답 등 외부 요소를 테스트할 수 없는 상황에서 Mock 객체를 사용하여 코드의 동작을 검증할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Mocking의 장점&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;의존성 제거&lt;/b&gt;: 네트워크, 데이터베이스 등 외부 환경에 의존하지 않습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;테스트 속도 향상&lt;/b&gt;: 가벼운 Mock 객체를 사용하므로 테스트가 더 빠르게 실행됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;유연한 검증&lt;/b&gt;: 특정 함수의 반환값을 설정하여 다양한 시나리오를 테스트할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Flutter에서 Mocking 설정하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter에서는 Mocking을 위해 주로 &lt;b&gt;&lt;code&gt;mockito&lt;/code&gt; 패키지&lt;/b&gt;를 사용합니다. &lt;code&gt;mockito&lt;/code&gt;는 간단하고 강력한 Mock 객체 생성을 지원합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. &lt;code&gt;mockito&lt;/code&gt; 패키지 설치&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 &lt;code&gt;pubspec.yaml&lt;/code&gt; 파일에 &lt;code&gt;mockito&lt;/code&gt; 패키지를 추가합니다:&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;dev_dependencies:
  mockito: ^5.0.0
  flutter_test:
    sdk: flutter&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 패키지를 설치합니다:&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;flutter pub get&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 테스트 파일 생성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 파일은 &lt;code&gt;test&lt;/code&gt; 디렉토리에 저장되며, 예시 이름은 &lt;code&gt;api_service_test.dart&lt;/code&gt;입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Mock 객체 생성 및 사용하기&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. Mock 클래스 생성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트할 클래스를 Mock 객체로 만들기 위해 &lt;code&gt;mockito&lt;/code&gt;의 &lt;b&gt;&lt;code&gt;Mock&lt;/code&gt;&lt;/b&gt; 클래스를 확장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &lt;b&gt;&lt;code&gt;ApiService&lt;/code&gt;&lt;/b&gt;라는 클래스가 있다고 가정해 보겠습니다:&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;class ApiService {
  Future&amp;lt;String&amp;gt; fetchData() async {
    // 실제 네트워크 요청 로직
    return &quot;Real Data&quot;;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 클래스를 테스트하기 위해 &lt;b&gt;Mock 클래스를 생성&lt;/b&gt;합니다:&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;import 'package:mockito/mockito.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/api_service.dart';

// Mock 클래스 생성
class MockApiService extends Mock implements ApiService {}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. Mock 객체 사용&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mock 객체를 사용하여 테스트를 작성하는 예제는 다음과 같습니다:&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;void main() {
  group('ApiService 테스트', () {
    // Mock 객체 선언
    late MockApiService mockApiService;

    setUp(() {
      mockApiService = MockApiService();
    });

    test('fetchData가 &quot;Mock Data&quot;를 반환하는지 확인', () async {
      // Mock 객체의 반환값 설정
      when(mockApiService.fetchData()).thenAnswer((_) async =&amp;gt; &quot;Mock Data&quot;);

      // 테스트 실행
      final result = await mockApiService.fetchData();

      // 결과 검증
      expect(result, &quot;Mock Data&quot;);
    });
  });
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주요 함수와 개념&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. &lt;code&gt;when&lt;/code&gt;과 &lt;code&gt;thenAnswer&lt;/code&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;when&lt;/code&gt;&lt;/b&gt;: 특정 함수가 호출될 때 동작을 정의합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;thenAnswer&lt;/code&gt;&lt;/b&gt;: 비동기 함수의 반환값을 설정할 때 사용합니다.&lt;/li&gt;
&lt;li&gt;예:
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;when(mockApiService.fetchData()).thenAnswer((_) async =&amp;gt; &quot;Mock Result&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. &lt;code&gt;verify&lt;/code&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 함수가 호출되었는지 확인할 수 있습니다.&lt;/li&gt;
&lt;li&gt;예:
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;verify(mockApiService.fetchData()).called(1);&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. &lt;code&gt;any&lt;/code&gt;와 &lt;code&gt;captureAny&lt;/code&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;any&lt;/code&gt;&lt;/b&gt;: 인자 값에 관계없이 호출을 검증합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;captureAny&lt;/code&gt;&lt;/b&gt;: 인자 값을 캡처합니다.&lt;/li&gt;
&lt;li&gt;예:
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;when(mockApiService.fetchData(any)).thenReturn(&quot;Mock Value&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;모범 사례 및 팁&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;단일 동작 검증&lt;/b&gt;: 각 테스트 케이스는 하나의 동작만 검증하도록 작성하세요.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;실제 클래스와 Mock 객체 분리&lt;/b&gt;: Mock 객체는 테스트 코드에서만 사용해야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;가짜 반환값 다양화&lt;/b&gt;: 다양한 시나리오(성공, 실패 등)를 가정하여 여러 반환값을 설정해보세요.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;setUp&lt;/code&gt;과 &lt;code&gt;tearDown&lt;/code&gt; 사용&lt;/b&gt;: 테스트 전후로 초기화 작업을 수행합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Flutter Mocking의 활용 사례&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;네트워크 API 테스트&lt;/b&gt;: 외부 API 호출 대신 Mock 데이터를 반환합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터베이스 검증&lt;/b&gt;: 데이터베이스 호출을 가짜 객체로 대체합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;UI 로직 테스트&lt;/b&gt;: 위젯 테스트에서 비즈니스 로직을 검증할 때 활용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mocking은 Flutter 단위 테스트에서 필수적인 기술입니다. &lt;code&gt;mockito&lt;/code&gt; 패키지를 활용하면 복잡한 외부 의존성을 제거하고 더 빠르고 유연한 테스트를 작성할 수 있습니다. 앱의 품질을 높이기 위해 Mock 객체를 적극적으로 활용해 보세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter 개발에서 단위 테스트와 Mocking을 완벽하게 마스터한다면 안정적이고 확장 가능한 앱을 개발할 수 있을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734397662431&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import 'dart:async';
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

Future&amp;lt;Album&amp;gt; fetchAlbum(http.Client client) async {
  final response = await client
      .get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));

  if (response.statusCode == 200) {
    // If the server did return a 200 OK response,
    // then parse the JSON.
    return Album.fromJson(jsonDecode(response.body) as Map&amp;lt;String, dynamic&amp;gt;);
  } else {
    // If the server did not return a 200 OK response,
    // then throw an exception.
    throw Exception('Failed to load album');
  }
}

class Album {
  final int userId;
  final int id;
  final String title;

  const Album({required this.userId, required this.id, required this.title});

  factory Album.fromJson(Map&amp;lt;String, dynamic&amp;gt; json) {
    return Album(
      userId: json['userId'] as int,
      id: json['id'] as int,
      title: json['title'] as String,
    );
  }
}

void main() =&amp;gt; runApp(const MyApp());

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State&amp;lt;MyApp&amp;gt; createState() =&amp;gt; _MyAppState();
}

class _MyAppState extends State&amp;lt;MyApp&amp;gt; {
  late final Future&amp;lt;Album&amp;gt; futureAlbum;

  @override
  void initState() {
    super.initState();
    futureAlbum = fetchAlbum(http.Client());
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Fetch Data Example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      ),
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Fetch Data Example'),
        ),
        body: Center(
          child: FutureBuilder&amp;lt;Album&amp;gt;(
            future: futureAlbum,
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return Text(snapshot.data!.title);
              } else if (snapshot.hasError) {
                return Text('${snapshot.error}');
              }

              // By default, show a loading spinner.
              return const CircularProgressIndicator();
            },
          ),
        ),
      ),
    );
  }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Flutter Cookbook</category>
      <category>apptesting</category>
      <category>flutter</category>
      <category>fluttermocking</category>
      <category>fluttertesting</category>
      <category>flutter단위테스트</category>
      <category>mocking</category>
      <category>mockito</category>
      <category>개발자팁</category>
      <category>단위테스트</category>
      <category>모바일개발</category>
      <author>플러터를 배워보자</author>
      <guid isPermaLink="true">https://flutter-explorer.tistory.com/71</guid>
      <comments>https://flutter-explorer.tistory.com/entry/mocking#entry71comment</comments>
      <pubDate>Tue, 17 Dec 2024 11:00:56 +0900</pubDate>
    </item>
    <item>
      <title>Flutter 단위 테스트(Unit Testing) 완벽 가이드: 고품질 앱을 만드는 핵심</title>
      <link>https://flutter-explorer.tistory.com/entry/introduction-1</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter 앱 개발에서 테스트는 필수 요소입니다. 특히 &lt;b&gt;단위 테스트(Unit Testing)&lt;/b&gt; 는 코드의 안정성과 품질을 보장하기 위한 가장 기본적인 단계입니다. 이번 글에서는 Flutter에서 단위 테스트를 설정하고 실행하는 방법을 단계별로 설명합니다. 또한, 테스트를 작성하는 주요 팁과 모범 사례를 함께 살펴보겠습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고. &lt;a href=&quot;https://docs.flutter.dev/cookbook/testing/unit/introduction&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;An&amp;nbsp;introduction&amp;nbsp;to&amp;nbsp;unit&amp;nbsp;testing&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Flutter 단위 테스트란 무엇인가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단위 테스트는 코드의 가장 작은 단위(주로 함수 또는 메서드)의 동작을 검증하는 테스트입니다. Flutter에서 단위 테스트는 다음과 같은 이점을 제공합니다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;코드 안정성 확보&lt;/b&gt;: 작은 코드 변경이 전체 애플리케이션에 미치는 영향을 줄입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;버그 조기 발견&lt;/b&gt;: 개발 초기 단계에서 문제를 식별하고 수정할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;생산성 향상&lt;/b&gt;: 테스트를 통해 코드 품질을 높이고, 디버깅 시간을 줄입니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단위 테스트 기본 설정&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. &lt;code&gt;flutter_test&lt;/code&gt; 패키지 사용&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter는 기본적으로 단위 테스트를 지원하며, &lt;code&gt;flutter_test&lt;/code&gt; 패키지를 포함합니다. 이는 Flutter 프로젝트 생성 시 기본적으로 포함됩니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 테스트 파일 생성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 파일은 &lt;code&gt;test&lt;/code&gt; 디렉토리 아래에 생성됩니다. 예를 들어, &lt;code&gt;test/counter_test.dart&lt;/code&gt;와 같은 이름을 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;project/
│
├── test/
│   └── counter_test.dart&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Flutter 단위 테스트 작성 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter에서 단위 테스트는 주로 &lt;b&gt;&lt;code&gt;test&lt;/code&gt; 함수&lt;/b&gt;를 사용하여 작성됩니다. 다음은 기본적인 단위 테스트 작성 예제입니다:&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. &lt;code&gt;flutter_test&lt;/code&gt; 임포트&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;import 'package:flutter_test/flutter_test.dart';
import 'package:my_app/counter.dart'; // 실제 테스트할 파일&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 테스트 함수 작성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 &lt;code&gt;Counter&lt;/code&gt; 클래스를 테스트하는 기본 예제입니다:&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;void main() {
  group('Counter', () {
    test('초기 값이 0인지 확인', () {
      final counter = Counter();
      expect(counter.value, 0);
    });

    test('increment 호출 시 값이 1 증가하는지 확인', () {
      final counter = Counter();
      counter.increment();
      expect(counter.value, 1);
    });

    test('decrement 호출 시 값이 1 감소하는지 확인', () {
      final counter = Counter();
      counter.decrement();
      expect(counter.value, -1);
    });
  });
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주요 함수 및 개념&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. &lt;code&gt;group&lt;/code&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;관련 테스트를 그룹화하여 논리적 구조를 제공합니다.&lt;/li&gt;
&lt;li&gt;예:
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;group('Counter Tests', () {
  test('...', () { ... });
});&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. &lt;code&gt;test&lt;/code&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단일 테스트를 정의하는 데 사용됩니다.&lt;/li&gt;
&lt;li&gt;매개변수:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테스트 이름&lt;/li&gt;
&lt;li&gt;테스트 함수&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;예:
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;test('example test', () {
  expect(1 + 1, 2);
});&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. &lt;code&gt;expect&lt;/code&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예상 결과를 정의하며, 테스트의 성공 여부를 결정합니다.&lt;/li&gt;
&lt;li&gt;예:
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;expect(actual, expected);&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단위 테스트 실행&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 터미널에서 실행&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 명령어로 모든 테스트를 실행할 수 있습니다:&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;flutter test&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 특정 파일 테스트&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 파일만 테스트하려면 다음과 같이 실행합니다:&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;flutter test test/counter_test.dart&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단위 테스트 모범 사례&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;하나의 테스트에 하나의 기능만 검증&lt;/b&gt;: 각 테스트는 단일 동작을 검증하도록 설계합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;테스트 이름을 명확히 작성&lt;/b&gt;: 테스트 이름은 동작을 명확히 설명해야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;setUp&lt;/code&gt;과 &lt;code&gt;tearDown&lt;/code&gt; 사용&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 테스트 실행 전후에 초기화 작업을 수행합니다.
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;setUp(() {
// 초기화 코드
});
tearDown(() {
// 정리 코드
});&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Flutter 단위 테스트의 중요성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter 단위 테스트는 개발 초기 단계에서 오류를 발견하고, 코드 변경 시 기존 기능이 의도치 않게 망가지는 것을 방지하는 데 매우 유용합니다. 특히 대규모 프로젝트에서는 단위 테스트가 필수적입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter에서 단위 테스트는 고품질 앱을 만드는 데 필수적인 과정입니다. &lt;code&gt;flutter_test&lt;/code&gt;를 활용해 테스트를 작성하고, 모범 사례를 따라 코드 품질을 높여 보세요. 테스트 자동화를 통해 생산성을 극대화하고, 사용자에게 안정적인 앱 경험을 제공할 수 있습니다.&lt;/p&gt;</description>
      <category>Flutter Cookbook</category>
      <category>appdevelopment</category>
      <category>flutter</category>
      <category>flutterguide</category>
      <category>fluttertesting</category>
      <category>flutterunittest</category>
      <category>flutter개발</category>
      <category>softwaretesting</category>
      <category>UnitTesting</category>
      <category>앱개발</category>
      <category>코드품질</category>
      <author>플러터를 배워보자</author>
      <guid isPermaLink="true">https://flutter-explorer.tistory.com/70</guid>
      <comments>https://flutter-explorer.tistory.com/entry/introduction-1#entry70comment</comments>
      <pubDate>Mon, 16 Dec 2024 11:00:50 +0900</pubDate>
    </item>
    <item>
      <title>Flutter 통합 테스트에서 성능 프로파일링: 최적화된 앱 개발의 비결</title>
      <link>https://flutter-explorer.tistory.com/entry/profiling</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter 앱 개발에서 성능은 사용자 경험에 직결되는 핵심 요소입니다. 특히 통합 테스트와 성능 프로파일링을 결합하면, 실제 사용자 환경에서의 성능 문제를 미리 발견하고 해결할 수 있습니다. 이번 포스트에서는 Flutter 통합 테스트에서 성능 프로파일링을 구현하는 방법과 이를 통해 얻을 수 있는 이점을 심도 있게 다뤄보겠습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고. &lt;a href=&quot;https://docs.flutter.dev/cookbook/testing/integration/profiling&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Measure&amp;nbsp;performance&amp;nbsp;with&amp;nbsp;an&amp;nbsp;integration&amp;nbsp;test&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;성능 프로파일링이란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성능 프로파일링은 애플리케이션이 실행되는 동안 리소스 사용, 응답 시간, 프레임 속도 등의 주요 성능 데이터를 수집하고 분석하는 과정입니다. 이를 통해 성능 병목 현상을 찾아내고, 앱의 실행 효율성을 개선할 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Flutter에서 성능 프로파일링의 목적&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;FPS(Frame Per Second) 최적화&lt;/b&gt;&lt;br /&gt;앱의 렌더링 성능을 분석하여 화면 전환 및 애니메이션 부드러움을 보장.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CPU 및 메모리 사용 분석&lt;/b&gt;&lt;br /&gt;불필요한 자원 소비를 파악하고 최적화 방안을 모색.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;네트워크 요청 속도 확인&lt;/b&gt;&lt;br /&gt;API 응답 시간 및 데이터 처리 성능 개선.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;통합 테스트와 성능 프로파일링의 연계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;통합 테스트와 성능 프로파일링을 결합하면 앱의 주요 흐름을 테스트하면서 동시에 성능 데이터를 수집할 수 있습니다. 이는 단순히 기능적 오류를 찾는 것을 넘어, 실제 사용자 환경에서의 성능 최적화를 가능하게 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Flutter에서 성능 프로파일링 구현 방법&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 의존성 추가&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;pubspec.yaml&lt;/code&gt;에 다음과 같은 패키지를 추가합니다:&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;dev_dependencies:
  integration_test:
    sdk: flutter&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가 후 &lt;code&gt;flutter pub get&lt;/code&gt; 명령어를 실행합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 통합 테스트 구성&lt;/h4&gt;
&lt;h5&gt;&lt;b&gt;테스트 드라이버 작성&lt;/b&gt;&lt;/h5&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;test_driver/integration_test_driver.dart&lt;/code&gt; 파일을 생성하고 아래 코드를 작성합니다:&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;import 'package:integration_test/integration_test_driver_extended.dart';

Future&amp;lt;void&amp;gt; main() =&amp;gt; integrationDriver();&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;&lt;b&gt;테스트 파일 작성&lt;/b&gt;&lt;/h5&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;integration_test/app_test.dart&lt;/code&gt; 파일에 아래 코드를 추가합니다:&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:your_app/main.dart' as app;

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  testWidgets('홈 화면 로딩 성능 측정', (WidgetTester tester) async {
    app.main();
    await tester.pumpAndSettle();

    final Stopwatch stopwatch = Stopwatch()..start();

    // 특정 위젯 확인
    expect(find.text('Welcome'), findsOneWidget);

    stopwatch.stop();
    print('로딩 시간: ${stopwatch.elapsedMilliseconds}ms');
  });
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 성능 데이터 수집 및 분석&lt;/h4&gt;
&lt;h5&gt;&lt;b&gt;성능 데이터 수집&lt;/b&gt;&lt;/h5&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 실행 시 다음 명령어로 성능 데이터를 수집합니다:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;flutter drive --driver=test_driver/integration_test_driver.dart --target=integration_test/app_test.dart --profile&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;&lt;b&gt;프로파일링 데이터 분석&lt;/b&gt;&lt;/h5&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과는 JSON 형식으로 저장되며, 이를 Flutter DevTools 또는 기타 데이터 분석 도구를 활용해 시각화할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;성능 최적화를 위한 팁&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;느린 렌더링 해결&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;debugProfileBuildsEnabled&lt;/code&gt; 및 &lt;code&gt;debugProfilePaintsEnabled&lt;/code&gt;를 사용해 렌더링 문제를 확인합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;네트워크 호출 최적화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;dio&lt;/code&gt; 패키지와 같은 HTTP 클라이언트를 활용하여 요청 시간을 측정하고 최적화합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Flutter DevTools 활용&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DevTools를 사용해 CPU, 메모리, 프레임 속도 데이터를 상세히 분석합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Jank(프레임 끊김) 최소화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;shouldRebuild&lt;/code&gt; 메서드를 적절히 사용하여 불필요한 위젯 재구성을 방지합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;통합 테스트와 성능 프로파일링의 실질적 이점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;실제 사용자 경험 개선&lt;/b&gt;&lt;br /&gt;사용자 흐름을 따라 테스트하면서 성능 병목 현상을 제거.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;효율적인 자원 관리&lt;/b&gt;&lt;br /&gt;CPU 및 메모리 사용을 분석하여 불필요한 낭비 제거.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;빠른 문제 발견 및 수정&lt;/b&gt;&lt;br /&gt;문제를 사전에 파악해 유지보수 비용 절감.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter 통합 테스트와 성능 프로파일링은 단순히 오류를 찾는 것을 넘어, 사용자 경험을 개선하고 앱의 실행 효율성을 극대화할 수 있는 강력한 도구입니다. 위 가이드를 따라 프로젝트에 통합하고, 더 나은 Flutter 앱을 만들어보세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734044842154&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// integration_test/scrolling_test.dart

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';

import 'package:scrolling/main.dart';

void main() {
  final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  testWidgets('Counter increments smoke test', (tester) async {
    // Build our app and trigger a frame.
    await tester.pumpWidget(MyApp(
      items: List&amp;lt;String&amp;gt;.generate(10000, (i) =&amp;gt; 'Item $i'),
    ));

    final listFinder = find.byType(Scrollable);
    final itemFinder = find.byKey(const ValueKey('item_50_text'));

    await binding.traceAction(
      () async {
        // Scroll until the item to be found appears.
        await tester.scrollUntilVisible(
          itemFinder,
          500.0,
          scrollable: listFinder,
        );
      },
      reportKey: 'scrolling_timeline',
    );
  });
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734044871924&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// test_driver/perf_driver.dart

import 'package:flutter_driver/flutter_driver.dart' as driver;
import 'package:integration_test/integration_test_driver.dart';

Future&amp;lt;void&amp;gt; main() {
  return integrationDriver(
    responseDataCallback: (data) async {
      if (data != null) {
        final timeline = driver.Timeline.fromJson(
          data['scrolling_timeline'] as Map&amp;lt;String, dynamic&amp;gt;,
        );

        // Convert the Timeline into a TimelineSummary that's easier to
        // read and understand.
        final summary = driver.TimelineSummary.summarize(timeline);

        // Then, write the entire timeline to disk in a json format.
        // This file can be opened in the Chrome browser's tracing tools
        // found by navigating to chrome://tracing.
        // Optionally, save the summary to disk by setting includeSummary
        // to true
        await summary.writeTimelineToFile(
          'scrolling_timeline',
          pretty: true,
          includeSummary: true,
        );
      }
    },
  );
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Flutter Cookbook</category>
      <category>flutter</category>
      <category>flutterdevtools</category>
      <category>fluttertesting</category>
      <category>flutter성능분석</category>
      <category>flutter앱개발</category>
      <category>flutter테스트</category>
      <category>IntegrationTest</category>
      <category>performanceprofiling</category>
      <category>모바일앱테스트</category>
      <category>앱성능최적화</category>
      <author>플러터를 배워보자</author>
      <guid isPermaLink="true">https://flutter-explorer.tistory.com/69</guid>
      <comments>https://flutter-explorer.tistory.com/entry/profiling#entry69comment</comments>
      <pubDate>Sun, 15 Dec 2024 11:00:02 +0900</pubDate>
    </item>
    <item>
      <title>Flutter 통합 테스트 완벽 가이드: 안정적인 앱 개발을 위한 첫걸음</title>
      <link>https://flutter-explorer.tistory.com/entry/introduction</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter는 빠르고 강력한 앱 개발 환경을 제공하지만, 사용자 경험의 안정성을 보장하기 위해서는 테스트가 필수적입니다. 이 중에서도 통합 테스트는 앱의 주요 흐름과 기능이 예상대로 작동하는지 확인하는 중요한 단계입니다. 본 블로그에서는 Flutter 통합 테스트의 기본 개념부터 실질적인 구현 방법까지 자세히 알아보겠습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고. &lt;a href=&quot;https://docs.flutter.dev/cookbook/testing/integration/introduction&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Integration&amp;nbsp;testing&amp;nbsp;concepts&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;통합 테스트란 무엇인가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;통합 테스트는 앱의 여러 위젯과 서비스를 통합적으로 테스트하여 사용자가 실제로 앱을 사용할 때 발생할 수 있는 문제를 발견하는 과정입니다. 이는 다음과 같은 이점을 제공합니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;기능 안정성 확인&lt;/b&gt;: 주요 사용자 흐름이 올바르게 작동하는지 보장.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;회귀 테스트&lt;/b&gt;: 새로운 코드가 기존 기능에 문제를 일으키지 않는지 검증.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용자 경험 개선&lt;/b&gt;: 사용자 인터페이스(UI)와 사용자 경험(UX)의 문제점을 미리 발견.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Flutter 통합 테스트의 구조&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter 통합 테스트는 다음 세 가지 주요 구성 요소로 이루어집니다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;테스트 드라이버&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테스트 실행을 위한 코드 작성.&lt;/li&gt;
&lt;li&gt;보통 &lt;code&gt;test_driver/&lt;/code&gt; 디렉터리에 작성.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;테스트 파일&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 테스트가 진행되는 파일.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;integration_test/&lt;/code&gt; 디렉터리에 작성.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Flutter Integration Test 패키지&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;통합 테스트를 위한 Flutter의 기본 패키지.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;integration_test&lt;/code&gt; 라이브러리를 사용.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;통합 테스트 환경 설정&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 패키지 설치&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;pubspec.yaml&lt;/code&gt; 파일에 다음 의존성을 추가합니다:&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;dev_dependencies:
  integration_test:
    sdk: flutter&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 &lt;code&gt;flutter pub get&lt;/code&gt; 명령어를 실행합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 프로젝트 디렉터리 구조&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter 통합 테스트는 다음과 같은 디렉터리 구조를 권장합니다:&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;/test_driver
  /integration_test_driver.dart
/integration_test
  /app_test.dart&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Flutter 통합 테스트 작성하기&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 테스트 드라이버 생성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;test_driver/integration_test_driver.dart&lt;/code&gt; 파일을 생성하고 아래 코드를 작성합니다:&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;import 'package:integration_test/integration_test_driver.dart';

Future&amp;lt;void&amp;gt; main() =&amp;gt; integrationDriver();&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 테스트 파일 작성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;integration_test/app_test.dart&lt;/code&gt; 파일에 다음 코드를 추가합니다:&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:your_app/main.dart' as app;

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();

  testWidgets('앱 시작 시 홈 화면 확인', (WidgetTester tester) async {
    app.main();
    await tester.pumpAndSettle();

    expect(find.text('Welcome'), findsOneWidget);
  });
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Flutter 통합 테스트 실행하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 실행은 다음 명령어를 사용합니다:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;flutter test integration_test/app_test.dart&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 Android Studio/Visual Studio Code의 테스트 실행 도구를 사용할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;통합 테스트 작성 시 고려 사항&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;테스트 독립성 유지&lt;/b&gt;&lt;br /&gt;테스트는 서로 영향을 주지 않도록 독립적으로 작성해야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;대기 시간 관리&lt;/b&gt;&lt;br /&gt;API 호출이나 애니메이션처럼 대기 시간이 필요한 경우, &lt;code&gt;await tester.pumpAndSettle()&lt;/code&gt;를 활용하여 충분한 시간을 부여합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;실제 사용자 시나리오에 초점&lt;/b&gt;&lt;br /&gt;테스트 케이스는 실제 사용자가 앱을 사용하는 방식을 반영해야 합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Flutter 통합 테스트의 확장 활용&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;백엔드와의 통신 테스트&lt;/b&gt;&lt;br /&gt;API 응답 데이터를 활용해 네트워크 통신이 제대로 이루어지는지 검증합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;다양한 디바이스 환경에서 테스트&lt;/b&gt;&lt;br /&gt;화면 크기와 OS 버전에 따른 UI 테스트를 진행합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;지속적 통합(CI) 도구와 연동&lt;/b&gt;&lt;br /&gt;Jenkins, GitHub Actions와 같은 CI 도구와 통합하여 테스트 자동화를 구현합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter 통합 테스트는 앱 개발의 필수적인 부분으로, 앱의 기능 안정성과 사용자 경험을 강화합니다. 위 가이드를 통해 통합 테스트를 설정하고 효과적으로 활용하여 Flutter 앱의 품질을 높여보세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Flutter Cookbook</category>
      <category>flutter</category>
      <category>fluttertesting</category>
      <category>fluttertestingguide</category>
      <category>flutter개발</category>
      <category>flutter앱품질</category>
      <category>flutter통합테스트</category>
      <category>flutter튜토리얼</category>
      <category>integrationtesting</category>
      <category>모바일앱테스트</category>
      <category>테스트자동화</category>
      <author>플러터를 배워보자</author>
      <guid isPermaLink="true">https://flutter-explorer.tistory.com/68</guid>
      <comments>https://flutter-explorer.tistory.com/entry/introduction#entry68comment</comments>
      <pubDate>Sat, 14 Dec 2024 11:00:53 +0900</pubDate>
    </item>
    <item>
      <title>Flutter에서 카메라로 사진 촬영하기: Flutter Camera Plugin 완벽 가이드</title>
      <link>https://flutter-explorer.tistory.com/entry/picture-using-camera</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter는 다양한 플러그인을 제공하여 모바일 개발을 더욱 쉽고 강력하게 만들어줍니다. 그중 하나가 카메라 플러그인으로, 이를 사용하면 간단히 앱에서 사진을 찍거나 동영상을 녹화할 수 있습니다. 이 블로그에서는 Flutter Camera 플러그인을 사용해 카메라 기능을 구현하는 방법을 단계별로 알아보겠습니다.&lt;/p&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고. &lt;/b&gt;&lt;a href=&quot;https://docs.flutter.dev/cookbook/plugins/picture-using-camera&quot;&gt;&lt;b&gt;Take a picture using the camera&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;왜 Flutter Camera Plugin을 사용해야 할까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter Camera Plugin은 다음과 같은 강력한 기능을 제공합니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;사진 촬영&lt;/b&gt;: 앱 내에서 고품질의 이미지를 캡처 가능.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;동영상 녹화&lt;/b&gt;: 동영상 촬영과 저장을 위한 API 제공.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;다양한 플랫폼 지원&lt;/b&gt;: Android와 iOS 모두에서 동일한 코드로 구현 가능.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;다양한 설정 옵션&lt;/b&gt;: 카메라 해상도, 플래시 모드, 줌 설정 등 세부 조정 가능.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Flutter Camera Plugin 설정 방법&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 플러그인 설치&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter 프로젝트에서 &lt;code&gt;camera&lt;/code&gt; 플러그인을 추가합니다. 프로젝트의 &lt;code&gt;pubspec.yaml&lt;/code&gt; 파일에 다음을 추가하세요:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;dependencies:
  camera: ^0.10.0+4&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 &lt;code&gt;flutter pub get&lt;/code&gt; 명령어를 실행해 플러그인을 설치합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. Android 및 iOS 권한 설정&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Android&lt;/b&gt;: &lt;code&gt;AndroidManifest.xml&lt;/code&gt; 파일에 다음 권한을 추가합니다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;uses-permission android:name=&quot;android.permission.CAMERA&quot; /&amp;gt;
&amp;lt;uses-permission android:name=&quot;android.permission.RECORD_AUDIO&quot; /&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;iOS&lt;/b&gt;: &lt;code&gt;Info.plist&lt;/code&gt; 파일에 다음 키를 추가합니다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;key&amp;gt;NSCameraUsageDescription&amp;lt;/key&amp;gt;
&amp;lt;string&amp;gt;이 앱은 카메라를 사용합니다.&amp;lt;/string&amp;gt;
&amp;lt;key&amp;gt;NSMicrophoneUsageDescription&amp;lt;/key&amp;gt;
&amp;lt;string&amp;gt;이 앱은 마이크를 사용합니다.&amp;lt;/string&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 초기화 및 카메라 컨트롤러 설정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카메라를 사용하려면 &lt;code&gt;CameraController&lt;/code&gt;를 초기화해야 합니다. 다음은 기본적인 초기화 코드입니다:&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;import 'package:camera/camera.dart';
import 'package:flutter/material.dart';

class CameraExample extends StatefulWidget {
  @override
  _CameraExampleState createState() =&amp;gt; _CameraExampleState();
}

class _CameraExampleState extends State&amp;lt;CameraExample&amp;gt; {
  late CameraController _controller;
  late List&amp;lt;CameraDescription&amp;gt; _cameras;

  @override
  void initState() {
    super.initState();
    _initializeCamera();
  }

  Future&amp;lt;void&amp;gt; _initializeCamera() async {
    _cameras = await availableCameras();
    _controller = CameraController(
      _cameras[0], // 후면 카메라 선택
      ResolutionPreset.high,
    );
    await _controller.initialize();
    setState(() {});
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    if (!_controller.value.isInitialized) {
      return Center(child: CircularProgressIndicator());
    }
    return CameraPreview(_controller);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Flutter로 사진 촬영하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사진을 촬영하려면 &lt;code&gt;CameraController.takePicture()&lt;/code&gt; 메서드를 사용합니다. 다음은 버튼 클릭으로 사진을 촬영하고 저장하는 예제입니다:&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;Future&amp;lt;void&amp;gt; _takePicture() async {
  try {
    final image = await _controller.takePicture();
    print('사진 저장 경로: ${image.path}');
  } catch (e) {
    print('사진 촬영 실패: $e');
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용자 경험을 위한 추가 기능&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 플래시 모드 변경&lt;/h4&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;await _controller.setFlashMode(FlashMode.auto);&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 줌 기능 추가&lt;/h4&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;await _controller.setZoomLevel(2.0);&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 동영상 녹화&lt;/h4&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;await _controller.startVideoRecording();
await _controller.stopVideoRecording();&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주요 고려사항&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;권한 요청&lt;/b&gt;: Android와 iOS의 권한 요청은 사용자가 앱을 처음 실행할 때 꼭 확인해야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;디바이스 호환성&lt;/b&gt;: 모든 디바이스가 동일한 카메라 설정을 지원하지 않을 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;리소스 관리&lt;/b&gt;: 카메라 컨트롤러는 사용 후 반드시 &lt;code&gt;dispose()&lt;/code&gt;를 호출해 리소스를 해제해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Flutter Camera Plugin으로 확장 가능한 프로젝트 아이디어&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;QR 코드 스캐너&lt;/b&gt;: 사진 촬영 대신 실시간 QR 코드 스캔 기능 구현.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SNS 카메라 앱&lt;/b&gt;: 필터와 스티커를 추가해 사진 편집 가능.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;동영상 편집 앱&lt;/b&gt;: 녹화한 동영상에 간단한 편집 기능 추가.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter Camera Plugin은 Flutter 개발자들에게 강력한 도구를 제공합니다. 이 플러그인을 활용하면 단순한 사진 촬영 앱부터 복잡한 멀티미디어 애플리케이션까지 다양한 기능을 손쉽게 구현할 수 있습니다. 위 가이드를 참고하여 여러분만의 독창적인 카메라 애플리케이션을 만들어 보세요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734044632681&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import 'dart:async';
import 'dart:io';

import 'package:camera/camera.dart';
import 'package:flutter/material.dart';

Future&amp;lt;void&amp;gt; main() async {
  // Ensure that plugin services are initialized so that `availableCameras()`
  // can be called before `runApp()`
  WidgetsFlutterBinding.ensureInitialized();

  // Obtain a list of the available cameras on the device.
  final cameras = await availableCameras();

  // Get a specific camera from the list of available cameras.
  final firstCamera = cameras.first;

  runApp(
    MaterialApp(
      theme: ThemeData.dark(),
      home: TakePictureScreen(
        // Pass the appropriate camera to the TakePictureScreen widget.
        camera: firstCamera,
      ),
    ),
  );
}

// A screen that allows users to take a picture using a given camera.
class TakePictureScreen extends StatefulWidget {
  const TakePictureScreen({
    super.key,
    required this.camera,
  });

  final CameraDescription camera;

  @override
  TakePictureScreenState createState() =&amp;gt; TakePictureScreenState();
}

class TakePictureScreenState extends State&amp;lt;TakePictureScreen&amp;gt; {
  late CameraController _controller;
  late Future&amp;lt;void&amp;gt; _initializeControllerFuture;

  @override
  void initState() {
    super.initState();
    // To display the current output from the Camera,
    // create a CameraController.
    _controller = CameraController(
      // Get a specific camera from the list of available cameras.
      widget.camera,
      // Define the resolution to use.
      ResolutionPreset.medium,
    );

    // Next, initialize the controller. This returns a Future.
    _initializeControllerFuture = _controller.initialize();
  }

  @override
  void dispose() {
    // Dispose of the controller when the widget is disposed.
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Take a picture')),
      // You must wait until the controller is initialized before displaying the
      // camera preview. Use a FutureBuilder to display a loading spinner until the
      // controller has finished initializing.
      body: FutureBuilder&amp;lt;void&amp;gt;(
        future: _initializeControllerFuture,
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.done) {
            // If the Future is complete, display the preview.
            return CameraPreview(_controller);
          } else {
            // Otherwise, display a loading indicator.
            return const Center(child: CircularProgressIndicator());
          }
        },
      ),
      floatingActionButton: FloatingActionButton(
        // Provide an onPressed callback.
        onPressed: () async {
          // Take the Picture in a try / catch block. If anything goes wrong,
          // catch the error.
          try {
            // Ensure that the camera is initialized.
            await _initializeControllerFuture;

            // Attempt to take a picture and get the file `image`
            // where it was saved.
            final image = await _controller.takePicture();

            if (!context.mounted) return;

            // If the picture was taken, display it on a new screen.
            await Navigator.of(context).push(
              MaterialPageRoute(
                builder: (context) =&amp;gt; DisplayPictureScreen(
                  // Pass the automatically generated path to
                  // the DisplayPictureScreen widget.
                  imagePath: image.path,
                ),
              ),
            );
          } catch (e) {
            // If an error occurs, log the error to the console.
            print(e);
          }
        },
        child: const Icon(Icons.camera_alt),
      ),
    );
  }
}

// A widget that displays the picture taken by the user.
class DisplayPictureScreen extends StatelessWidget {
  final String imagePath;

  const DisplayPictureScreen({super.key, required this.imagePath});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Display the Picture')),
      // The image is stored as a file on the device. Use the `Image.file`
      // constructor with the given path to display the image.
      body: Image.file(File(imagePath)),
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Flutter Cookbook</category>
      <category>cameraplugin</category>
      <category>flutter</category>
      <category>fluttercamera</category>
      <category>flutterui</category>
      <category>flutter동영상녹화</category>
      <category>flutter사진촬영</category>
      <category>flutter초보</category>
      <category>flutter튜토리얼</category>
      <category>모바일앱개발</category>
      <category>앱개발가이드</category>
      <author>플러터를 배워보자</author>
      <guid isPermaLink="true">https://flutter-explorer.tistory.com/67</guid>
      <comments>https://flutter-explorer.tistory.com/entry/picture-using-camera#entry67comment</comments>
      <pubDate>Fri, 13 Dec 2024 11:00:10 +0900</pubDate>
    </item>
    <item>
      <title>Flutter에서 Google Mobile Ads 통합하기: 광고 수익화의 모든 것</title>
      <link>https://flutter-explorer.tistory.com/entry/google-mobile-ads-1</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter 애플리케이션을 개발하면서 광고 수익화를 고려하고 있다면, Google Mobile Ads 플러그인은 꼭 알아야 할 필수 도구입니다. 이 글에서는 Google Mobile Ads 플러그인의 주요 기능과 Flutter에서 이를 어떻게 통합하고 활용할 수 있는지 단계별로 안내합니다.&lt;/p&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고. &lt;a href=&quot;https://docs.flutter.dev/cookbook/plugins/google-mobile-ads&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Add&amp;nbsp;ads&amp;nbsp;to&amp;nbsp;your&amp;nbsp;mobile&amp;nbsp;Flutter&amp;nbsp;app&amp;nbsp;or&amp;nbsp;game&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Google Mobile Ads란 무엇인가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Google Mobile Ads는 Google AdMob 네트워크를 Flutter 애플리케이션에 통합할 수 있게 하는 플러그인입니다. 이를 통해 앱 개발자는 &lt;b&gt;배너 광고&lt;/b&gt;, &lt;b&gt;전면 광고&lt;/b&gt;, &lt;b&gt;보상형 광고&lt;/b&gt;, &lt;b&gt;네이티브 광고&lt;/b&gt;를 구현하여 광고 수익화를 극대화할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주요 광고 유형&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Google Mobile Ads 플러그인을 사용하면 다음과 같은 주요 광고 형식을 앱에 쉽게 추가할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. &lt;b&gt;배너 광고&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;특징&lt;/b&gt;: 화면 하단 또는 상단에 고정되어 지속적으로 노출됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용 예시&lt;/b&gt;: 사용자가 앱을 탐색하는 동안 노출.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. &lt;b&gt;전면 광고 (Interstitial Ads)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;특징&lt;/b&gt;: 앱 전환 시 전체 화면에 표시됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용 예시&lt;/b&gt;: 레벨 완료 후, 페이지 이동 시.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. &lt;b&gt;보상형 광고 (Rewarded Ads)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;특징&lt;/b&gt;: 사용자가 광고를 시청하면 보상을 받습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용 예시&lt;/b&gt;: 게임 아이템, 프리미엄 콘텐츠 해제.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. &lt;b&gt;네이티브 광고&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;특징&lt;/b&gt;: 앱 디자인에 맞춘 맞춤형 광고.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용 예시&lt;/b&gt;: 피드 형식의 앱에서 자연스럽게 광고를 삽입.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Flutter에서 Google Mobile Ads 통합하기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. &lt;b&gt;플러그인 설치&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;pubspec.yaml&lt;/code&gt; 파일에 다음을 추가하여 플러그인을 설치합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;dependencies:
  google_mobile_ads: ^2.0.0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 &lt;code&gt;flutter pub get&lt;/code&gt; 명령어로 의존성을 설치합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. &lt;b&gt;초기화&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱 실행 시 Google Mobile Ads SDK를 초기화해야 합니다. &lt;code&gt;main.dart&lt;/code&gt; 파일의 &lt;code&gt;main()&lt;/code&gt; 함수에서 초기화를 설정합니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;void main() {
  WidgetsFlutterBinding.ensureInitialized();
  MobileAds.instance.initialize();
  runApp(MyApp());
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. &lt;b&gt;광고 구현&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;(1) 배너 광고 추가&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;BannerAd myBanner = BannerAd(
  adUnitId: '광고 단위 ID',
  size: AdSize.banner,
  request: AdRequest(),
  listener: BannerAdListener(),
);

myBanner.load();&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;(2) 전면 광고 추가&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;InterstitialAd.load(
  adUnitId: '광고 단위 ID',
  request: AdRequest(),
  adLoadCallback: InterstitialAdLoadCallback(
    onAdLoaded: (InterstitialAd ad) {
      ad.show();
    },
    onAdFailedToLoad: (LoadAdError error) {
      print('전면 광고 로드 실패: $error');
    },
  ),
);&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;(3) 보상형 광고 추가&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;RewardedAd.load(
  adUnitId: '광고 단위 ID',
  request: AdRequest(),
  rewardedAdLoadCallback: RewardedAdLoadCallback(
    onAdLoaded: (RewardedAd ad) {
      ad.show(
        onUserEarnedReward: (AdWithoutView ad, RewardItem reward) {
          print('보상 획득: ${reward.amount}');
        },
      );
    },
    onAdFailedToLoad: (LoadAdError error) {
      print('보상형 광고 로드 실패: $error');
    },
  ),
);&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;팁과 주의사항&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;광고 단위 ID&lt;/b&gt;:&lt;br /&gt;Google AdMob 계정을 통해 광고 단위 ID를 생성하고 각 광고 유형에 적절히 설정합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용자 경험 고려&lt;/b&gt;:&lt;br /&gt;과도한 광고는 사용자 경험을 해칠 수 있으므로, 적절한 빈도로 광고를 배치하세요.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;테스트 광고 활용&lt;/b&gt;:&lt;br /&gt;앱 개발 중에는 실제 광고 대신 테스트 광고를 사용하여 정책 위반을 방지하세요.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;앱 정책 준수&lt;/b&gt;:&lt;br /&gt;Google의 광고 정책을 준수하여 광고 게재가 중단되는 상황을 방지해야 합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Google Mobile Ads 활용의 장점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;수익화 극대화&lt;/b&gt;: 다양한 광고 형식을 제공하여 수익 창출 가능성을 높임.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;글로벌 네트워크&lt;/b&gt;: Google AdMob의 글로벌 광고 네트워크를 통해 높은 광고 수익률을 기대할 수 있음.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용자 데이터 기반 최적화&lt;/b&gt;: 광고 노출 빈도 및 타겟팅을 자동으로 최적화.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Google Mobile Ads 플러그인은 Flutter 애플리케이션의 수익화를 위한 강력한 도구입니다. 배너, 전면, 보상형, 네이티브 광고를 적절히 활용하면 사용자 경험을 유지하면서도 수익성을 극대화할 수 있습니다. Flutter 앱을 성공적으로 광고 수익화하려면 위의 단계를 참고하여 Google Mobile Ads를 통합해 보세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1733957021702&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import 'dart:io';

import 'package:flutter/widgets.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';

class MyBannerAdWidget extends StatefulWidget {
  /// The requested size of the banner. Defaults to [AdSize.banner].
  final AdSize adSize;

  /// The AdMob ad unit to show.
  ///
  /// TODO: replace this test ad unit with your own ad unit
  final String adUnitId = Platform.isAndroid
      // Use this ad unit on Android...
      ? 'ca-app-pub-3940256099942544/6300978111'
      // ... or this one on iOS.
      : 'ca-app-pub-3940256099942544/2934735716';

  MyBannerAdWidget({
    super.key,
    this.adSize = AdSize.banner,
  });

  @override
  State&amp;lt;MyBannerAdWidget&amp;gt; createState() =&amp;gt; _MyBannerAdWidgetState();
}

class _MyBannerAdWidgetState extends State&amp;lt;MyBannerAdWidget&amp;gt; {
  /// The banner ad to show. This is `null` until the ad is actually loaded.
  BannerAd? _bannerAd;

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: SizedBox(
        width: widget.adSize.width.toDouble(),
        height: widget.adSize.height.toDouble(),
        child: _bannerAd == null
            // Nothing to render yet.
            ? SizedBox()
            // The actual ad.
            : AdWidget(ad: _bannerAd!),
      ),
    );
  }

  @override
  void initState() {
    super.initState();
    _loadAd();
  }

  @override
  void dispose() {
    _bannerAd?.dispose();
    super.dispose();
  }

  /// Loads a banner ad.
  void _loadAd() {
    final bannerAd = BannerAd(
      size: widget.adSize,
      adUnitId: widget.adUnitId,
      request: const AdRequest(),
      listener: BannerAdListener(
        // Called when an ad is successfully received.
        onAdLoaded: (ad) {
          if (!mounted) {
            ad.dispose();
            return;
          }
          setState(() {
            _bannerAd = ad as BannerAd;
          });
        },
        // Called when an ad request failed.
        onAdFailedToLoad: (ad, error) {
          debugPrint('BannerAd failed to load: $error');
          ad.dispose();
        },
      ),
    );

    // Start loading.
    bannerAd.load();
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Flutter Cookbook</category>
      <category>AdMob</category>
      <category>flutter</category>
      <category>flutter광고</category>
      <category>googlemobileads</category>
      <category>광고수익화</category>
      <category>모바일광고</category>
      <category>배너광고</category>
      <category>보상형광고</category>
      <category>앱수익화</category>
      <category>전면광고</category>
      <author>플러터를 배워보자</author>
      <guid isPermaLink="true">https://flutter-explorer.tistory.com/66</guid>
      <comments>https://flutter-explorer.tistory.com/entry/google-mobile-ads-1#entry66comment</comments>
      <pubDate>Thu, 12 Dec 2024 11:00:12 +0900</pubDate>
    </item>
  </channel>
</rss>