[25.06.27 TIL] 개인 과제와 Dart 프로그래밍

김영민·2025년 6월 27일

🎯 오늘은 오전에 제출까지 시간이 좀 남아서 개인 과제 기능을 조금 추가했다.
그리고 오후에는 과제 해설을 듣고, 남은 강의를 모두 들어서 이번주 학습 목표를 모두 완수했다.


🥽 개인 과제

💻 기능 추가

👩‍💻 작업

✅ 오전에 시간 여유가 있어 기능 추가 작업을 진행

📁 lib/shopping_mall

  // 장바구니 초기화
  void clearCart() {
    if (totalPrice != 0) {
      totalPrice = 0;
      print("장바구니를 초기화합니다.");
    } else if (totalPrice == 0) {
      print("이미 장바구니가 비어있습니다.");
    }
  }

💬 totalPrice를 사용하여, 장바구니 기능을 쇼핑몰 클래스에 추가하였다.
장바구니에 추가하면 값이 바뀌기 때문에 지표로 활용하기 좋다고 판단하였고, 결과값을 출력하도록 구현했다.


📁 bin/shopping_mall

// 프로그램 종료 로직
    else if (numberInput == "4") {
      print("이용해 주셔서 감사합니다 ~ 안녕히 가세요 !");
      break;
      print("정말 종료하시겠습니까 ?");
//
      String? offInput = stdin.readLineSync()?.trim();
      if (offInput == "5") {
        print("이용해 주셔서 감사합니다 ~ 안녕히 가세요 !");
        break;
      } else {
        print("종료하지 않습니다.");
      }
    }
   // 장바구니 초기화 로직
   else if (numberInput == "6") {
     shoppingMall.clearCart();
   }

💬 이어서 위에서 추가한 장바구니 초기화 로직을 사용할 수 있게 호출해주었다.


💬 그리고 프로그램 종료 시 바로 종료되는 것이 아닌 프로그램 종료 확인 메세지 출력 기능을 추가하여 재확인하도록 구현했다.

동일하게 값을 입력받아 5 라는 값이 입력되면 종료하도록 하였고, 그 외의 값은 모두 종료하지 않고 다시 메뉴 목록이 뜨도록 했다.


❕ 개선하면 좋은 기능

3. 장바구니에 담은 상품들의 목록과 가격을 볼 수 있는 기능

 
[ 설명 ]

  • 3을 입력 시 장바구니에 담긴 상품 목록과 장바구니에 담긴 상품들의 금액 총합을 출력합니다.

    [ 조건 ]
  • 장바구니가 비어있을 경우 장바구니에 담긴 상품이 없습니다. 출력
  • 출력 형태 : 장바구니에 셔츠, 청바지, 넥타이가 담겨있네요. 총 [가격]원 입니다!

    [ 힌트 ]
  • 장바구니에 담길 상품 이름을 담을 변수를 List 타입의 변수로 새로 생성하고, 리스트 내 요소를 한번에 출력할 수 있는 join 함수를 이용해 보세요.

💭 필수와 도전 과제 기능까지는 구현을 완료했는데, 바로 이 도전 과제 하나가 남았다.
비어있을 경우 totalPrice 로 확인, 그리고 새로운 List 타입 변수에 입력한 상품명을 담아주고 이를 출력하면 되는 것 같다.


💻 코드 해설

var isTerminated = false;
while (!isTerminated) {
.
.
.
// 프로그램 종료 로직
isTerminated = true;
var isReady = true;
while(isReady) { 
	//시작되고있으면
    .
    .
   isReady = false; // 종료 
}

while (true) 보다는 bool 타입을 써서 이런식으로 프로그램을 종료시키는 로직도 있다고 한다.

✔ 이 bool 타입을 false, true 로 시작할 때 그 상황마다 달라진다. !나 bool 값 변경이나...


if (cartItem == isEmpty) {
	print("장바구니에 담긴 상품이 없습니다.")
	print("장바구니에  ${cartItem.join(", ")}가 담겨있네요. 총 ${totalPrice}원 입니다!");
}

✔ 이런 식으로 cartItem 에 상품을 담고, isEmpty 있는지 확인하는 코드를 사용해 값을 나눠 출력한다고 한다. 위에서 이론으로 생각한 방법이 맞았던 것이다.


💬 REDAME 수정

✅ 기능 추가에 따른 README 수정

🛠 기능 소개

  • 📜 상품 목록 조회
  • 🛒 장바구니 담기
  • 💰 장바구니 총 금액 확인
  • ❌ 프로그램 종료
     
  • 🛒 장바구니 초기화
  • ❌ 프로그램 종료 메세지 출력

🌼 Git Commit

기능 추가
✅ 장바구니 초기화 기능 구현
✅ 프로그램 종료 확인 메세지 출력
✅ README 수정


💬 Dart 프로그래밍

🔥 예외 처리

✏️ 예외(Exception)

✅ 프로그램이 실행되는 동안 발생하는 예외적인, 즉 로직때로 흘러가지 않는 돌발 상황

🔍 예외 처리를 하지 않으면 프로그램이 종료된다.


🔍 모든 종류의 예외는 Excepiton 클래스를 상속받는다.

Exception 클래스는 모든 종류의 예외 클래스의 상위 클래스다.


var exception = Exception("뭘까?")

❌ Exception 클래스를 상속 받지 않고 생성하면, 예외 타입을 바로 알 수 없기 때문에 이를 상속받아 객체를 생성한다.


📋 Dart 예외 종류

📁 DeferredLoadException

DeferredLoadException(String message)

✔ 라이브러리가 필요한 시점에 로드되지 못하면 발생한다.
message 에 예외에 대한 설명을 작성한다.


📁 FormatException

FormatException([String message = "", dynamic source, int? offset])

✔ 데이터가 처리 가능한 형태가 아닐 때, 처리하지 못할 때 발생한다. 즉, 타입이 달라서 발생하는 오류, String, int 타입, ? 널 허용 여부 매칭 등 이에 해당하는 것이다.

✔ 'source' 는 예외가 발생한 원인


📁 IOException

IOException()

✔ Input, Output 👉 즉, 입출력 동작 중 발생하는 오류를 뜻한다.
✔ 매개 변수로 message 를 받지 않는다.

🔸 FileSystemException : 파일
   파일을 다룰 때 발생 👉 파일에 접근 불가나 파일 자체가 없을 때, 혹은 파일을 찾을 수 없는 경우 발생한다.
🔸 HttpException : http
🔸 ProcessException
🔸 SignalException
🔸 SocketException
🔸 StdinException : 입력
🔸 StdoutException : 출력
🔸 TlsException
🔸 WebSocketException


📁 OSError

OSError([String message = "", int errorCode = noErrorCode])

✔ 운영체제로 인해 발생하는 오류로 message 로 운영체제가 제공하는 예외에 대한 설명을 받는다. 설명을 제공하지 않으면 빈 문자열 ”” 이 나온다.

errorCode 에는 운영체제가 제공하는 오류 코드가 들어간다.


📁 TimeoutException

TimeoutException(String? message, [Duration? duration])

✔ 비동기 결과를 기다리는 동안 특정 시간이 초과되면 발생한다.

message 에 시간 초과가 발생한 원인에 대한 들어가고, duration 에는 초과된 시간이 들어간다.


📋 사용자 예외

✅ Dart 예외가 아닌 커스텀한 예외를 사용하고 싶을 때, Exception 클래스를 상속 받는 예외 클래스를 생성한다.


class AgeException implements Exception {
  final String? message;  
  AgeException(this.message);
//  
  
  String toString() => message ?? 'AgeException 이 발생했어요 !';
}

implements 를 사용하여 상속 받고, @oerride 로 재정의한다.


class AgeException implements Exception {
  final String? message;  
  AgeException(this.message);
}
//
void printAge(int age) {
	if (age < 0) {
		throw AgeException(null);
	}
  print(age);
}
//
void main() {
	try {
		printAge(-8);
	} catch (e) {
    print(e);
  }
}

throw 는 의도적으로 Exception 을 발생시키겠다는 의미이다.

try ~ catch 는 예외를 처리한다.


✅ 발생한 예외를 처리하기 위한 경우

📁 try

try {
	// 예외가 발생할 수 있는 코드
}

✔ 예외가 발생할 수 있는 코드를 넣는 부분으로, 생략할 수 없다.

try 코드 블록 실행 중 예외가 발생하면 뒤에 코드는 실행되지 않고 끝나며, catch 문으로 넘어간다.


📁 catch

catch (e) {
	print(e);
}

try 코드 블록 실행중에 예외가 발생했을 때 실행할 코드를 넣으며, 예외가 발생하지 않으면 실행되지 않는다.

✔ 생략할 수 없으며, 1개 이상이고 개수 제한이 없다.

✔ 발생한 예외의 객체가 () 에 들어간다.


📁 on

on IOException catch (e) {
	// IOException 이 발생했을 때 실행할 코드
}

try 코드 블록에서 발생하는 예외 중 특정 타입을 다룰 때 사용한다.

✔ 예외 처리 구문에 필수는 아니지만, 단독으로 사용할 수 없어 catch 와 같이 사용한다.

✔ 예외 타입이 특정되어 해당 타입이 가지고 있는 요소를 사용 가능하다.


📁 finally

finally {
	// 예외 처리 후 실행할 코드
}

✔ 예외 발생 여부와 상관없이 실행할 코드를 작성한다.
try, catch 를 모두 거친 후에 실행되며, 필수 요소는 아니다.


❕ 예시

try {
	String invalidNumber = "abc";
	int result = int.parse(invalidNumber);
	print(result); // 실행 X
} catch (e) {
	print(e); // FormatException: abc
}

try {
	String invalidNumber = "abc";
	int result = int.parse(invalidNumber);
	print(result);
} on FormatException catch (e) {
	print(e); // FormatException 발생 시
} catch (e) {
	print(e); // FormatException 이 아닌 다른 예외가 발생 시
} finally {
  print('예외 처리 끝 -'); // 무조건 실행
}

✅ 예외를 의도적으로 발생시키기 위한 경우

📁 throw
특정 경우에 예외를 의도적으로 발생시키기 위해 Exception 클래스나 Error 클래스를 통해 생성한 객체를 던진다.


🔍 throw 를 통해 발생된 예외는 try, catch, on 으로 처리할 수 있다.

void printAge(int age) {
  if (age < 0) {
    throw Exception('나이는 0 이상이어야 해요 !');
  }
  print(age); // 실행 X
}
//
void main() {
  try {
    printAge(-8);
  } catch (e) {
    print(e);
  }
}

✏️ 오류

❗ 프로그램 외부의 환경이나 시스템에서 발생하는 문제

🔍 모든 종류의 오류는 Error 클래스를 상속 받는다.


🔍 오류가 발생하면 프로그램이 정상적으로 실행되지 않기 때문에 발생하지 않도록 한다.


🔍 발생을 예상하거나 처리할 수 없어서 프로그램을 종료한다.


📋 Dart 오류 종류

📁 ArgumentError

✔ 허용되지 않는 인자를 함수에 전달했을 때 발생


📁 RangeError

var numbers = [1, 2, 3];
print(numbers[3]); // 없음

ArgumentError 를 상속받은 클래스로, 허용되지 않는 범위의 값이 매개변수로 전달되었을 때 발생한다.


📁 IndexError

var numbers = [1, 2, 3];
print(numbers[-1]); // 음수 불가
print(numbers[5]);

✔ 허용되지 않는 범위의 Index 를 사용했을 때 발생하며, RangeError 클래스를 상속받는다.


📁 AssertionError

assert(101 < 100);

assert() 문의 매개변수에 전달하는 조건문이 거짓일 때 발생


assert()
✔ 주로 논리적인 오류를 잡아낼 때 사용
✔ 매개변수에 넣은 조건문이 참이면 정상적으로 실행이 계속 되고, 조건문이 거짓이면 예외 AssertionError 를 발생시킨다.


👉 assert([조건문]);

assert(text != null); // null이면 오류 발생
assert(urlString.startsWith('https'));

startsWith() 는 문자열이 특정 값으로 시작하는지 판별하는 함수이다.


👉 assert([조건문], '[메시지]');

assert(number < 100, '100 이상이면 안 돼요 !');

✔ 조건문이 거짓이면 오른쪽 메시지 출력


📁 NoSuchMethodError

✔ 함수나 메서드를 실행할 수 없는 경우에 발생

👉 함수는 객체에 의존적이지 않고, 메서드는 객체에 의존적이다. 따라서 호출할 때 함수는 객체를 통하지 않지만, 메서드는 객체를 통해야 한다.


📁 OutOfMemoryError

✔ 시스템 메모리 부족으로 더이상 할당할 수 없을 때 발생


📁 StackOverflowError

✔ 스택 오버플로우(Stack Overflow) 때 발생하는데, 메모리 공간 중 하나인 스택(Stack) 영역의 수용 가능한 범위를 넘어가는 현상을 말한다.


🔸 메모리 공간

코드(Code)

👉 프로그램에서 실행할 코드를 담는 공간
👉 프로그램 시작부터 종료까지 메모리에 남아있다.


데이터(Data)

👉 전역 변수나 정적 변수를 담는 공간
👉 프로그램 시작부터 종료까지 메모리에 남아있다.


힙(Heap)

👉 개발자가 직접 할당하고, 해제할 수 있는 동적인 공간
👉 프로그램 실행 중에 크기가 결정된다.


스택(Stack)

👉 지역 변수나 매개 변수를 담는 공간
👉 그 변수가 속한 코드 블록의 실행이 끝나면 메모리에서 사라진다.


📁 StateError

List<int> numbers = [];
print(numbers.first); // 리스트가 비어있음

✔ 객체가 현재 특정 동작 수행이 불가능한 경우에 발생


📁 TypeError

String b = '2';
print(b as double);

✔ 상한 타입과 타입이 맞지 않은 경우에 발생하는데, as 는 변환할 때 사용하는 타입 체크 연산자이다.


📁 UnimplementedError

class Dog {
  void bark() {
    print('멍멍 !');
  }
}
void main() {
  Dog dog = Dog();
  dog.run(); // 없음
}

✔ 구현되지 않은 메서드나 기능을 호출했을 때 발생


📁 UnsupportedError

const numbers = [1, 2, 3];
numbers.add(4); // const에 add 불가

✔ 객체를 통해 실행될 수 없는 동작일 때 발생


📋 사용자 오류

✅ Dart 오류가 아닌 커스텀한 오류를 사용하고 싶을 때 Error 클래스를 상속 받는 새로운 오류 클래스를 만든다.


class AgeError extends Error {
  final String message;  
  AgeError(this.message);
}
class AgeError extends Error {
  final String? message;  
  AgeError(this.message);
//  
  
  String toString() => message ?? 'AgeError 가 발생했어요 !';
}

⚡ 예외 vs 오류

예외 (Exception)오류 (Error)
발생 원인프로그래밍 로직 문제
(ex. 논리적 오류, 비정상적인 입력)주로 시스템 문제
(ex. 메모리 부족)
예상 가능 여부OX
처리 가능 여부O (try-catch)X
프로그램 정상 실행 가능 여부OX (비정상 종료)

🔥 라이브러리 확장

✅ 특정 기능이나 작업을 쉽고 빠르게 수행할 수 있도록 미리 작성한 코드를 모아둠.

✏️ dart.dev

📋 dart SDK 표준 라이브러리

🔍 플랫폼에 따라 사용할 수 있는 라이브러리가 다르다.

✅ 모든 플랫폼 (Native platform + Web platform) 에서 사용 가능한 라이브러리
📁 dart:core

✔ 타입 (ex. int, double, String), 콜렉션 등 핵심 기능을 지원
✔ Dart 프로그램에 포함되어 있어서 따로 import 하지 않아도 된다.


📁 dart:async

Future , Stream 과 같은 클래스를 통해 비동기 프로그래밍을 지원


📁 dart:collection

dart:core 에서 제공하지 않는 무거운 콜렉션 타입을 제공
queue, linked list, hashmap, binary tree


📁 dart:convert

✔ 서로 다른 데이터 표현 방식을 가진 데이터(ex. JSON, UTF-8) 를 전환하기 위한 인코더(Incoder)와 디코더(Decoder)를 제공


📁 dart:developer

✔ 디버거(Debugger) 같은 개발자 도구와 상호작용


📁 dart:math

✔ 수학 상수와 함수, 난수 생성 등의 수학 관련 기능


✅ Native platform(Mobile, Desktop)에서 사용 가능한 라이브러리

📁 dart:ffi

✔ Dart*가 C API 를 사용할 수 있도록 하는 기능


📁 dart:io

✔ 파일, 소켓, HTTP, 기타 입출력 등의 기능을 제공


✅ Web platform(JavaScript 로 컴파일되는 코드)에서 사용 가능한 라이브러리

📁 package:web

✔ 가벼운 브라우저 API 와 연결하는 기능


📁 dart:js_interop

✔ JavaScript와 브라우저 API를 상호운용할 수 있는 기능


📁 dart:html

✔ HTML 요소들과 Web 기반 응용 프로그램 리소스들을 제공


✏️ pub.dev

📋 외부 라이브러리

📁 cupertino_icons

✔ Flutter의 Cupertino 위젯에 쓰이는 기본 아이콘 에셋을 제공


📁 intl

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


📁 shared_preferences

✔ 간단한 데이터를 다루는 기능
✔ iOS와 macOS에서 사용하는 NSUserDefaults, Android에서 사용하는 SharedPreferences 와 같은 역할


📁 url_launcher

✔ URL을 다루는 기능


📁 image_picker

✔ 사진 다루는 기능
✔ 카메라로 사진 찍는 기능이랑 앨범 사진 선택 기능 등


📁 firebase_core

✔ Firebase와 연동하는 작업할 때 핵심 기능을 제공


📁 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 네트워크 사용


💡 location

✔ 크로스 플랫폼을 개발할 때 위치에 대해, 위치 기반으로 작업을 할 때 사용


💡 flutter_animate

✔ 플러터 관련해서 애니메이션을 다룰 때 사용


💡 toggle_switch

✔ 위치 되는 버튼을 만들 때 사용


✏️ Import

📋 import '[라이브러리 이름]';

import 'package:http/http.dart'

📋 import '[파일 경로]';

import 'src/my_utils.dart';

📁 as

import 'package:http/http.dart' as http;

✔ 별칭 부여


📁 show

import 'package:lib1/lib1.dart' show foo;

✔ 선택


📁 hide

import 'package:lib2/lib2.dart' hide foo;

✔ 제외


📁 deferred as

📋 import ‘[라이브러리 이름이나 파일 경로]’ deferred as [식별자];

import 'package:greetings/hello.dart' deferred as hello;

✔ 라이브러리가 필요한 시점에 로드(지연 로딩)
Web platform 에서만 지원


import 'package:greetings/hello.dart' deferred as hello;
//
Future<void> greet() async {
  await hello.loadLibrary();
  hello.printGreeting();
}

deferred as 뒤에 썼던 식별자를 통해 loadLibrary() 를 호출하여 사용한다.

await 는 실행이 끝날 때까지 다음 작업을 못 하도록 하며, await hello.loadLibrary(); 때문에 라이브러리가 로드될 때까지 멈춰있는다.

deferred as 를 통해 식별자를 만들어 주는 순간, loadLibrary() 가 자동으로 정의된다.


🔥 비동기 프로그래밍

✏️ 동기 프로그래밍(Synchronous Programming)

✅ 작업이 완료될 때까지 기다렸다가 결과가 나오면 값을 반환

❗ 오래 걸리는 작업의 경우

❕ 파일로부터 데이터를 읽을 때
❕ 데이터 베이스에 데이터를 쓸 때
❕ 네트워크를 통해 데이터를 불러올 때

👉 이럴 때 필요한게 비동기 방식!


✏️ 비동기 프로그래밍(Asynchronous Programming)

🔍 작업이 완료될 때까지 기다리지 않고, 미래의 특정 시점에 값을 반환한다.


🔍 결과값이 나올 때까지 멈춰 있지 않고, 수행할 수 있는 다른 작업을 찾아서 수행한다.


🔍 결과값이 나올 때까지 다른 작업을 계속 할 수 있기 때문에 시간이 오래 걸리는 작업을 처리할 때 사용한다.


⚡ Future

💭 비동기 프로그래밍에서 사용하는 클래스로 dart:async에 포함되어 있는 클래스

💡 import dart:async; 를 해야 Future 클래스를 사용할 수 있지만, dart:core 가 사용할 수 있도록 만들어 따로 dart:async 를 import 하지 않아도 된다.


Future<int> number = Future.value(1);
Future<String> name = Future.value('MIni');
Future<bool> isOddNumber = Future.value(1.isOdd);

✔ 결과값의 타입은 Future 로, 실행이 완료되었을 때 결과값을 반환하고, 실행을 종료한다.

Future<타입> 형태를 보면 제네릭 클래스라는 것을 알 수 있다.


✅ 하나의 작업에 대해 한 번 발생하는 이벤트나 값에 대해 한 번 발생하는 단일 비동기 작업에 사용한다.


📋 Future.delayed(Duration(seconds: [지연 시간]));

void main() {
	int seconds = 2;
	print('실행 시작 !');
	Future.delayed(Duration(seconds: seconds));
  print('실행 끝 !');
}
/*
실행 시작 !
실행 끝 !
*/

📋 Future.delayed(Duration(seconds: [지연 시간]), () {[지연 시간 후의 동작]});

void main() {
	int seconds = 2;
	print('실행 시작 !');
	Future.delayed(Duration(seconds: seconds), () {
    print('$seconds초 기다림 !');
  });
  print('실행 끝 !');
}
/*
실행 시작 !
2초 기다림 !
실행 끝 !
*/

비동기 프로그래밍

void introduce(String name) async {
	print('$name의 자기소개');
//  
  await Future.delayed(Duration(seconds: 2), () {
    print('안녕 ? 나는 $name');
  });	
	print('$name의 자기소개 끝 !');
}
//
void main() {
  introduce('나');
}
/*
나의 자기소개
안녕 ? 나는 나
나의 자기소개 끝 !
*/

✔ 비동기적인 코드 앞에 await 를 붙이고,
await 를 붙인 코드를 포함하는 함수에 async 를 붙인다.


🔍 코드를 순서대로 출력하고 싶다면?

void introduce(String name) async {
	print('$name의 자기소개');
//  
  await Future.delayed(Duration(seconds: 2), () {
    print('안녕 ? 나는 $name');
  });	
	print('$name의 자기소개 끝 !');
}
//
void main() {
  introduce('미니');
  introduce('코드');
}
/*
미니의 자기소개
코드의 자기소개
안녕 ? 나는 미니
미니의 자기소개 끝 !
안녕 ? 나는 코드
코드의 자기소개 끝 !
*/

await 의 딜레이 2초를 기다리면서 다음 코드를 실행한다.
✔ 비동기 방식으로 다음 코드가 실행되는 것을 알 수 있다.


🔍 2개의 작업을 순서대로 출력하고 싶다면?

Future<void> introduce(String name) async {
	print('$name의 자기소개');
//  
  await Future.delayed(Duration(seconds: 2), () {
    print('안녕 ? 나는 $name');
  });	
	print('$name의 자기소개 끝 !');
    return '이름은 $name'; //반환값이 필요한 경우 작성
}
//
void main() async {
  await introduce('hayancode');
  await introduce('Mini');
}
/*
hayancode의 자기소개
안녕 ? 나는 hayancode
hayancode의 자기소개 끝 !
Mini의 자기소개
안녕 ? 나는 Mini
Mini의 자기소개 끝 !
*/

awaitFuture 류의 코드 앞에만 사용할 수 있기 때문에 introduce() 의 반환 타입을 Future<void> 로 바꿔주어야 한다.

💡 main()async 를 붙이고, introduce() 는 반환값이 없기 때문에 return 구문이 없기 때문에
Future<void> 타입으로 한다.

await 는 여러개 있어도 상관 없다.


⚡ Stream

💭 비동기 프로그래밍에서 사용하는 클래스로 dart:async에 포함되어 있는 클래스

Future 와 동일하게 따로 import 하지 않아도 사용 가능하다.


✅ 시간에 따라 연속적인 데이터를 제공하는데, 한 번에 하나의 값이 아닌 여러 값을 비동기적으로 받는다.


Steam<int> number = Steam.value(1);
Steam<String> name = Steam.value('Mini');
Steam<bool> isOddNumber = Steam.value(1.isOdd);

✔ 결과값은 Stream 타입이며, 동일하게 제네릭 클래스이다.

✔ 계속 실행되기 때문에 직접 실행을 종료해줘야 한다.


✅ 하나의 작업에 대해 값이나 이벤트가 여러 번 발생하는 경우나 비동기 연산의 결과값이 여러 번 반환되는 경우 그 값을 순차적으로 받기 위해 사용한다.


📁 yield

Stream<String> emitNames(List<String> names) async* {
  for (var i = 0; i < names.length; i++) {
    yield '${i + 1}번째는 ${names[i]} ~';
  }
}

Stream 을 타입으로 갖는 함수는 async* 을 사용해 값을 방출한다.

✔ 값을 방출한다는 의미에서 return과 비슷하다고 생각해도 될 것 같다.


📁 listen()

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 = ['Mini', 'hayancode', '김영민'];
  emitNames(names).listen((element) {
    print(element);
  });
}
/*
1번째는 Mini
2번째는 hayancode
3번째는 김영민
*/

yield 를 통해 방출되는 값을 받기 위해 사용하는 메서드이다.


비동기 프로그래밍

🔍 코드

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(number);
  });
}
/*
10
9
8
7
6
5
4
3
2
1
0
*/

yield 를 통해 값을 방출하고, listen 을 사용해서 값을 받아 사용한다.


🌱 오늘은 과제 제출 마감 전에 기능을 추가해보고, 과제 해설을 들었다.

그리고 남은 강의 3강을 마저 들어서 완강했는데, 제일 어려운 부분이었던 것 같다.
여기서 오류처리를 배우는데, 강의랑 과제 시간이 촉박하다 보니까 오늘 들을 강의를 남겨두고 진행했었는데 이 부분을 학습했어야 했다.

물론 다른 방법으로 예외 처리를 해서 안전하게 잘 돌아가도록 코드를 작성했지만 이런 방법도 있구나 선택지가 넓어진 느낌이다.

라이브러리는 아는 내용이라서 그냥 종류가 많구나 하면서 알게되었다면, 동기 비동기 이부분이 생각보다 어려웠다.

코드를 여기 붙이는 이유와 붙였을때 비동기, 비동기의 동기 방식 이런게 조금 헷갈렸던 것 같다.
그래도 정리하면서 중간중간 멈추고 이해하는 시간을 가져서 어느정도 알고 넘어갔다.


🚀 다음주에는 새로운 dart 심화? 강의와 개인 과제가 있다고 하는데 좀.. 걱정이 되기도 하고.. 이번주 기초도 생각보다 어렵고 학습하는데 오래 걸렸는데 심화는 얼마나 어려울지 상상이 안간다.

그리고 그 다음부터는 팀프로젝트를 진행하려나..? 좀 걱정이 된다. 과제를 하면서 느낀 것은 사람마다 코드를 짜는 방식이 좀 다르고, 더 편하게 느끼는 로직이 있을텐데 팀 프로젝트를 하면서 서로 이게 낫지 않냐는 충돌이 발생하면 그걸 다시 코드를 고쳐야하는지? 걱정..? 궁금증도 있고..💫

profile
💻 [25.05.26~] Flutter 공부중⏳

0개의 댓글