[다트/플러터] 비동기 프로그래밍

다트 언어에서는 asyncawait 키워드를 사용하여 비동기 프로그래밍을 구현할 수 있다. 비동기 방식으로 작업을 수행하는 이유는 하나의 작업이 수행되고 완료되기를 기다리는 동안 다른 작업을 동시에 수행하여 리소스를 효율적으로 사용하고 일련의 작업들을 빠르게 처리하기 위함이다. 일반적으로 비동기로 수행하는 작업은 다음과 같다.

  • 네트워크를 통한 데이터 송신/수신
  • 데이터베이스 읽기/쓰기
  • 파일에서 데이터 읽기/쓰기


이러한 비동기 작업의 결과는 단순한 데이터 타입이 아닌 특별한 타입으로 제공된다. 비동기 작업의 결과가 단일로 간주되는 경우 결과는 Future 객체로 제공되며, 결과가 여러 부분으로 분리되는 경우 Stream 객체로 제공된다. 비동기 작업을 수행하는 함수를 단순히 호출하고 결과를 받아 처리하게 되면 작업이 완료될 때까지 기다리지 않는 방식으로 동작하므로, 완료된 작업의 결과 대신 완료되지 않은 결과나 초기값을 받게 된다. 따라서 비동기 작업을 의도된 방식으로 올바르게 처리하기 위해서는 asyncawait 키워드를 사용한 비동기 함수 호출 및 결과 처리가 필요하다.

비동기 작업의 결과를 표현하기 위한 객체인 Future는 미완료(uncompleted) 또는 완료(completed) 두 가지 상태를 가질 수 있다. 비동기 함수를 단순히 호출하는 경우 완료되지 않은 Future가 반환된다. 해당 Future는 함수의 비동기 작업이 완료되거나 오류가 발생하기를 기다리고 있는 미완료 상태이다. 비동기 함수를 호출한 후 작업이 완료될 때까지 기다리는 경우 비동기 작업이 성공하면 Future는 작업의 결과값을 가지며 완료 상태가 된다. 작업이 결과를 반환하지 않는다면 빈 값을 갖는다. 비동기 작업이 도중에 실패하게 되면 Future는 작업의 결과값 대신 에러와 관련된 값을 가지지만 완료 상태가 된다.

asyncawait 키워드는 비동기 함수를 정의하고 해당 작업의 결과를 사용하는 선언적 방법을 제공한다. asyncawait를 사용할 때 다음 두 가지 기본 가이드를 따르면 된다.

  • 비동기 함수를 정의하려면 함수 본문 앞에 async 키워드를 추가한다.
  • await 키워드는 비동기 함수 내에서만 동작한다.


반환 타입이 Future인, async로 정의한 비동기 함수를 await 키워드 없이 호출 시 반환되는 Future는 함수의 비동기 작업이 완료되거나 오류가 발생하기를 기다리고 있는 미완료 상태이다. 반면 await 키워드를 사용하여 호출 시 반환되는 Future는 작업의 결과나 작업 실행 도중 발생한 에러를 값으로 가지며 완료 상태이다. async 비동기 함수를 await를 사용하여 호출할지 말지는 선택적이다. await 키워드를 사용하여 비동기 작업의 완성된 결과를 얻을 수 있다. await 키워드는 async 비동기 함수 내에서만 동작하기 때문에 async 비동기 함수를 호출하고 await로 완료된 상태의 Future를 반환 받는 함수 또한 async 키워드를 사용하여 비동기 함수로 정의가 필요하다.

비동기 작업을 올바르게 호출하고 그 결과를 받아 처리할 수 있도록 구현을 하게 되면, 비동기 함수를 호출하는 일반 함수(동기 함수)도 비동기 작업이 완료될 때까지 기다린 후 결과를 받아 처리할 것이므로 일정 시간 대기가 필요한 함수가 된다. 따라서 이 함수도 비동기적으로 호출될 수 있도록 변경되어야 하며, 결국 함수 호출이 시작되는 최상위 레벨의 함수에 이르기까지 함수 정의 방식 및 함수 호출 방식의 변경이 필요하게 된다.

데이터를 조회하여 위젯의 내용을 구성하는 경우 데이터 조회 작업이 비동기적으로 수행된다면 조회가 완료된 시점에 결과값을 사용하여 위젯을 빌드(또는 재빌드)해야 한다. 이 경우 플래그 변수나 특정 상태값을 갖는 변수를 사용하여 비동기 데이터 조회와 UI 업데이트 처리를 하는 대신 Future의 최신 스냅샷을 기반으로 위젯을 빌드하는 역할을 하는 FutureBuilder를 사용할 수 있다. 이외에 비동기적으로 UI를 처리하는 방법으로는 provider 패키지가 제공하는 콜백을 통한 앱 상태 관리, BLoc 디자인 패턴을 사용한 앱 상태 관리 등이 있다.

FutureBuilder를 사용하여 비동기로 조회하는 데이터의 존재 여부, 정상 조회 여부에 따라 동일한 타입이지만 프로퍼티가 서로 다른, 상태가 있는(stateful) 위젯을 다르게 빌드할 수 있다. 이 때 서로 다른 프로퍼티를 가지는 위젯들을 서로 다르게 식별하기 위해 키(key)를 사용해야 한다. 키를 사용하지 않을 경우 동일한 타입의 상태 기반 위젯이 조건 분기문에 따라 다른 상태로 빌드되면, 플러터는 위젯의 상태를 올바르게 추적하지 못할 수 있다. 플러터에서는 상태 기반 위젯의 상태가 변경될 때마다 위젯 트리가 재구성된다. 키는 위젯의 고유성을 식별하는데 사용되며, 플러터는 키를 사용하여 위젯의 상태를 올바르게 관리할 수 있다. 키가 없는 경우, 플러터는 위젯의 타입과 위치만으로 위젯을 식별하므로 동일한 타입의 위젯이 다른 위치에 빌드되면 상태 관리에 문제가 발생할 수 있다.


참고

Comments