-
[코드 팩토리의 플러터 프로그래밍] 3일차카테고리 없음 2025. 3. 6. 14:43
📋 목표
- Dart 인프런 강의 완강
- Chapter 05 플러터 입문하기1️⃣ 비동기 프로그래밍(AP)과 3.0 업데이트 문법
📦 비동기 프로그래밍(Async Programing)
CPU를 최대한의 효율로 사용할 수 있는 프로그래밍으로
Thread에 대한 간단한 개념
작업을 수행할 때 가장 작은 수행 단위 하나의 작업(Thread)가 수행되는 동안에 CPU는 다른 작업을 수행할 수 없다(동기적 처리 방식) 이러한 방식으로 보았을 때 어떠한 작업에서 서버 요청하는 부분이 있다면 이 요청의 response를 받기 전까지 CPU는 다른 작업을 수행할 수 없다(가장 기본적인 처리방식) 이러한 방식은 실제로 그닥 유용하지 않기에 비동기 프로그래밍을 통해서 이러한 문제의 해결을 할 수 있다. 그리고 Dart언어는 기본적으로 비동기 프로그래밍이다.
Future, delayed, async, await
Future은 미라에 받아올 값이라는 키워드로 명확히 어떤 의미인지는 모르겠지만 비동기 데이터 값에 같이 사용되는 것 같다.
delayed의 개념은 setTimeout과 거의 동일한 것 같다. 2개의 parameter를 받는데 첫번째는 경과 시간을 받고 두번째는 함수를 받는다.void main() { Future<String> name = Future.value('hihi'); addNumbers(1, 2); } void addNumbers(int number1, int number2) { print('ing~~ $number1 + $number2'); Future.delayed(Duration(seconds: 2), (){ print(number1 + number2); }); print('end'); }
- setTimeout에서는 첫번째인자로 함수를 두번째 인자로 시간을 받기에 이러한 차이점이 있어보인다.
- javascript의 동작원리 중 이벤트루프와 같은 방식으로 Dart도 진행될 수 있는 것 같다. 비동기나 setTimeout, setInterval과 같은 callback Queue에 들어가는 값들은 나중에 실행되고 구조처럼
void main() { Future<String> name = Future.value('hihi'); addNumbers(1, 2); addNumbers(3, 4); } void addNumbers(int number1, int number2) async { print('ing~~ $number1 + $number2'); await Future.delayed(Duration(seconds: 2), (){ print(number1 + number2); }); print('end'); }
- 여기서 async를 적는 부분이 좀 차이가 있는데 가장 신기했던 건 비동기 자체가 await을 사용하게 되면 기존의 구조대로 동기적으로 실행되는 줄 알았는데 출력을 보니 아래와 같았다. 즉 await을 사용한다고해서 response를 받을 때까지 기다리는 구조가 아니라는 소리다. 자바스크립트에서도 그럴까?
async function main() { await addNumbers(1, 2); await addNumbers(3, 4); } async function addNumbers(number1, number2) { console.log(`~ing~~~, ${number1} + ${number2}`); await setTimeout(() => { console.log(`result ${number1 + number2}`); }, 2000); console.log(`end ${number1 + number2}`); } console.log(main());
해당 코드를 구현해서 실행해보았지만 결과는 원하는대로 나오지 않았다.
위와 같은 결과를 가져왔기 때문에 기본적으로 JS와 Dart의 동작원리는 차이가 나는 것으로 생각되니 나중에 한 번 정리해보려 한다.
await을 사용할 때 주의할 점으로는 Future를 return하는 것만 await을 사용할 수 있다. 비동기를 하려면 무조건 Future가 사용된다고 보면 되는 것이다.
void main() async { Future<String> name = Future.value('hihi'); await addNumbers(1, 2); await addNumbers(3, 4); } Future<void> addNumbers(int number1, int number2) async { print('ing~~ $number1 + $number2'); await Future.delayed(Duration(seconds: 2), (){ print(number1 + number2); }); print('end'); }
- 기존에 사용했던 addNumbers 함수를 비동기형태로 순서대로 동작하고 싶다면 Future를 통해서 해당 함수의 return을 바꾸어주어야한다.
stream
Future와 비교당하는 키워드
개념은 await을 통해서 값을 한 번에 출력해내는 일반적인 방식이 아닌 직접 닫는 표현을 해주고 그 닫는 표현 전까지 yield라는 키워드를 통해서 return을 데이터의 흐름을 여러번 내려받을 수 있다는 것
조금 복잡해보이는 부분이 많기에 잘 들어야할 것 같다. 실제로 이렇게 보완해서 나오는 개념이 주로 많이 사용되니깐...ㅎ기본적으로 제공해주는 기능이 아니기에 package를 불러와야한다.
import 'dart:async'; void main() { final controller = StreamController(); final stream = controller.stream; final streamListener1 = stream.listen((val) { print('listener1: $val'); }); controller.sink.add(1); controller.sink.add(2); controller.sink.add(3); controller.sink.add(4); }
- 기본적인 동작방식이다 dart:async라는 패키지를 불러와서 controller와 stream을 생성하고, controller로 해당 stream의 값을 변경할 수 있는 구조 그리고 값이 변경될 때마다 stream에 listen을 통해서 실행문이 동작한다.
import 'dart:async'; void main() { playAllStream().listen((val) { print(val); }); } Stream<int> playAllStream() async* { yield* calculate(1); yield* calculate(1000); } Stream<int> calculate(int number) async* { for (int i = 0; i < 5; i++) { yield i * number; } await Future.delayed(Duration(seconds: 2)); }
- yeild를 사용한 방식
📦 3.0 업데이트 문법
Record, destructuring, rest, ignore
nameAndAge(Map<String, dynamic> json) { return [json['name'], json['age']]; }
위 예제를 보면 Map을 매개변수로 받아서 해당 변수의 구성요소를 list형태로 반환하는 함수이다. 현재 함수에서는 가장 큰 문제는 dynamic으로 int, String 어떤 자료형을 가져올지 모르기에 return하는 list의 타입을 특정하기 힘들다는 문제이다. 그렇게에 단순히 list로 return하는 것은 명확하지 않다.
이러한 문제점을 해결하기 위해서 Record를 활용한다.
void main() { final result = nameAndAge({'name': 'hi', 'age': 12}); print(result.$1); print(result.$2); } // Record 기능 tuple과 비슷한 기능으로 예제를 보면서 이해하자 (String, int) nameAndAge(Map<String, dynamic> json) { return (json['name'], json['age']); } // result로 hi와 12가 각각 출력된다.
이렇게 명확하게 무슨 값을 return하는지 알 수 있게 되었다. 또한 값을 가져오는 방식도 순서가 보장되기에 $1, $2와 같이 손쉽게 가져올 수 있다.
List<Map<String, dynamic>> getAAAType (){ return [ { 'name': 'hihi', 'age': 12 }, { 'name': 'hello', 'age': 13 } ]; }
해당 함수는 Map을 구성요소로 List를 반환하는 함수인데 함수의 타입을 보게되면 value로 오는 값이 int일수도 있고 뭐가 될지 모르기에 dynamic을 통해서 나타내었다. 이러한 경우도 Record를 활용해서 명확하게 타입을 나타낼 수 있다.
void main() { final result = getAAAType(); for (final value in result) { print(value.name); print(value.age); } } List<({String name, int age})> getAAAType() { return [(name: 'hihi', age: 12), (name: 'hello', age: 13)]; }
네임드 파라미터 결국 데이터를 핸들링할 때에 가장 자주 사용하게 되는 구조는 네임드 파라미터를 활용한 방법이라 생각된다 그렇기에 위와 같은 방식은 자주 사용될 방식이라 생각된다.
순서와 타입을 보장해주는 것이지 개수가 몇 개든 상관은 없다. 그리고 네임드 파라미터가 아닌 순서에 대한 값에 대해서 사용하는 방식도 있지만 그만 알아보겠다.
그럼 이어서 destructuring을 알아보겠다. 구조분해라고 보면 되기에 간단하다.
void main() { final [first, second] = getAAAType(); print(first); print(second); } List<({String name, int age})> getAAAType() { return [(name: 'hihi', age: 12), (name: 'hello', age: 13)]; }
Record뿐만아니라 List, Map.. 다른 자료형에서도 사용 가능하다.
final numbers = [1, 2, 3, 4, 5, 6, 7]; final [x, _, ...rest, y, z] = numbers;
위 하나로 rest, ignore에 대한 얘기는 생략...
pattern matching을 적극적으로 사용한 Validation
굉장히 신기한 기능이다 siwtch문에 다양한 조건문도 넣을 수 있고 class도 넣을 수 있고 이거저거 다 넣을 수도 있다고 한다. [_, _, _] 이게 또 신기했다.
switch를 사용하는 또다른 문법인데 arrow func로 바로 표현이 가능하다 _ 이건 default임
클래스의 키워드들
class 앞에 final을 붙이게 되면 extends, implement, mixin으로 사용이 불가능하다.
base를 붙이게 되면 extends는 가능하지만 implement는 불가능하다. extends도 base, sealed, final로 선언된 클래스만 가능
interface로 선언하면 implement만 가능하다.
sealed로 선언하면 abstrat이면서 final이다. 패턴 매칭을 사용할 수 있도록 해준다. 모든 케이스가 패턴매칭이 되었는지 확인하게 해주는 기능
mixin class는 extends나 with를 사용할 수 없다. 클래스는 on 키워드를 사용할 수 없기에 on도 사용 불가
2️⃣ Chapter 05 플러터 입문하기
📦 플러터란?
구글이 구현한 크로스 플랫폼 프레임워크로 초기에는 iOS와 Android 앱만 지원했지만 현재는 MacOS, 윈도우 .. 웹사이트까지 지원
스키라 엔진이 실행되는 플랫폼에서는 똑같은 API를 사용해서 프로그래밍할 수 있어 일관된 UI 제공이 가능하며 다른 크로스 플랫폼 프레임워크보다 UI 디버깅 부담이 적다.출처: 코드팩토리의 플러터 프로그래밍 플러터는 3단계의 구조로 되어있는데 Embedder와 Engine부분은 아직까지 잘 모르겠고 Framwork부분이 필수 위젯, 애니메이션, 등등의 개발자가 직접 사용하는 요소들이 위치해있다고 한다. 이러한 계층구조가 일관된 API를 제공하게 해준다고 한다.
또한 React Native와 비교했을 때 스키아 엔진을 통해서 보다 빠른 퍼포먼스를 낼 수 있다는 장점이 있다.