Flutter에서 비즈니스 로직을 작성하다 보면,에러 처리, nullable 값 처리, 조건 분기 처리가 반복되며 코드가 복잡해지기 시작한다. 이런 복잡한 흐름을 깔끔하게 다루기 위해 등장한 것이 바로 dartz 패키지이다. 이 글에서는 dartz의 핵심 개념인 Either, Option, fold, map 등을 실제 앱 개발에서 자주 마주치는 예제 중심으로 설명한다.
dartz는 Dart에서 함수형 프로그래밍 스타일을 적용할 수 있게 해주는 라이브러리다.
주요 제공 기능
• Either<L, R>: 실패/성공 분기 표현
• Option<T>: 값이 있을 수도, 없을 수도 있는 값 표현 (null 대체)
• fold(), map(), flatMap(): 함수형 방식의 데이터 처리
기본 구조
Either<L, R>
예제: 사용자 정보를 가져오는 함수
Either<Failure, User> getUser(String id) {
if (id == '404') {
return Left(ApiFailure(message: '사용자를 찾을 수 없음', statusCode: 404));
} else {
return Right(User(id: id, name: '홍길동'));
}
}
사용: fold로 처리
final result = getUser('123');
result.fold(
(failure) => print('실패: ${failure.message}'),
(user) => print('성공: ${user.name}'),
);
fold(leftFn, rightFn)은 성공/실패 분기를 깔끔하게 처리하는 함수형 패턴이다.
✅ ResultFuture — dartz + async 응답 조합
typedef ResultFuture<T> = Future<Either<Failure, T>>;
typedef ResultVoid = ResultFuture<void>;
실제 네트워크 요청 등을 처리할 때 비동기 + 실패 대응이 모두 필요하므로
이런 식의 typedef를 사용하면 실무에서 매우 유용하다.
예시:
ResultFuture<User> fetchUser() async {
try {
final response = await dio.get(...);
return Right(User.fromJson(response.data));
} catch (e) {
return Left(ApiFailure(...));
}
}
Option의 종류
• Some(value) → 값이 있음
• None() → 값이 없음
Option<String> getNickname(User user) {
return user.nickname != null ? Some(user.nickname!) : none();
}
final result = getNickname(user);
result.fold(
() => print('닉네임 없음'),
(value) => print('닉네임: $value'),
);
final result = Right(10)
.map((value) => value * 2) // Right(20)
.flatMap((value) => Right(value + 1)); // Right(21)
result.fold(
(l) => print('실패'),
(r) => print('성공: $r'),
);
• ❌ if-else + try-catch 남발 → ✅ 함수형 분기로 대체
• ❌ null 체크 반복 → ✅ Option으로 안전하게 처리
• ❌ 예외 흐름 중첩 → ✅ Either로 명확한 흐름 분리
• ✅ 테스트에 강하고 유지보수에 유리한 구조 설계 가능
dartz는 단순히 특별한 문법을 제공하는 게 아니라
“실패를 타입으로 다룬다”, “값이 없을 수 있다는 것을 명시한다” 같은
함수형 프로그래밍의 철학을 Dart에 잘 녹여주는 라이브러리다.
실제 앱에서 상태관리, 레포지토리 패턴, API 처리 등에 응용하면
코드가 훨씬 깔끔하고 명확해진다.