플러터 강좌: 14.2 비동기 방식

작성자: 조광형

작성일: [오늘 날짜]

비동기 프로그래밍의 중요성

모던 애플리케이션 개발에서 비동기 프로그래밍은 필수적인 요소입니다. 특히, 사용자 경험(UX)을 향상시키고 응답성을 높이는 데 있어서 비동기 작업은 매우 중요합니다. 비동기 방식은 여러 작업이 동시에 진행될 수 있도록 하여, 한 작업이 완료되기를 기다리는 동안 다른 작업을 수행할 수 있게 합니다. 이를 통해 사용자는 더 빠르고 원활한 애플리케이션을 사용할 수 있습니다.

플러터에서의 비동기 프로그래밍

플러터에서 비동기 프로그래밍은 주로 Future와 async/await 키워드를 통해 구현됩니다. Flutter는 Dart 프로그래밍 언어로 작성되며, 비동기 프로그래밍을 쉽게 할 수 있도록 다양한 기능을 지원합니다. 이제 플러터에서 비동기 함수를 사용하는 방법을 살펴보겠습니다.

Future와 비동기 함수

Future는 비동기 작업의 결과를 나타내는 클래스입니다. Future 객체는 향후 완료될 작업을 나타내며, 이 객체는 비동기 함수에서 반환됩니다. 비동기 함수는 async 키워드로 정의되고, 내부에서 await 키워드를 사용하여 Future가 완료될 때까지 기다립니다.

비동기 함수 예제


Future fetchData() async {
    await Future.delayed(Duration(seconds: 2));
    return "데이터 로드 완료!";
}
            

위의 예제는 2초 동안 대기한 후 “데이터 로드 완료!”라는 문자열을 반환하는 비동기 함수입니다.

await의 사용

await 키워드는 Future가 완료될 때까지 기다리게 하여, 비동기 코드의 순차적인 실행을 가능하게 합니다. 예를 들어, 위의 fetchData 함수를 호출하는 방법은 다음과 같습니다.

await 예제


void main() async {
    print("데이터를 불러오는 중...");
    String result = await fetchData();
    print(result);
}
            

main 함수 또한 async로 정의하였으며, fetchData 함수의 결과를 await를 사용해 기다립니다. 이를 통해 “데이터를 불러오는 중…” 메시지는 즉시 출력되고, 2초 후에 “데이터 로드 완료!”가 출력됩니다.

비동기 작업의 에러 처리

비동기 작업에서 에러 처리는 중요한 부분입니다. try-catch 블록을 사용하여 비동기 함수에서 발생한 예외를 처리할 수 있습니다.

에러 처리 예제


Future fetchWithError() async {
    throw Exception("에러 발생!");
}

void main() async {
    try {
        String result = await fetchWithError();
        print(result);
    } catch (e) {
        print("에러: ${e.toString()}");
    }
}
            

위의 예제에서 fetchWithError 함수는 예외를 던집니다. main 함수에서는 해당 함수의 호출을 try-catch 블록으로 감싸 에러를 처리합니다.

Future의 여러 관리 방법

플러터에서 비동기 처리를 보다 효과적으로 관리하기 위해, 여러 Future를 동시에 실행할 수 있는 방법이 제공됩니다. 여기에 Future.wait 메소드를 사용하면 여러 Future를 동시에 기다리는 것이 가능합니다.

Future.wait 예제


Future fetchAllData() async {
    var first = fetchData();
    var second = fetchWithError();
    var results = await Future.wait([first, second]);

    print(results);
}

void main() async {
    try {
        await fetchAllData();
    } catch (e) {
        print("에러: ${e.toString()}");
    }
}
            

fetchAllData 함수는 두 개의 Future를 동시에 실행하고, 결과를 기다립니다. 두 번째 Future가 에러를 발생시키면 catch 블록에서 에러를 처리합니다.

스트림(Stream)의 활용

스트림은 비동기 데이터의 연속적인 흐름을 처리하기 위한 것입니다. 데이터가 지속적으로 발생하는 경우에 유용합니다. 예를 들어, 웹소켓 연결, 파일 읽기, 데이터베이스 쿼리 등이 있습니다. 스트림은 일련의 이벤트를 비동기적으로 처리합니다.

스트림 예제


Stream numberStream() async* {
    for (int i = 0; i < 5; i++) {
        await Future.delayed(Duration(seconds: 1));
        yield i;
    }
}

void main() async {
    await for (var number in numberStream()) {
        print(number);
    }
}
            

numberStream 함수는 스트림을 생성하고, 1초마다 숫자를 출력합니다. main 함수에서는 await for 키워드를 사용하여 스트림의 데이터에 접근합니다.

비동기 프로그래밍의 모범 사례

비동기 프로그래밍을 할 때 몇 가지 모범 사례를 따르는 것이 중요합니다:

  • 에러 처리: 비동기 함수에서 예외 처리를 잊지 마세요.
  • Future 조합: Future.wait, Future.any 등을 활용해 여러 작업을 효율적으로 처리하세요.
  • 스트림 사용: 연속적인 데이터 처리가 필요할 때는 스트림을 고려하세요.

결론

플러터에서의 비동기 프로그래밍은 사용자 경험을 향상시키고, 동시 작업 처리를 가능하게 합니다. Future, async/await, 그리고 스트림을 포함한 다양한 비동기 처리 기법을 배워 활용하면, 복잡한 애플리케이션도 보다 쉽게 관리할 수 있습니다. 이 강좌를 통해 비동기 프로그래밍의 기초를 다지고, 실제 애플리케이션에 적용해 보길 바랍니다!