공부 일기/TIL

Flutter 창업반 3주차 TIL2 - 라이브러리 이용, 동기/비동기

oosuhada 2024. 11. 8. 19:36

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 표준 라이브러리, 프로그래밍에 필요한 기능을 제공하는 핵심 라이브러리

 

  1. 모든 플랫폼 (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

 

 

  1. Native platform - Mobile, Desktop 에서 사용 가능한 라이브러리

 

1) dart:ffi - DartC API 를 사용할 수 있도록 하는 기능을 제공

 

2) dart:io - input/output. 파일, 소켓, HTTP, 기타 입출력 등의 기능을 제공

 

 

  1. Web platform 에서 사용 가능한 라이브러리 - JavaScript 로 컴파일

 

1) package:web - 가벼운 브라우저 API 와 연결하는 기능을 제공

 

2) dart:js_interop - JavaScript 와 브라우저 API 를 상호운용할 수 있는 기능을 제공

 

3) dart:html - HTML 요소들과 Web 기반 응용 프로그램 리소스들을 제공

 

 

 

Dart SDK 에 포함되어 있지 않은 라이브러리

- Pub.dev 에서 다양한 외부 라이브러리를 사용 가능

 

  1. cupertino_icons 

- FlutterCupertino 위젯에 쓰이는 기본 아이콘 에셋을 제공

 

  1. intl 

- 번역, 날짜 포맷팅, 숫자 포맷팅 등 국제화와 현지화 기능을 제공

 

  1. shared_preferences 

- 간단한 데이터를 다루는 기능을 제공

- iOSmacOS 에서 사용하는 NSUserDefaults, Android 에서 사용하는 SharedPreferences 와 같은 역할

 

  1. url_launcher 

- URL 을 다루는 기능을 제공

 

  1. image_picker 

- iOSAndroid 에서 사진 다루는 기능을 제공

- 예시로 카메라로 사진 찍는 기능이랑 앨범에서 사진 선택하는 기능

 

  1. firebase_core

- Firebase 와 연동하는 작업할 때 필요한 핵심 기능을 제공

- 데이터베이스, 배포, 오류 축적, AI 등

 

  1. firebase_auth 

- Firebase 인증 API 를 사용하기 위한 기능을 제공

 

  1. google_fonts -

- fonts.google.com 에서 제공하는 폰트를 사용하기 위한 기능을 제공

 

  1. permission_handler

- iOSAndroid 의 권한을 다루는 기능을 제공

 

  1. custom_lint

- Lint 규칙을 쉽게 적용할 수 있도록 해주는 기능을 제공

- Lint : 오류가 발생할 수 있는 코드, 코드 스타일에 어긋난 코드, 비효율적이거나 불필요한 코드 등을 찾아내서 경고를 띄워주는 도구

- 코드의 품질을 개선 목적, 코드의 가독성을 향상, 유지보수성 높은 프로젝트

 

  1. flutter_svg

- SVG 파일형식을 렌더링하기 위한 기능을 제공

 

  1. cached_network_image **

- 네트워크를 통해 사진을 받아오고, 캐싱하는 기능을 제공

 

  1. flutter_local_notifications

- 로컬 알림을 다루는 기능 (ex. 알림 표시, 알림 예약) 을 제공

 

  1. path_provider

- 파일 시스템을 사용하기 위한 기능을 제공

 

  1. geolocator

- iOSAndroid 에서 위치를 다루기 위한 기능을 제공

 

  1. 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초 다 기다림 !

*/

 

 

  1. Future.delayed() 로 제일 기본적인 비동기 코드 작성

 

void introduce(String name) {

print('$name의 자기소개 시작 !');

  

  Future.delayed(Duration(seconds: 2), () {

    print('안녕 ? 나는 $name ~');

  });

 

print('$name의 자기소개 끝 !');

}

 

void main() {

  introduce('강미래');

}

 

/*

강미래의 자기소개 시작 !

강미래의 자기소개 끝 !

안녕 ? 나는 강미래 ~

*/

//코드가 동기적으로 동작했다면 2초동안 멈춰 있었을것

 

  1. 코드를 응용해서 2가지 작업을 동작

 

void introduce(String name) {

print('$name의 자기소개 시작 !');

  

  Future.delayed(Duration(seconds: 2), () {

    print('안녕 ? 나는 $name ~');

  });

 

print('$name의 자기소개 끝 !');

}

 

void main() {

  introduce('강미래');

  introduce('강현재');

}

 

/*

강미래의 자기소개 시작 !

강미래의 자기소개 끝 !

강현재의 자기소개 시작 !

강현재의 자기소개 끝 !

안녕 ? 나는 강미래 ~

안녕 ? 나는 강현재 ~

*/

 

  1. 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('강현재'); 가 실행되고 있음

 

  1. 비동기적으로 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('강현재');

}

 

/*

강미래의 자기소개 시작 !

안녕 ? 나는 강미래 ~

강미래의 자기소개 끝 !

강현재의 자기소개 시작 !

안녕 ? 나는 강현재 ~

강현재의 자기소개 끝 !

*/

 

  1. 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번째는 강과거 ~

*/

 

 

  1. 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. 정수들을 한 번에 다 출력하지 않고, 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

*/

 

//타이머 코드

 

  1. 동기적으로 작동한다는것 보기 위한 타이머 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가 출력되었을것

 

  1. 타이머 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 같은 고차함수를 사용하여 값을 가공할 있다.