비동기프로그래밍,future,async,await

Date:     Updated:

카테고리:

태그:

플러터에서 비동기 프로그래밍은 앱의 응답성을 유지하면서 긴 작업을 수행할 수 있도록 하는 중요한 개념입니다. 비동기 프로그래밍을 통해 네트워크 요청, 파일 입출력, 데이터베이스 쿼리 같은 시간이 걸리는 작업을 처리할 수 있습니다. 아래에서 비동기 프로그래밍의 기본 개념과 플러터에서의 구현 방법에 대해 자세히 설명하겠습니다.

1. 비동기 프로그래밍의 필요성

비동기 프로그래밍은 다음과 같은 이유로 필요합니다:

  • 응답성 유지: 긴 작업을 수행하는 동안 UI가 멈추지 않도록 합니다. 이를 통해 사용자에게 더 나은 경험을 제공합니다.
  • 리소스 효율성: 비동기 작업을 통해 CPU 자원을 효율적으로 사용할 수 있습니다. 다른 작업을 동시에 처리할 수 있습니다.

2. Dart의 비동기 프로그래밍

Dart는 비동기 프로그래밍을 지원하기 위해 FutureStream이라는 두 가지 주요 개념을 제공합니다.

Future

  • Future: 비동기 작업의 결과를 나타내는 객체입니다. 작업이 완료되면 결과값을 반환하거나 오류를 발생시킬 수 있습니다.
  • 작성법: async 키워드로 정의된 함수 내에서 await를 사용하여 Future의 결과를 기다릴 수 있습니다.
Future<String> fetchData() async {
  // 비동기 작업 시뮬레이션
  await Future.delayed(Duration(seconds: 2));
  return '데이터가 성공적으로 가져와졌습니다.';
}

Stream

  • Stream: 연속적인 데이터 흐름을 나타냅니다. 주로 데이터가 여러 번 발생할 때 사용됩니다(예: 웹소켓, 사용자 이벤트).
  • 작성법: Stream을 사용하여 데이터 흐름을 구독할 수 있으며, async* 키워드로 비동기 제너레이터를 정의할 수 있습니다.
Stream<int> countStream() async* {
  for (int i = 1; i <= 5; i++) {
    await Future.delayed(Duration(seconds: 1));
    yield i; // 값을 발생시킴
  }
}

3. 비동기 함수 사용 예제

아래는 플러터에서 비동기 함수를 사용하는 예제입니다. 이 예제는 API로부터 데이터를 비동기적으로 가져오는 방법을 보여줍니다.

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '비동기 프로그래밍 예제',
      home: DataFetchPage(),
    );
  }
}

class DataFetchPage extends StatefulWidget {
  @override
  _DataFetchPageState createState() => _DataFetchPageState();
}

class _DataFetchPageState extends State<DataFetchPage> {
  String _data = '데이터를 가져오는 중입니다...';

  Future<void> _fetchData() async {
    try {
      final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1'));
      if (response.statusCode == 200) {
        final jsonData = json.decode(response.body);
        setState(() {
          _data = jsonData['title'];
        });
      } else {
        setState(() {
          _data = '데이터를 가져오는 데 실패했습니다.';
        });
      }
    } catch (e) {
      setState(() {
        _data = '오류 발생: $e';
      });
    }
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('비동기 데이터 가져오기'),
      ),
      body: Center(
        child: Text(_data, style: TextStyle(fontSize: 20)),
      ),
    );
  }
}

4. 코드 설명

  • Future<void> _fetchData(): 비동기 함수로, 데이터를 가져오는 로직을 포함합니다. await를 사용하여 비동기 요청을 처리하고, 결과에 따라 UI를 업데이트합니다.
  • try-catch 블록: 데이터 요청 중 오류가 발생할 수 있으므로, 오류를 처리하기 위해 try-catch를 사용합니다.
  • initState(): 페이지가 초기화될 때 _fetchData() 함수를 호출하여 데이터를 가져옵니다.

// ———————————————————-

플러터에서의 Future, async, await는 비동기 프로그래밍을 구현하는 데 필수적인 요소입니다. 이들 개념은 Dart 언어에서 제공되며, 비동기 작업을 간편하게 처리할 수 있도록 도와줍니다. 아래에서 각각의 개념을 자세히 설명하겠습니다.

1. Future

Future는 비동기 작업의 결과를 나타내는 객체입니다. 비동기 함수는 시간이 걸리는 작업을 수행할 때, 그 작업이 완료될 때까지 기다리지 않고 다른 작업을 수행할 수 있게 해줍니다.

  • Future의 상태: Future는 세 가지 상태를 가집니다.
    • 대기 중(Pending): 비동기 작업이 진행 중인 상태입니다.
    • 완료됨(Completed): 비동기 작업이 성공적으로 완료된 상태로, 결과값을 가지고 있습니다.
    • 오류(Error): 비동기 작업이 실패한 상태로, 오류 정보를 가지고 있습니다.
  • Future 생성:

    Future<String> fetchData() {
      return Future.delayed(Duration(seconds: 2), () {
        return '데이터가 성공적으로 가져와졌습니다.';
      });
    }
    
  • Future 사용 예:

    void main() async {
      String data = await fetchData(); // Future의 결과를 기다림
      print(data);
    }
    

2. async

async는 함수를 비동기적으로 실행하도록 표시하는 키워드입니다. async가 있는 함수는 항상 Future를 반환합니다. 이 함수 내에서 await 키워드를 사용하여 비동기 작업의 결과를 기다릴 수 있습니다.

  • async 함수 정의:

    Future<void> myAsyncFunction() async {
      // 비동기 작업 수행
      await Future.delayed(Duration(seconds: 1));
      print('비동기 작업 완료');
    }
    
  • async 사용 예:

    void main() async {
      print('작업 시작');
      await myAsyncFunction(); // 비동기 작업을 기다림
      print('작업 종료');
    }
    

3. await

awaitasync 함수 내에서만 사용할 수 있는 키워드입니다. await를 사용하면 Future가 완료될 때까지 함수의 실행을 일시 중지합니다. 이로 인해 비동기 작업의 결과를 직관적으로 얻을 수 있습니다.

  • await 사용 예:

    Future<void> fetchData() async {
      print('데이터를 가져오는 중...');
      String data = await Future.delayed(Duration(seconds: 2), () {
        return '가져온 데이터';
      });
      print(data); // 두 초 후에 실행
    }
    
    void main() {
      fetchData();
    }
    

4. 전체적인 예제

아래는 Future, async, await의 사용을 보여주는 전체 예제입니다. 이 예제에서는 API에서 데이터를 비동기적으로 가져오는 과정을 구현합니다.

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Future, Async, Await 예제',
      home: DataFetchPage(),
    );
  }
}

class DataFetchPage extends StatefulWidget {
  @override
  _DataFetchPageState createState() => _DataFetchPageState();
}

class _DataFetchPageState extends State<DataFetchPage> {
  String _data = '데이터를 가져오는 중입니다...';

  // 비동기 함수: 데이터를 가져오는 함수
  Future<void> _fetchData() async {
    try {
      final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1'));
      if (response.statusCode == 200) {
        final jsonData = json.decode(response.body);
        setState(() {
          _data = jsonData['title']; // API로부터 가져온 데이터의 제목을 표시
        });
      } else {
        setState(() {
          _data = '데이터를 가져오는 데 실패했습니다.';
        });
      }
    } catch (e) {
      setState(() {
        _data = '오류 발생: $e';
      });
    }
  }

  @override
  void initState() {
    super.initState();
    _fetchData(); // 페이지가 초기화될 때 데이터 가져오기
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('비동기 데이터 가져오기'),
      ),
      body: Center(
        child: Text(_data, style: TextStyle(fontSize: 20)),
      ),
    );
  }
}

5. 코드 설명

  1. Future: _fetchData 함수는 Future<void>로 선언되어 있으며, 비동기 작업을 포함합니다.
  2. async: _fetchData 함수는 async로 정의되어 있어 비동기 작업을 수행할 수 있습니다.
  3. await: await를 사용하여 HTTP 요청이 완료될 때까지 기다립니다. 응답이 성공적이면 데이터를 UI에 반영합니다.

사용사례

Future는 비동기 작업을 처리하는 데 매우 유용한 Dart의 기능입니다. 플러터 개발에서 Future의 사용 사례는 다양하며, 아래에 몇 가지 일반적인 사용 사례를 설명하겠습니다.

1. 네트워크 요청

가장 일반적인 사용 사례 중 하나는 API 또는 서버와의 통신입니다. 데이터베이스나 RESTful API에 요청을 보내고 응답을 받을 때 Future를 사용하여 비동기적으로 작업을 수행합니다.

Future<String> fetchPost() async {
  final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1'));
  if (response.statusCode == 200) {
    return response.body; // 성공적인 응답
  } else {
    throw Exception('데이터를 가져오는 데 실패했습니다.');
  }
}

2. 파일 입출력

로컬 파일 시스템에서 파일을 읽거나 쓸 때도 Future를 사용할 수 있습니다. 비동기 파일 입출력을 통해 UI가 멈추지 않도록 할 수 있습니다.

import 'dart:io';

Future<String> readFile(String path) async {
  final file = File(path);
  return await file.readAsString(); // 파일 내용을 비동기적으로 읽기
}

3. 데이터베이스 쿼리

SQLite와 같은 로컬 데이터베이스에서 데이터를 조회할 때도 Future를 사용하여 비동기적으로 쿼리 결과를 처리합니다.

Future<List<User>> fetchUsers() async {
  final db = await openDatabase('my_db.db');
  final List<Map<String, dynamic>> maps = await db.query('users'); // 비동기 쿼리
  return List.generate(maps.length, (i) {
    return User.fromMap(maps[i]); // 결과를 객체로 변환
  });
}

4. 타이머 및 지연 작업

일정 시간 후에 작업을 수행해야 할 때도 Future를 사용할 수 있습니다. 예를 들어, 특정 시간 후에 데이터를 새로 고치는 기능을 구현할 수 있습니다.

Future<void> delayTask() async {
  await Future.delayed(Duration(seconds: 3)); // 3초 대기
  print('작업 수행');
}

5. 애니메이션 및 트랜지션

애니메이션이나 화면 전환 효과를 비동기적으로 처리할 때 Future를 사용할 수 있습니다. 예를 들어, 애니메이션이 완료된 후 다음 작업을 수행할 수 있습니다.

Future<void> animateAndDoSomething() async {
  await animate(); // 애니메이션이 끝날 때까지 대기
  print('애니메이션 완료 후 작업 수행');
}

6. 외부 라이브러리와의 통합

외부 라이브러리나 서비스와 통신할 때도 Future를 사용하여 비동기 작업을 수행할 수 있습니다. 예를 들어, Firebase와 같은 클라우드 서비스에서 데이터를 가져오는 경우입니다.

Future<void> fetchFirebaseData() async {
  final data = await FirebaseFirestore.instance.collection('users').get();
  // 데이터 처리
}

결론

Future는 비동기 처리를 통해 다양한 작업을 수행하는 데 매우 유용합니다. 네트워크 요청, 파일 입출력, 데이터베이스 쿼리, 타이머, 애니메이션, 외부 라이브러리와의 통합 등 다양한 상황에서 활용될 수 있습니다. 이러한 기능을 통해 플러터 애플리케이션의 응답성을 높이고 사용자 경험을 개선할 수 있습니다.

결론

  • Future, async, await는 비동기 프로그래밍을 간편하게 처리할 수 있도록 도와주는 Dart의 주요 개념입니다.
  • 이러한 개념을 활용하면 비동기 작업을 쉽게 구현하고, UI의 응답성을 유지할 수 있습니다.
  • 비동기 프로그래밍은 사용자 경험을 향상시키는 데 중요한 역할을 합니다. 이러한 개념을 잘 활용하여 더 나은 애플리케이션을 개발할 수 있습니다.
  • 비동기 프로그래밍은 플러터 애플리케이션에서 매우 중요한 개념입니다. FutureStream을 사용하여 비동기 작업을 처리하면 앱의 응답성을 높이고,
  • 사용자 경험을 개선할 수 있습니다. 비동기 함수와 적절한 예외 처리를 통해 안정적인 애플리케이션을 개발할 수 있습니다.

Dart 카테고리 내 다른 글 보러가기

댓글 남기기