Dart 튜토리얼 11편: dart:core & dart:async 실전(문자열·컬렉션·Future/Stream)

요약#

핵심 요지#

  • 문제 정의: 기본 타입(String1, List2)과 비동기(Future3, Stream4)를 각각 따로 배우면, 실제 코드를 조립할 때 어디서부터 시작해야 할지 막힌다.
  • 핵심 주장: Dart는 모든 프로그램에 자동으로 포함되는 dart:core5 위에, 비동기 실행을 위한 dart:async6를 얹어 “기본 타입 + 비동기 흐름”을 한 언어 경험으로 만든다.
  • 주요 근거: print()7 같은 기본 I/O부터 int.parse()[^int-parse], RegExp8, 컬렉션 타입, 그리고 async9/await10 기반의 FutureStream 사용 예시가 함께 제공된다.
  • 실무 기준: 먼저 dart:core의 문자열/컬렉션 처리 패턴을 잡고, 다음으로 Future의 에러 핸들링(catchError()11, whenComplete()12)을 “초기에 등록”하는 습관을 들인다.

문서가 설명하는 범위#

  • dart:core의 문자열/정규식, 숫자 처리, 컬렉션 사용 예시
  • dart:asyncFuture/Stream 개요와 async/await 스타일
  • Future 체인에서의 에러 처리(catchError(), whenComplete()) 패턴과 주의점

읽는 시간: 15분 | 난이도: 초급


참고 자료#


문제 상황#

입문자 입장에서는 “문자열 처리”와 “비동기 처리”가 서로 다른 챕터처럼 보입니다.
하지만 실제 앱 코드는 문자열을 파싱하고, 리스트를 만들고, 네트워크/파일 작업을 기다리는 흐름이 한 함수 안에서 같이 등장합니다.
이 조합을 처음부터 같이 묶어두지 않으면, 코드를 짜다가 Future 체인에서 에러가 사라지거나, Stream을 어디서 받아야 하는지 헷갈릴 수 있습니다.


해결 방법#

단계 1: dart:core로 “기본 데이터 처리”를 빠르게 고정하기#

Why#

NOTE

비동기를 배우기 전에, 데이터가 “어떤 형태로 들어오고 나가는지”가 먼저 정리되어야 합니다.
특히 문자열/숫자/컬렉션 처리는 거의 모든 코드의 바닥입니다.

print('I drink $tea.');

What#

NOTE

dart:core는 모든 Dart 프로그램에 자동으로 포함되는 기본 라이브러리입니다.
문자열, 숫자, 컬렉션 같은 핵심 타입과 기본 함수가 여기에 들어 있습니다.

How#

TIP

문자열과 숫자 변환은 “파싱 → 검증” 패턴으로 자주 쓰입니다.

assert(int.parse('42') == 42);
assert(int.parse('0x42') == 66);
assert(double.parse('0.50') == 0.5);
// 16진수처럼 radix를 명시할 수도 있다.
assert(int.parse('42', radix: 16) == 66);

문자열은 검색/분해/치환 패턴이 반복됩니다.

// 포함 여부/시작/끝/인덱스
assert('Never odd or even'.contains('odd'));
assert('Never odd or even'.startsWith('Never'));
assert('Never odd or even'.endsWith('even'));
assert('Never odd or even'.indexOf('odd') == 6);
// 부분 문자열/분해
assert('Never odd or even'.substring(6, 9) == 'odd');
var parts = 'progressive web apps'.split(' ');
assert(parts.length == 3);
// 정규식 치환
var greetingTemplate = 'Hello, NAME!';
var greeting = greetingTemplate.replaceAll(RegExp('NAME'), 'Bob');
assert(greeting != greetingTemplate);

Watch out#

WARNING

문자열 인덱싱과 codeUnitsUTF-1613 단위로 동작합니다.
즉, “문자 하나 = 인덱스 하나”라고 단정하기 전에, 처리 단위를 먼저 확인해야 합니다.

// UTF-16 코드 유닛 리스트를 얻는다.
var codeUnitList = 'Never odd or even'.codeUnits.toList();
assert(codeUnitList[0] == 78);

결론: dart:core의 기본 패턴(파싱/치환/분해)을 먼저 고정하면, 비동기 코드에서도 데이터 흐름이 흔들리지 않습니다.


단계 2: dart:asyncFuture를 “읽기 쉬운 흐름”으로 만들기#

Why#

NOTE

네트워크/파일 작업은 결과가 “나중에” 옵니다.
그래서 코드가 “대기 → 다음 작업” 흐름으로 이어질 수 있어야 합니다.

myFunc().then(processValue).catchError(handleError);

What#

NOTE

Future는 “나중에 완료되는 작업의 결과”를 표현합니다.
then()14으로 다음 작업을 연결하고, catchError()로 에러 흐름을 처리합니다.

How#

TIP

then() 체인은 길어지기 쉽습니다.
그래서 같은 흐름을 async/await로 바꾸면 읽기가 쉬워집니다.

Future<void> runUsingAsyncAwait() async {
var entryPoint = await findEntryPoint();
var exitCode = await runExecutable(entryPoint, args);
await flushThenExit(exitCode);
}

에러는 try/catch로 한 덩어리로 묶을 수 있습니다.

var entryPoint = await findEntryPoint();
try {
var exitCode = await runExecutable(entryPoint, args);
await flushThenExit(exitCode);
} catch (e) {
// 예외를 처리한다.
}

Watch out#

WARNING

에러 핸들러 등록이 늦으면, 에러가 이미 발생한 뒤라 잡지 못할 수 있습니다.
즉, Future를 만든 뒤 “나중에” catchError()를 붙이는 패턴은 피하는 편이 안전합니다.

Future<Object> future = asyncErrorFunction();
// 나쁜 예: 에러 핸들러 등록이 너무 늦다.
Future.delayed(const Duration(milliseconds: 500), () {
future.then(...).catchError(...);
});

결론: Future는 “읽기 쉬운 연결”과 “빠른 에러 처리 등록”이 핵심입니다.


단계 3: Future 체인의 에러 처리 패턴을 “결과/정리”까지 포함해 완성하기#

Why#

NOTE

실패가 나면 “로그를 남기고 끝”이 아니라, 연결된 후속 작업의 진행 여부와 자원 정리까지 결정해야 합니다.
그래서 catchError()whenComplete()를 함께 보는 편이 좋습니다.

final server = connectToServer();
server
.post(myUrl, fields: const {'name': 'Dash', 'profession': 'mascot'})
.then(handleResponse)
.catchError(handleError)
.whenComplete(server.close);

What#

NOTE

catchError()는 에러를 처리하거나 값을 반환해 흐름을 이어갈 수 있습니다.
whenComplete()는 성공/실패와 무관하게 실행할 “마무리 작업”을 등록합니다.

How#

TIP

아래 예시는 중간에 에러가 나면 catchError()로 값을 만들어 “다음 then”을 계속 진행합니다.

Future<String> one() => Future.value('from one');
Future<String> two() => Future.error('error from two');
Future<String> three() => Future.value('from three');
Future<String> four() => Future.value('from four');
void main() {
one()
.then((_) => two())
.then((_) => three())
.then((_) => four())
.then((value) => value.length)
.catchError((e) {
print('Got error: $e');
return 42;
})
.then((value) {
print('The value is $value');
});
}

Watch out#

WARNING

then()onErrorcatchError()를 섞을 때는, “어느 에러가 어디로 흘러가는지”가 헷갈릴 수 있습니다.
특히 then(..., onError: ...) 안에서 또 다른 비동기 함수를 호출하면, 그 에러는 뒤의 catchError()로 넘어갈 수 있습니다.

asyncErrorFunction()
.then(
successCallback,
onError: (e) {
handleError(e); // 원래 에러
anotherAsyncErrorFunction(); // 여기서 생긴 새 에러
},
)
.catchError(handleError);

결론: Future 에러 처리는 “흐름을 계속할지/멈출지”와 “항상 정리할지”까지 함께 결정해야 합니다.

Footnotes#

  1. String(스트링): 문자열을 표현하는 타입이다.

  2. List(리스트): 순서가 있는 컬렉션 타입이다.

  3. Future(퓨처): 나중에 완료되는 작업의 결과를 표현하는 타입이다.

  4. Stream(스트림): 시간이 지나면서 여러 이벤트/값이 들어오는 흐름을 표현하는 타입이다.

  5. dart(dart): 모든 Dart 프로그램에 자동으로 포함되는 핵심 라이브러리다.

  6. dart(dart): Future/Stream 등 비동기 기능을 제공하는 라이브러리다.

  7. print()(프린트): 객체의 문자열 표현을 콘솔에 출력하는 함수다.

  8. RegExp(정규식): 문자열 패턴을 표현하는 정규식 타입이다.

  9. async(어싱크): 함수가 비동기이며 await10를 사용할 수 있음을 표시하는 키워드다.

  10. await(어웨이트): Future3 완료를 기다렸다가 다음 코드를 진행하게 하는 키워드다. 2

  11. catchError()(캐치 에러): Future3 에러를 처리하는 핸들러를 등록하는 메서드다.

  12. whenComplete()(웬 컴플리트): 성공/실패와 무관하게 실행할 마무리 작업을 등록하는 메서드다.

  13. UTF-16(UTF-16): 문자열을 16비트 단위 코드 유닛으로 표현하는 문자 인코딩 방식이다.

  14. then(덴): Future3 완료 후 실행할 콜백을 등록하는 메서드다.

공유

이 글이 도움이 되었다면 다른 사람과 공유해주세요!

Dart 튜토리얼 11편: dart:core & dart:async 실전(문자열·컬렉션·Future/Stream)
https://moodturnpost.net/posts/dart/dart-core-async-practice/
작성자
Moodturn
게시일
2026-01-04
Moodturn

목차