Dart 언어는 비동기 프로그래밍을 지원하고 이때 주요하게 쓰이는 개념으로 Future, Stream, await, async 등이 있다.
이번 포스팅에서는 Dart 언어에서의 비동기 프로그래밍의 기본 방식과 Future, await, async 키워드에 대해 다뤄 볼 예정이다.
어떤 프로그래밍을 하던 Data Fetching, Writing, Reading 이 존재하는 분야라면 비동기 처리 방식이 중요할 수 밖에 없기 때문에 이에 대해서 정리를 해보고자 한다.
아래의 내용은 Dart 공식 도큐먼트 내용을 기준으로 발췌하고 정리하였다.
Asynchronous Programming: Futures, async, await
- 비동기 작업이 대표적으로 사용되는 부분
- 네트워크에서 데이터를 불러오는 작업
- 데이터베이스에 데이터를 저장하는 작업
- 파일로부터 데이터를 불러오는 작업
- 이러한 비동기 작업의 결과로 단일 결과는 Future, 여러 작업 결과는 Stream을 통해서 받게 된다.
- Dart의 함수에서 이러한 비동기 작업을 수행하기 위해서 aync, await 라는 키워드를 사용한다.
Example: Incorrectly using an asynchronous function
< 코드 >
< 결과 >
< 설명 >
순서를 적어보자면
1. createOrderMessage 함수가 호출됨
2. fetchUserOrder가 호출됨
3. createOrderMessage의 반환 값이 반환 됨
4. main의 print 함수가 호출 됨
5. 2초 후, fetchUserOrder에서 'Large Latte' 값이 반환 됨
이다.
즉, fetchUserOrder의 반환 값이 반환되기 이전에 createOrderMessage의 반환 값이 반환되었기 때문에 올바르지 않은 작동이 일어났다고 할 수 있다.
이를 해결하기 위해서 우선 Future의 기본적인 구조를 먼저 익혀야 한다.
Future
Future는 한국어 어원 그대로 미래라는 뜻을 가지고 있다.
Future뒤에 <T> 라는 템플릿 표시가 붙는데 이는 미래에 전달받을 반환 값의 타입을 명시해주는 부분이다.
즉, Future<String> 은 미래에 받아올 정보가 String이라는 것을 명시하는 것이다.
다음으로 Future의 반환 값은 두 가지로 분류할 수 있다.
1. Uncompleted
: 비동기 함수에서 반환 값을 기다리는 상태의 Future 값을 의미한다. 즉, 비동기 함수가 값을 반환하기 이전에 Future<T>값을 반환받았음을 의미한다.
2. Completed
: 해당 비동기 함수에서 Future 반환 값을 받았거나, Error를 throw한 경우를 의미한다.
즉, 위의 첫 예시에서 createOrderMessage 함수가 fetchUserOrder를 호출했을 때 얻은 Future 반환 값은 Uncompleted value이다.
개발자가 원하는 것은 Completed Value이기 때문에 Data Fetching이 완료된 이후 시점을 정확히 정의하기 위해서는 async, await 키워드의 개념을 숙지해야한다.
Async, Await
Async라는 용어는 Asynchronous의 약자로 비동기임을 의미한다.
Await라는 용어는 Asynchronous한 작업을 기다리라는 의미이다.
즉, async 키워드는 function의 body 앞에 붙이면 된다는 것만 기억하면 된다.
또한, await 키워드는 async 키워드가 붙은 비동기 함수의 Future 반환 값을 기다릴 때, 호출 함수 앞에 붙이면 됨을 기억하면 된다.
위의 두 가지만 잘 기억하면 아주 간단하게 첫 번째 예시의 오류를 고칠 수 있다.
< 코드 >
< 결과 >
< 설명 >
이해를 돕기 위해 기존 코드에서 어떤 순서로 코드를 변경했는지 설명을 하는게 좋을 것 같다.
1. 4번 줄에서 fetchUserOrder를 호출 할 때, await 키워드를 붙였다.
2. await 키워드를 붙인 순간 createOrderMessage는 언제 동작이 끝날지 모르는 비동기 함수가 되었기 때문에 body 앞에 async 키워드를 붙였다.
3. 비동기 함수가 되었기 때문에 반환 값도 비동기적으로 반환할 것이기 때문에 불분명한 미래를 의미하는 Future<String> 반환 값으로 바꿔주었다.
4. 마지막으로 main 함수에서 createOrderMessage의 반환 값이 비동기적이기 때문에 await 키워드를 붙여주었다.
5. 그렇다면 main 함수도 언제 끝날지 모르는 비동기 함수가 되었기 때문에 body 앞에 async 키워드를 붙여주었다.
6. main은 반환 값이 없기 때문에 void에서 수정하지 않았다.
이런 코드 수정 흐름을 이해한다면, 비동기적인 작업(Data Fetching, Writing, Reading)등을 진행할 때 Completed Future Value를 받아볼 수 있을 것이다.
Error handling
마지막으로 비동기 작업은 늘 다양한 이유로 Error가 발생하기 때문에 비동기 작업의 Error Handling 방법에 대해 작성해보겠다.
예를 들어 위의 예시에서 주문 받는 와중에 fetchUserOrder에서 에러가 발생했다고 한다면 createOrderMessage 함수 내에서 에러를 return 하거나 print하거나 block하는 작동을 정의해줘야한다.
Error handling의 방식은 다른 언어들과 동일하게 try-catch syntax를 사용한다.
위의 예시 코드를 이용해서 Error code 예시를 작성해보겠다.
< 코드 >
< 결과 >
< 설명 >
기존의 코드를 약간 변형하여 fetchUserOrder 함수가 호출되면 2초 후에 Error를 throw 하도록 하였고, 해당 비동기 함수의 값을 기다리는 부분들에 try-catch syntax를 적용하여 Error를 처리하였다.
물론 Error 처리 방식은 아주 다양할 수 있지만!
Error가 throw될 가능성이 있는 비동기 함수에 await을 붙여서 사용하였다면 try-catch syntax를 활용하여 Error handling을 진행해야 함을 잊지 않아야 한다.
Dart 공식 문서를 통해 "비동기, Future, await, async, Error Handling"에 대해 간단하게 알아보았다.
모든 언어에서 Data fetching, reading, writing을 고려하지 않는 언어가 없다고 해도 무방할 만큼 매우매우 중요한 부분이기 때문에 관련 내용을 우선 살펴 보았다.
다음에는 비동기 함수에서 (Future로 할 수 없는) 하나의 반환 값이 아닌 여러 반환 값을 전달할 수 있는 Stream 키워드에 대해서도 공부해보고 글을 작성해봐야겠다.
'Flutter > Basic Knowledge' 카테고리의 다른 글
[Dart] Stream 이란? (2) | 2024.01.22 |
---|---|
[Dart] 단일 상속과 Mixin (0) | 2024.01.10 |
[Dart] AOT(Ahead-Of-Time) 과 JIT(Just-In-Time) 컴파일러 (0) | 2024.01.10 |
[Dart] Garbage Collection 이란? (1) | 2024.01.10 |
[Dart] Dart 란? (0) | 2024.01.10 |