[Dart 언어강좌] 013. Async Await와 비동기 프로그래밍, Future와 Stream의 개념

현대 소프트웨어 개발에서 비동기 프로그래밍은 필수적인 개념입니다. 비동기 프로그래밍을 통해 개발자는 더 효율적이고 반응성이 뛰어난 애플리케이션을 구축할 수 있습니다. Dart 언어는 비동기 프로그래밍을 지원하기 위해 Async/Await 구문, Future, 그리고 Stream을 제공합니다. 이 글에서는 이들 개념을 자세히 설명하고, 각각의 사용 예시를 제공합니다.

1. 비동기 프로그래밍이란?

비동기 프로그래밍은 특정 작업이 완료될 때까지 다른 작업을 기다리지 않고 진행할 수 있는 프로그래밍 방식입니다. 이는 주요 스레드의 블로킹을 방지하여 애플리케이션의 효율성을 향상시킵니다. 예를 들어, 네트워크 요청을 보내고 그 응답을 기다리는 동안 애플리케이션이 사용자 인터페이스를 차단하지 않도록 할 수 있습니다.

2. Future와 비동기 프로그래밍

Dart에서 비동기 작업의 결과를 나타내는 객체가 Future입니다. Future는 시간이 지남에 따라 완료될 수 있는 값을 나타내며, 이 값이 준비되면 Future는 완료 상태가 됩니다. Future를 사용하면 다음과 같이 비동기적으로 작업을 처리할 수 있습니다.

2.1. Future 생성하기

Future는 다양한 방법으로 생성될 수 있습니다. 가장 일반적인 방법 중 하나는 Future.value() 메서드를 사용하는 것입니다.

dart
Future fetchData() async {
    return 42; // 이 값은 Future가 완료된 후 반환됩니다.
}

2.2. 비동기 함수 정의하기

Dart에서는 async 키워드를 사용하여 비동기 함수를 정의할 수 있습니다. 이 함수 내에서 await 키워드를 사용하여 Future의 결과를 기다릴 수 있습니다.

dart
Future main() async {
    int data = await fetchData();
    print('Fetched data: $data');
}

위 코드에서 fetchData 함수는 Future를 반환하며, main 함수에서는 await을 사용하여 이 값을 기다립니다. 결과적으로 프로그램이 데이터를 가져오는 동안 다른 작업을 차단하지 않습니다.

3. Async/Await의 개념

Dart에서 비동기 프로그래밍을 쉽게 구현할 수 있도록 돕는 async/await 구문이 있습니다. 이 구문을 사용하여 비동기 함수 내에서 코드를 작성할 수 있으며, 마치 동기 코드처럼 가독성이 높아집니다.

3.1. Async 함수 정의하기

async 키워드는 비동기 함수를 정의하는 데 사용됩니다. 비동기 함수 안에서는 await 키워드를 사용하여 Future의 완료를 기다릴 수 있습니다. 예를 들어:

dart
Future fetchUsername() async {
    await Future.delayed(Duration(seconds: 2)); // 2초 지연
    return 'JohnDoe'; // 이름 반환
}

3.2. Async 함수 사용하기

위의 fetchUsername 함수를 사용할 때, 다음과 같이 비동기에 값을 가져올 수 있습니다.

dart
Future main() async {
    String username = await fetchUsername();
    print('Fetched Username: $username');
}

실행 시 2초 후에 ‘Fetched Username: JohnDoe’라는 메시지가 출력됩니다. 이처럼 asyncawait를 활용하면 비동기 프로그램을 간편하게 작성할 수 있습니다.

4. Stream과 비동기 데이터 처리

Stream은 여러 값의 시퀀스를 비동기적으로 전달하는 데이터 구조입니다. Stream은 Future와 유사하지만, 복수의 데이터를 순차적으로 받을 수 있다는 점에서 차별화됩니다.

4.1. Stream 생성하기

Stream을 생성하는 방법은 여러 가지가 있습니다. 가장 간단한 방법 중 하나는 Stream.periodic() 메서드를 사용하여 주기적인 이벤트를 발생시키는 것입니다.

dart
Stream countStream() async* {
    for (int i = 0; i < 5; i++) {
        await Future.delayed(Duration(seconds: 1));
        yield i; // 값 방출
    }
}

4.2. Stream 사용하기

Stream으로부터 데이터를 사용하려면 await for 구문을 사용할 수 있습니다.

dart
Future main() async {
    await for (int value in countStream()) {
        print('Count: $value');
    }
}

이 코드를 실행하면 매초 0부터 4까지의 값이 출력됩니다. Stream을 통해 지속적으로 변화하는 데이터를 효과적으로 처리할 수 있습니다.

5. Future와 Stream의 비교

Future와 Stream은 비동기 작업을 다루는 두 가지 주요 방법이지만, 사용 사례가 다릅니다. Future는 단일 비동기 이벤트를 처리하는 데 적합하며, Stream은 지속적인 데이터 흐름을 관리하는 데 유용합니다.

5.1. Future

  • 단일 값을 반환합니다.
  • 오직 한 번 완료될 수 있습니다.
  • 코드 가독성이 우수합니다.

5.2. Stream

  • 여러 값을 비동기적으로 생성합니다.
  • 값을 여러 번 방출할 수 있습니다.
  • 데이터 흐름을 쉽게 관리할 수 있습니다.

결론

Dart에서 비동기 프로그래밍을 통해 개발자는 효율적인 애플리케이션을 구축할 수 있습니다. async/await, Future 및 Stream을 활용하면 비동기 작업을 간편하게 구현할 수 있고, 복잡한 비동기 흐름을 쉽게 관리할 수 있습니다. 이 글에서 다룬 내용을 바탕으로 Dart의 비동기 프로그래밍을 실습하며 더 나은 기술력을 키우시길 바랍니다.