인공지능 개발자 수다(유튜브 바로가기) 자세히보기

flutter

[flutter] dart 언어 - 비동기 프로그래밍 (5)

Suda_777 2025. 2. 23. 04:25

목차

    반응형

     

    0. 개요

    • 네트워크 요청, 파일 읽기, 데이터베이스 쿼리 같은 작업은 비동기적으로 실행하는 것이 더 효율적
    • 시간이 걸리는 작업을 기다리는 동안 다른 작업을 수행할 수 있음
    • Future, async, await을 사용하여 비동기 코드를 작성
    • stream 으로 연속된 값을 가져올 수 있음
    • completer 를 이용해 Future를 직접 제어

     


    1. Future - 비동기 작업

    Dart에서 비동기 작업을 처리할 때 Future 객체를 사용한다

     

    먼저, 일반적인 동기 함수 코드를 보고 비교해 보자

    void longTask() {
      print("작업 시작...");
      sleep(Duration(seconds: 3)); // 3초 동안 멈춤
      print("작업 완료!");
    }
    
    void main() {
      print("A");
      longTask();
      print("B");
    }

     

    실행 결과

    A
    작업 시작...
    (3초 후)
    작업 완료!
    B

     

     

    비동기 함수 예시

    • Future<자료형> 함수이름() async {코드}
    • await를 사용하면 비동기 작업이 끝날 때까지 기다린 후 실행됨
    Future<String> fetchData() async {
      await Future.delayed(Duration(seconds: 2));
      return "데이터 로드 완료";
    }
    
    void main() async {
      print("시작");
    
      String data = await fetchData();
      print(data); // 2초 후 실행됨
    
      print("끝");
    }

     

    실행 결과

    A
    B
    작업 시작...
    (3초 후)
    작업 완료!

     

    예외처리 적용

    Future<String> fetchData() async {
      await Future.delayed(Duration(seconds: 2));
      throw Exception("데이터 로드 실패!");
    }
    
    void main() async {
      try {
        String data = await fetchData();
        print("데이터: $data");
      } catch (e) {
        print("에러 발생: $e");
      }
    }

    2. Stream

    Dart의 Future는 한 번만 값을 반환하지만,
    Stream은 여러 개의 데이터를 지속적으로 반환할 때 사용

     

    2.1. 기본 사용법

    함수 선언

    • Stream<자료형> 함수이름() async* {코드내용}
    • await 을 이용해 기다림
    • return 대신 yield 사용

     

    비동기 함수 사용

    • stream 객체 생성
    • await for (자료형 변수 in stream객체) {반복코드}
    Stream<int> numberStream() async* {
      for (int i = 1; i <= 5; i++) {
        await Future.delayed(Duration(seconds: 1));
        yield i;
      }
    }
    
    void main() async {
      Stream<int> stream = numberStream();
    
      await for (int num in stream) {
        print("받은 데이터: $num");
      }
    
      print("스트림 종료");
    }

     


    2.2. Stream을 listen()으로 사용

    listen(함수)을 사용하면 데이터를 계속 수신, () 안에 실행할 함수를 넣어줄 수 있음

    data는 Stream에서 yield로 반환된 값이 자동으로 전달

    onDone을 사용하면 스트림 종료 시 실행할 코드 작성 가능

    void main() {
      stream.listen(
        (int data) => print("받은 데이터: $data"),
        onDone: () => print("스트림 완료"),
      );
    }

     

    where() 를 이용한 필터링

    void main() {
      Stream<int> stream = Stream.fromIterable([1, 2, 3, 4, 5, 6, 7]);
    
      stream.where((int num) => num % 2 == 0).listen(
        (int data) => print("짝수 데이터: $data"),
      );
    }

     


    2.3. 첫번째 값만 가져오기

    stream.first 를 이용해 첫번째 값만 가져올 수 있음

    void main() async {
      Stream<int> stream = Stream.fromIterable([10, 20, 30]);
    
      int firstValue = await stream.first;
      print("첫 번째 값: $firstValue");
    }

     

     


    2.4. 예외처리

    onError : 함수 를 사용해 예외처리

    error 변수에는 예외(Exception) 객체가 들어옴

    Stream<int> errorStream() async* {
      yield 1;
      yield 2;
      throw Exception("에러 발생!");
      yield 3;
    }
    
    void main() {
      errorStream().listen(
        (data) => print("받은 데이터: $data"),
        onError: (error) => print("오류 발생: $error"),
        onDone: () => print("스트림 종료"),
      );
    }

     


    3. Completer

    3.1. 설명

    함수가 아래 코드를 return 해주는 함수를 만든다고 하면

    return completer.future;

     

    아래 코드가 실행된 상태가 될때까지 기다린다.

    completer.complete()

     

    예를 들어 버튼을 클릭할 때 까지 기다린다고 할때

    유용하게 사용할 수 있겠다.

     


    3.2. Completer 를 사용하는 함수 정의

    진행 순서

     

    1. Future를 반환하는 함수를 async로 선언

     

    2.  Completer를 선언한다.

    Completer<자료형> completer = Completer<자료형>();

     

    2. completer 완료

    completer.complete() 

     

    3. 예외 발생

    completer.completeError() 

     

    4. Future 반환

    return completer.future

     

    import 'dart:async';
    import 'dart:convert';
    import 'package:http/http.dart' as http;
    
    Future<Map<String, dynamic>> fetchUserData() async {
      Completer<Map<String, dynamic>> completer = Completer<Map<String, dynamic>>();
    
      try {
        print("서버에서 사용자 데이터 가져오는 중...");
    
        // 실제 API 요청 (GET 요청)
        var response = await http.get(Uri.parse("https://jsonplaceholder.typicode.com/users/1"));
    
        if (response.statusCode == 200) {
          Map<String, dynamic> userData = jsonDecode(response.body);
          completer.complete(userData); // Future 완료 (성공)
        } else {
          completer.completeError("서버 오류: ${response.statusCode}");
        }
      } catch (error) {
        completer.completeError("네트워크 오류 발생: $error"); // Future 실패 (네트워크 오류)
      }
    
      return completer.future;
    }

     

    함수 실행

    await 로 실행하면 됨

    void main() async {
      print("사용자 데이터 요청 중...");
    
      try {
        Map<String, dynamic> user = await fetchUserData();
        print("사용자 데이터: $user");
      } catch (error) {
        print("에러 발생: $error");
      }
    
      print("작업 완료!");
    }
    반응형