다트 객체 지향 프로그래밍에 관하여 궁금하시다면 이전글을 참조해 주세요.😄
2023.02.22 - [독서/IT・컴퓨터] - [코드팩도리의 플러터 프로그래밍] 2장. 다트 객체지향 프로그래밍
[코드팩도리의 플러터 프로그래밍] 2장. 다트 객체지향 프로그래밍
다트 기초 문법은 이전글에서 보실 수 있어요.😆 2023.02.20 - [독서/IT・컴퓨터] - [코드팩토리의 플러터 프로그래밍] 1장. 다트(dart) 입문하기 [코드팩토리의 플러터 프로그래밍] 1장. 다트(dart) 입문
zzingonglog.tistory.com
다트 언어는 동기/비동기 프로그래밍을 지원합니다.
동기는 요청하고 나서 응답이 올 때까지 코드를 더 이상 진행하지 않고 응답을 받을 때까지 기다리고 받고 나서야 다음 코드로 진행을 하는데요.
반면에 비동기는 요청하고 나면 응답을 받지 않았어도 대기하지 않고 다음 코드를 진행합니다.
다트 언어에서 동기/비동기 프로그래밍을 하는 방법에 대해 정리해보겠습니다.
1. 동기 vs. 비동기 프로그래밍
지금까지 다트 언어를 배우면서 작성한 코드는 동기 방식이었습니다.
비동기 프로그래밍은 요청한 결과를 기다리지 않으며 응답 순서 또한 요청한 순서와 다를 수 있습니다.
비동기 방식의 장점은 컴퓨터 자원을 낭비하지 않고 효율적으로 작업을 수행한다는 점입니다.
2. Future
Future 클래스는 미래에 받아올 값이라는 의미의 클래스입니다.
미래에 받을 값을 제네릭을 이용하여 다양한 타입으로 받을 수 있는데요.
비동기 프로그래밍은 오래 걸리는 작업을 기다린 후에 값을 받아와야 하기 때문에 미래값을 표현하는 Future 클래스가 필요합니다.
Future<String> name; //미래에 받을 String 값
Future<int> number; //미래에 받을 int값
Future<bool> isOpend; //미래에 받을 boolean값
아래 예제 코드에서 특정 기간 동안 아무것도 하지 않고 기다리는 Future.delayed()를 사용했는데요.
첫 번째 매개 변수에 대기할 기간을 입력, 두 번째 매개변수에 대기 후 실행할 콜백 함수를 입력하면 됩니다.
결과를 보면 1 + 1 = 2는 가장 마지막에 출력이 됩니다.
Future.delayed()는 비동기 함수이기 때문에 3초 동안 기다려야 하는 메시지를 받으면 리소스를 허비하지 않고 다음 코드를 바로 실행하기 때문입니다.
[코드]
void main() {
addNumber(1, 1);
}
void addNumber(int n1, int n2) {
print('$n1 + $n2 계산 시작!');
//Future.delayed()를 사용하면 일정 시간 후에 콜백 함수를 실행할 수 있습니다.
Future.delayed(Duration(seconds : 3), (){
print('$n1 + $n2 = ${n1 + n2}');
});
print('$n1 + $n2 코드 실행 끝!');
}
[결과]
1 + 1 계산 시작!
1 + 1 코드 실행 끝!
1 + 1 = 2 //3초 후 출력
3. async와 await
비동기 함수를 이용해서 코드를 작성하다 보면 코드 작성순서와 실제 실행 순서가 다르기 때문에 헷갈릴 수가 있습니다.
그럴 때 async와 await 키워드를 사용하면 비동기 프로그래밍을 유지하면서 코드 가독성까지 높일 수 있습니다.
[코드]
void main() {
addNumber(1, 1);
}
//async 키워드는 함수 매개변수 정의와 바디 사이에 입력합니다.
Future<void> addNumber(int n1, int n2) async {
print('$n1 + $n2 계산 시작!');
//await는 대기하고 싶은 비동기 함수 앞에 입력합니다.
await Future.delayed(Duration(seconds : 3), (){
print('$n1 + $n2 = ${n1 + n2}');
});
print('$n1 + $n2 코드 실행 끝!');
}
[결과]
1 + 1 계산 시작!
1 + 1 = 2
1 + 1 코드 실행 끝!
결과를 보면 코드가 작성된 순서대로 출력이 된 것을 확인할 수 있습니다.
async와 await키워드를 사용하면 비동기 프로그래밍 특징을 그대로 유지하면서 코드가 작성된 순서대로 프로그램을 실행합니다.
[코드]
void main() {
addNumber(1, 1);
addNumber(2, 2);
}
Future<void> addNumber(int n1, int n2) async {
print('$n1 + $n2 계산 시작!');
await Future.delayed(Duration(seconds : 3), (){
print('$n1 + $n2 = ${n1 + n2}');
});
print('$n1 + $n2 코드 실행 끝!');
}
[결과]
1 + 1 계산 시작!
2 + 2 계산 시작!
1 + 1 = 2
1 + 1 코드 실행 끝!
2 + 2 = 4
2 + 2 코드 실행 끝!
코드를 직접 실행해 보면 결과에서 1, 2라인은 바로 출력이 되고 3초가 지난 후에 아래 4줄이 전부 출력됩니다.
이유는 addNumber 함수 자체도 비동기 프로그래밍으로 실행되었기 때문에 addNumber(1, 1)을 호출하고 3초를 기다리는 동안 CPU가 리소스를 낭비하지 않고 바로 그다음줄인 addNumber(2, 2)를 실행했기 때문입니다.
여기서 addNumber(1, 1)과 addNumber(2, 2)도 순차적으로 실행되기 원한다면 async와 await 키워드를 아래와 같이 추가합니다.
[코드]
void main() async {
await addNumber(1, 1);
await addNumber(2, 2);
}
Future<void> addNumber(int n1, int n2) async {
print('$n1 + $n2 계산 시작!');
await Future.delayed(Duration(seconds : 3), (){
print('$n1 + $n2 = ${n1 + n2}');
});
print('$n1 + $n2 코드 실행 끝!');
}
[결과]
1 + 1 계산 시작!
1 + 1 = 2
1 + 1 코드 실행 끝!
2 + 2 계산 시작!
2 + 2 = 4
2 + 2 코드 실행 끝!
마지막으로 Future 클래스로 결괏값을 받아봅시다.
[코드]
void main() async {
final result = await addNumber(1, 1);
print('결괏값 $result');
final result2 = await addNumber(2, 2);
print('결괏값 $result2');
}
Future<int> addNumber(int n1, int n2) async {
print('$n1 + $n2 계산 시작!');
await Future.delayed(Duration(seconds : 3), (){
print('$n1 + $n2 = ${n1 + n2}');
});
print('$n1 + $n2 코드 실행 끝!');
return n1 + n2;
}
[결과]
1 + 1 계산 시작!
1 + 1 = 2
1 + 1 코드 실행 끝!
결괏값 2
2 + 2 계산 시작!
2 + 2 = 4
2 + 2 코드 실행 끝!
결괏값 4
4. Stream
Future는 반환값을 딱 한 번 받아내는 비동기 프로그래밍에 사용합니다.
지속적으로 값을 반환받을 때는 Stream을 사용합니다.
Stream은 한 번 리슨 하면 Stream에 주입되는 모든 값들을 지속적으로 받아옵니다.
1) Stream 기본 사용법
Stream을 사용하려면 dart:async 패키지를 불러와야 합니다. 그리고 dart:async 패키지에서 제공하는 StreamController를 선언하고 listen() 해야 값을 지속적으로 반환 받을 수 있습니다.
[코드]
import 'dart:async'; //패키지 불러오기
void main() {
final controller = StreamController();
final stream = controller.stream; //Stream 가져오기
//Stream의 listen()을 실행하면 값이 주입될 때마다 콜백 함수를 실행합니다.
final streamListener1 = stream.listen((val) {
print(val);
});
//Stream에 값을 주입하기
controller.sink.add(1);
controller.sink.add(2);
controller.sink.add(3);
controller.sink.add(4);
}
[결과]
1
2
3
4
2) 브로드캐스트(Broadcast) 스트림
브로드캐스트는 하나의 스트림에서 여러 번 listen() 할 수 있습니다.
[코드]
import 'dart:async'; //패키지 불러오기
void main() {
final controller = StreamController();
//브로드캐스트 스트림 객체 생성
final bstream = controller.stream.asBroadcastStream();
// 첫번째 listen()
final streamListener1 = bstream.listen((val) {
print('Listening 1');
print(val);
});
// 두번째 listen()
final streamListener2 = bstream.listen((val) {
print('Listening 2');
print(val);
});
//add()를 실행할 때마다 모든 리슨 함수의 콜백 함수에 값이 주입됩니다.
controller.sink.add(1);
controller.sink.add(2);
controller.sink.add(3);
}
[결과]
Listening 1
1
Listening 2
1
Listening 1
2
Listening 2
2
Listening 1
3
Listening 2
3
3) 함수에서 Stream 반환하기
StreamController를 사용하지 않고도 직접 Stream을 반환하는 함수를 작성할 수 있습니다.
특이한 점은 Stream을 반환하는 함수는 async*로 함수를 선언하고 yield 키워드로 값을 반환합니다.
앞서 배웠던 Future를 반환하는 함수는 async로 함수를 선언하고 return키워드로 값을 반환합니다.
[코드]
import 'dart:async';
Stream<String> calculate(int number) async* {
for (int i = 0; i < 5; i++) {
//StreamController의 add()처럼 yield 키워드로 값을 반환
yield 'i = $i';
await Future.delayed(Duration(seconds: 1));
}
}
void playStream() {
//StreamController처럼 listen()으로 콜백 함수를 입력합니다.
calculate(1).listen((val){
print(val);
});
}
void main() {
playStream();
}
[결과]
i = 0
i = 1
i = 2
i = 3
i = 4
여기까지 다트언어의 기초가 마무리되었습니다.
다음 시간은 드디어 플러터에 대해 배울 시간이네요.⭐️
다음 글부터는 기술이나 오류 위주로 정리해볼게요.😉
'Programming > Dart & Flutter' 카테고리의 다른 글
플러터 앱에 다국어화(Localization) 설정하기 (0) | 2024.02.23 |
---|---|
플러터 앱에 Firebase 연동하기 (0) | 2024.02.22 |
[코드팩토리의 플러터 프로그래밍] 2장. 다트 객체지향 프로그래밍 (0) | 2023.02.22 |
[코드팩토리의 플러터 프로그래밍] 1장. 다트(dart) 입문하기 (2) | 2023.02.20 |