Flutter 창업반 3주차 TIL2 - 라이브러리 이용, 동기/비동기
11/5 - 화
Flutter 창업반 3주차 TIL2
라이브러리 이용 확장
- 라이브러리: 특정 기능이나 작업을 쉽고 빠르게 수행할 수 있도록 미리 작성한 코드의 집합
- 필요한 기능만 뽑아서 사용할 수 있는 효율성 / 코드 재사용성 / 반복하지 않기 때문에 가독성
- import '[라이브러리 이름]'; or import '[파일 경로]';
- as 를 통해 라이브러리에 별칭을 부여할 수 있음
- show 를 통해 라이브러리에서 필요한 부분만 선택적으로 가져올 수 있음
- hide 를 통해 라이브러리의 특정 부분을 제외하고 가져올 수 있음
- deferred as 를 통해 라이브러리가 필요한 시점에 로드되도록 할 수 있음. 지연로딩
import 'package:greetings/hello.dart' deferred as hello;
Future<void> greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
// hello라는 식별자를 통해서 라이브러리 호출.
await 는 실행이 끝날 때까지 다음 작업을 못 하도록
deferred as [식별자] 를 통해 식별자를 만들어 주는 순간, 그 식별자에 loadLibrary() 가 자동으로 정의
Dart SDK 에 포함되어 있는 라이브러리
- Dart SDK 표준 라이브러리, 프로그래밍에 필요한 기능을 제공하는 핵심 라이브러리
- 모든 플랫폼 (Native platform + Web platform) 에서 사용 가능
1) dart:core - import하지 않아도 됨. 타입 (ex. int, double, String), 콜렉션 등 핵심 기능을 지원
2) dart:async - Future , Stream 과 같은 클래스를 통해 비동기 프로그래밍을 지원
3) dart:collection - dart:core 에서 제공하지 않는 무거운 콜렉션 타입을 제공 queue, linked list, hashmap, binary tree 등
4) dart:convert - 서로 다른 데이터 표현 방식을 가진 데이터 (ex. JSON, UTF-8) 를 전환하기 위한 인코더 (Incoder) 와 디코더 (Decoder) 를 제공
5) dart:developer - 디버거 (Debugger) 같은 개발자 도구와 상호작용하는 기능을 제공
6) dart:math - 수학 상수와 함수, 난수 생성 등의 수학 관련 기능을 제공
7) 그 외 dart.dev/libraries
- Native platform - Mobile, Desktop 에서 사용 가능한 라이브러리
1) dart:ffi - Dart 가 C API 를 사용할 수 있도록 하는 기능을 제공
2) dart:io - input/output. 파일, 소켓, HTTP, 기타 입출력 등의 기능을 제공
- Web platform 에서 사용 가능한 라이브러리 - JavaScript 로 컴파일
1) package:web - 가벼운 브라우저 API 와 연결하는 기능을 제공
2) dart:js_interop - JavaScript 와 브라우저 API 를 상호운용할 수 있는 기능을 제공
3) dart:html - HTML 요소들과 Web 기반 응용 프로그램 리소스들을 제공
Dart SDK 에 포함되어 있지 않은 라이브러리
- Pub.dev 에서 다양한 외부 라이브러리를 사용 가능
- cupertino_icons
- Flutter 의 Cupertino 위젯에 쓰이는 기본 아이콘 에셋을 제공
- intl
- 번역, 날짜 포맷팅, 숫자 포맷팅 등 국제화와 현지화 기능을 제공
- shared_preferences
- 간단한 데이터를 다루는 기능을 제공
- iOS 와 macOS 에서 사용하는 NSUserDefaults, Android 에서 사용하는 SharedPreferences 와 같은 역할
- url_launcher
- URL 을 다루는 기능을 제공
- image_picker
- iOS 와 Android 에서 사진 다루는 기능을 제공
- 예시로 카메라로 사진 찍는 기능이랑 앨범에서 사진 선택하는 기능
- firebase_core
- Firebase 와 연동하는 작업할 때 필요한 핵심 기능을 제공
- 데이터베이스, 배포, 오류 축적, AI 등
- firebase_auth
- Firebase 인증 API 를 사용하기 위한 기능을 제공
- google_fonts -
- fonts.google.com 에서 제공하는 폰트를 사용하기 위한 기능을 제공
- permission_handler
- iOS 와 Android 의 권한을 다루는 기능을 제공
- custom_lint
- Lint 규칙을 쉽게 적용할 수 있도록 해주는 기능을 제공
- Lint : 오류가 발생할 수 있는 코드, 코드 스타일에 어긋난 코드, 비효율적이거나 불필요한 코드 등을 찾아내서 경고를 띄워주는 도구
- 코드의 품질을 개선 목적, 코드의 가독성을 향상, 유지보수성 높은 프로젝트
- flutter_svg
- SVG 파일형식을 렌더링하기 위한 기능을 제공
- cached_network_image **
- 네트워크를 통해 사진을 받아오고, 캐싱하는 기능을 제공
- flutter_local_notifications
- 로컬 알림을 다루는 기능 (ex. 알림 표시, 알림 예약) 을 제공
- path_provider
- 파일 시스템을 사용하기 위한 기능을 제공
- geolocator
- iOS 와 Android 에서 위치를 다루기 위한 기능을 제공
- dio **
- HTTP 네트워크 관련 통신 사용을 위한 기능을 제공
11/6 - 수
Flutter 창업반 3주차 TIL3
동기 프로그래밍 (Synchronous Programming)
- 작업이 완료될 때까지 기다렸다가 결과가 나오면 값을 반환
- 결과값이 나올 때까지 다른 모든 연산이 멈추기 때문에 다른 작업을 할 수 없음
- 병렬 계산이 가능할때 직렬 계산을 하고 있다 비효율적
void introduce(String name) {
print('자기소개 시작 !');
print('안녕 ? 나는 $name ~');
print('자기소개 완료 !');
}
void main() {
introduce('강미래');
introduce('강현재');
}
/*
자기소개 시작 !
안녕 ? 나는 강미래 ~
자기소개 완료 !
자기소개 시작 !
안녕 ? 나는 강현재 ~
자기소개 완료 !
*/
비동기 프로그래밍 (Asynchronous Programming)
- 작업이 완료될 때까지 기다리지 않고, 미래의 특정 시점에 값을 반환
- 결과값이 나올 때까지 멈춰 있지 않고, 수행할 수 있는 다른 작업을 찾아서 수행
- 결과값이 나올 때까지 다른 모든 연산이 멈추는 것이 아니기 때문에 다른 작업을 계속 할 수 있음
- 시간 오래 걸리는 작업: 파일로부터 데이터 불러올때, 데이터베이스에서 데이터 사용할때, 네트워크를 통해서 데이터 뽑아올때
- Dart SDK 표준 라이브러리 중에서 dart:async에 있는 Future , Stream 과 같은 클래스 를 통해 비동기 프로그래밍 을 지원
Future클래스
- 비동기 프로그래밍 에서 사용되는 대표적인 클래스
- Dart SDK 표준 라이브러리 인 dart:async 에 포함되어 있는 클래스
- 원래는 Future 클래스를 사용하려면 import 'dart:async'; 를 해야 하지만, dart:core 가 Future 클래스를 사용할 수 있도록 해주어서 따로 dart:async 를 import 하지 않아도 됨 (dart pad내에 이미 내장. Import x)
- 작업이 성공적으로 완료되었을 때 해당 결과값을 반환하고, 실행을 종료. 결과값의 타입은 Future
- Future<타입> 은 제네릭 클래스. 확장성이 있다
Future<int> number = Future.value(1);
Future<String> name = Future.value('강미래');
Future<bool> isOddNumber = Future.value(1.isOdd);
//타입들을 단지 Future 가 감싸고 있을 뿐이라고 생각하면 됨
- 하나의 작업에 대해 값이나 이벤트가 한 번 발생하는 단일 비동기 작업에 사용
cf. Future 에 있는 delayed() 라는 메서드
예시1. Future.delayed(Duration(seconds: [지연 시간]));
void main() {
int seconds = 2;
print('실행 시작 !');
Future.delayed(Duration(seconds: seconds));
print('실행 끝 !');
}
/*
실행 시작 !
실행 끝 !
*/
예시2. Future.delayed(Duration(seconds: [지연 시간]), () {[지연 시간 후의 동작]});
void main() {
int seconds = 2;
print('실행 시작 !');
Future.delayed(Duration(seconds: seconds), () {
print('$seconds초 다 기다림 !');
});
print('실행 끝 !');
}
/*
실행 시작 !
실행 끝 !
(2초 후) 2초 다 기다림 !
*/
- Future.delayed() 로 제일 기본적인 비동기 코드 작성
void introduce(String name) {
print('$name의 자기소개 시작 !');
Future.delayed(Duration(seconds: 2), () {
print('안녕 ? 나는 $name ~');
});
print('$name의 자기소개 끝 !');
}
void main() {
introduce('강미래');
}
/*
강미래의 자기소개 시작 !
강미래의 자기소개 끝 !
안녕 ? 나는 강미래 ~
*/
//코드가 동기적으로 동작했다면 2초동안 멈춰 있었을것
- 코드를 응용해서 2가지 작업을 동작
void introduce(String name) {
print('$name의 자기소개 시작 !');
Future.delayed(Duration(seconds: 2), () {
print('안녕 ? 나는 $name ~');
});
print('$name의 자기소개 끝 !');
}
void main() {
introduce('강미래');
introduce('강현재');
}
/*
강미래의 자기소개 시작 !
강미래의 자기소개 끝 !
강현재의 자기소개 시작 !
강현재의 자기소개 끝 !
안녕 ? 나는 강미래 ~
안녕 ? 나는 강현재 ~
*/
- async 랑 await를 사용하여 비동기적 코드를 순서대로 실행
- 비동기적인 코드를 동기적인 코드처럼 순서대로 실행되도록 하고 싶은 경우에는 비동기적인 코드 앞에 await를 붙이고, await 를 붙인 코드를 포함하는 함수에 async 를 붙이면 됨
- await 는 Future 류의 코드 앞에만 붙일 수 있음
void introduce(String name) async {
print('$name의 자기소개 시작 !');
await Future.delayed(Duration(seconds: 2), () {
print('안녕 ? 나는 $name ~');
});
print('$name의 자기소개 끝 !');
}
void main() {
introduce('강미래');
introduce('강현재');
}
/*
강미래의 자기소개 시작 !
강현재의 자기소개 시작 !
안녕 ? 나는 강미래 ~
강미래의 자기소개 끝 !
안녕 ? 나는 강현재 ~
강현재의 자기소개 끝 !
*/
//2초 기다리는 동안 다음 작업인 introduce('강현재'); 가 실행되고 있음
- 비동기적으로 2개의 작업이 순서대로 실행되게 코드 작성
Future<void> introduce(String name) async {
print('$name의 자기소개 시작 !');
//await 는 Future 류의 코드 앞에만 사용할 수 있다고 했기 때문에 introduce() 의 반환 타입을 void 타입 introduce 함수를 Future<void> 로 바꿔 줌
await Future.delayed(Duration(seconds: 2), () {
print('안녕 ? 나는 $name ~');
});
print('$name의 자기소개 끝 !');
}
// main도 함수기 때문에 async를 붙여줄 수 있음
void main() async {
await introduce('강미래');
await introduce('강현재');
}
/*
강미래의 자기소개 시작 !
안녕 ? 나는 강미래 ~
강미래의 자기소개 끝 !
강현재의 자기소개 시작 !
안녕 ? 나는 강현재 ~
강현재의 자기소개 끝 !
*/
- Void 대신 반환값이 있는 형태
Future<String> introduce(String name) async {
print('$name의 자기소개 시작 !');
await Future.delayed(Duration(seconds: 2), () {
print('안녕 ? 나는 $name ~');
});
print('$name의 자기소개 끝 !');
return '이름은 $name ~'; //하나의 값이 반환되는 순간 future는 실행을 종료
}
void main() async {
var riverfuture = await introduce('강미래'); // await 실행되면서 다른 작업을 찾음
var riverpresent = await introduce('강현재');
print('나의 $riverfuture 너의 $riverpresent'); // riverfuture, riverpresent두개가 종속되어 있기에 두개의 await가 실행되고 나서야 실행될 수 있음
}
/*
강미래의 자기소개 시작 !
안녕 ? 나는 강미래 ~
강미래의 자기소개 끝 !
강현재의 자기소개 시작 !
안녕 ? 나는 강현재 ~
강현재의 자기소개 끝 !
나의 이름은 강미래 ~ 너의 이름은 강현재 ~
*/
Future의 한계
- 하나의 작업당 결과값을 1번만 받을 수 있답니다 .. 일회용
- 하나의 작업에 결과값이 여러 번 나오는 경우에 적합하지 않음
- 이러한 한계를 극복하기 위해서 Stream 님이 등장
Stream 클래스
- 비동기 프로그래밍 에서 사용되는 대표적인 클래스
- Dart SDK 표준 라이브러리 인 dart:async 에 포함되어 있는 클래스
- dart:core 가 Stream 클래스를 사용할 수 있도록 해주어서 따로 dart:async 를 import 하지 않아도 됨
- 시간에 따라 연속적인 데이터 흐름을 제공. 한 번에 하나의 값이 아닌 여러 값을 비동기적으로 받을 수 있음
- 과값을 한 번 반환하고 나면 실행이 종료되는 Future 와 다르게 직접 실행을 종료해 주어야 실행이 종료됨
- 종료하지 않으면 계속 받으려고 준비하고 있는 상태가 됨
- Stream클래스의 결과값 타입은 Stream
- Steam<타입> 형태 -> 제네릭. Future 랑 마찬가지로, Stream 도 제네릭 클래스
Steam<int> number = Steam.value(1);
Steam<String> name = Steam.value('강미래');
Steam<bool> isOddNumber = Steam.value(1.isOdd);
- 하나의 작업에 대해 값이나 이벤트가 여러 번 발생하는 경우에 사용. 사용자가 버튼 누를 때마다 그 이벤트를 받아오는 경우 or 스와이프 등
- 비동기 연산의 결과값이 여러 번 반환되는 경우 그 값을 순차적으로 받기 위해 사용
Cf. Stream에서 사용하는 yield 와. listen()
yield : 값을 방출하도록 하는 키워드
Stream<String> emitNames() async* {
yield '강미래';
yield '강현재';
yield '강과거';
}
//Future 를 반환 타입으로 갖는 함수는 async 를 사용했지만,
Stream 을 반환 타입으로 갖는 함수는 async*을 사용
yield 옆에 있는 값의 타입이랑 Stream<타입> 에 있는 타입 이 같아야
Stream 에서는 반환 대신 방출 이라는 단어를 사용
Stream<String> emitNames(List<String> names) async* {
for (var i = 0; i < names.length; i++) {
yield '${i + 1}번째는 ${names[i]} ~';
}
}
//매개변수나 반복문을 사용해서 방출할 수 있음
//함수 와 메서드 에서 사용하는 return 과 같은 개념
listen() : yield 를 통해 방출되는 값을 받기 위해 사용하는 메서드
Stream<String> emitNames() async* {
yield '강미래';
yield '강현재';
yield '강과거';
}
void main() {
int number = 1;
emitNames().listen((name) {
print('$number번째는 $name ~');
number += 1;
});
}
// emitName() 에서 yield 를 통해 방출되는 값들이 순서대로 하나씩 listen 의 매개변수인 name 에 들어감
/*
1번째는 강미래 ~
2번째는 강현재 ~
3번째는 강과거 ~
*/
다르게 표현하는법
Stream<String> emitNames(List<String> names) async* {
for (var i = 0; i < names.length; i++) {
yield '${i + 1}번째는 ${names[i]} ~';
}
}
void main() {
List<String> names = ['강미래', '강현재', '강과거'];
emitNames(names).listen((element) {
print(element);
});
}
/*
1번째는 강미래 ~
2번째는 강현재 ~
3번째는 강과거 ~
*/
- 10 에서 0 까지의 정수를 출력하는 기본적인 비동기 코드
Stream<int> emitNumbers(int first) async* {
for (var i = first; i >= 0; i--) {
yield i;
}
}
void main() {
emitNumbers(10).listen((number) {
print(number);
});
}
/*
10
9
8
7
6
5
4
3
2
1
0
*/
- 정수들을 한 번에 다 출력하지 않고, 1초에 한 개씩 출력하고 싶을때
Stream<int> emitNumbers(int first) async* { //await를 위해 but stream이기에 *
for (var i = first; i >= 0; i--) {
yield i;
await Future.delayed(Duration(seconds: 1));
}
}
void main() {
emitNumbers(10).listen((number) {
print(number);
});
}
/*
10
9
8
7
6
5
4
3
2
1
0
*/
//타이머 코드
- 동기적으로 작동한다는것 보기 위한 타이머 2개 코드
Stream<int> emitNumbers(int first) async* {
for (var i = first; i >= 0; i--) {
yield i;
await Future.delayed(Duration(seconds: 1));
}
}
void main() {
emitNumbers(10).listen((number) {
print('[타이머 1] $number');
});
emitNumbers(10).listen((number) {
print('[타이머 2] $number');
});
}
/*
[타이머 1] 10
[타이머 2] 10
[타이머 1] 9
[타이머 2] 9
[타이머 1] 8
[타이머 2] 8
[타이머 1] 7
[타이머 2] 7
[타이머 1] 6
[타이머 2] 6
[타이머 1] 5
[타이머 2] 5
[타이머 1] 4
[타이머 2] 4
[타이머 1] 3
[타이머 2] 3
[타이머 1] 2
[타이머 2] 2
[타이머 1] 1
[타이머 2] 1
[타이머 1] 0
[타이머 2] 0
*/
//동기적 프로그램이었다면 타이머1전부 출력 후 타이머2가 출력되었을것
- 타이머 2개를 짝수 홀수 타이머로
Stream<int> emitNumbers(int first) async* {
for (var i = first; i >= 0; i--) {
yield i;
await Future.delayed(Duration(seconds: 1));
}
}
void main() {
emitNumbers(10).where((number) => number.isOdd).listen((number) {
print('[홀수 타이머] $number');
});
emitNumbers(10).where((number) => number.isEven).listen((number) {
print('[짝수 타이머] $number');
});
}
/*
[짝수 타이머] 10
[홀수 타이머] 9
[짝수 타이머] 8
[홀수 타이머] 7
[짝수 타이머] 6
[홀수 타이머] 5
[짝수 타이머] 4
[홀수 타이머] 3
[짝수 타이머] 2
[홀수 타이머] 1
[짝수 타이머] 0
*/
//Stream에서는 방출되는 값에 where, any, every, reduce, fold, takewhile, skipwhile 같은 고차함수를 사용하여 값을 가공할 수 있다.