모든 검증 오류 보고하기

FxDart 승

요구사항

2026-08 배치의 가입 폼 다섯 건을 세 가지 규칙(이름 존재, 이메일에 @ 포함, 나이 18세 이상)으로 검사합니다. 한 폼이 여러 규칙을 어길 수 있습니다 — 폼마다 어긴 규칙을 전부 보고한 뒤, 통과한 폼들을 나열하세요. 데이터는 코드에 들어 있습니다; 두 버전 모두 예상 출력 아래에 표시된 줄들을 출력해야 합니다.

예상 출력
form #2: name is required; email is malformed; must be 18 or older
form #4: email is malformed
form #5: name is required; must be 18 or older
valid: ana, cy

나란히 보기

RxDart

FxDart

차이가 나는 이유

누적 검증은 Rx 오류 채널이 구조적으로 해낼 수 없는 일입니다. 스트림 오류는 정확히 객체 하나를 싣고, 그것을 내보내는 순간 스트림이 끝납니다 — 처음 어긴 규칙을 올리면 그 폼의 나머지 실패도, 남은 폼들도 영영 보지 못합니다. 모든 복구 연산자(onErrorReturn, onErrorResumeNext)는 그 오류-하나-그리고-끝 모양을 위해 만들어져 있습니다. 그래서 여기 보이는 동작하는 RxDart 버전은 조용히 오류 채널을 버립니다: 각 폼을 데이터 채널 위의 실패 레코드로 매핑합니다 — 그 시점에 스트림이 기여하는 것은 비동기 maintoList 하나뿐입니다.

FxDart 쪽은 래퍼를 벗겨 낸 같은 아이디어입니다: 오류는 평범한 값이므로, 동기 map + partition이 실패한 폼들(오류 전부와 함께)과 유효한 폼들을 표현식 하나로 내놓습니다. 그리고 FxDart는 오류를 어디서나 값으로 다루기 때문에 이 패턴은 레코드를 넘어 확장됩니다: 타입 있는 오류 레이어가 누적을 대신해 줍니다 — zipOrAccumulate는 모든 규칙을 실행해 실패를 NonEmptyList로 이어 붙이고, mapOrAccumulate는 컬렉션 전체를 fail-slow로 검증합니다. 그 완전한 버전은 accumulate 튜토리얼을 보세요; 스트림 모델에는 손을 뻗을 상대가 없습니다.

벤치마크

Apple M1 Max, RAM 32 GB · Dart 3.12.2 (AOT 컴파일) · 2026-08-18

N = 100

시간 무승부

RxDart 125 µs
FxDart 27 µs

최대 메모리 RxDart 승

RxDart 15.5 MB
FxDart 16.5 MB

N = 1,000,000

시간 FxDart 승

RxDart 1094.5 ms
FxDart 303.4 ms

최대 메모리 RxDart 승

RxDart 345.3 MB
FxDart 369.0 MB

막대는 사이드별로 새 프로세스에서 반복 측정한 중앙값입니다(작은 N은 타이머 해상도를 위해 배치 처리). 두 사이드가 서로 5% 이내이거나 — 사람이 지각할 수 없는 차이인 0.6ms 이내이면 — 무승부로 칩니다. 상대 차이가 근소한 경우는 최대 5회까지 다시 측정합니다. 앱에서는 어느 막대가 짧든 몇 밀리초 이하의 차이는 사용자에게 보이지 않습니다. 메모리는 프로세스 최대 RSS입니다. Dart VM과 데이터셋은 양쪽이 동일하므로, 두 막대의 차이가 곧 파이프라인 자체가 붙들고 있는 양입니다.