Dart의 모든 예외는비검사 예외로, 함수에 던질 타입을 선언할 필요가 없으며 반드시 포착해야 하는 규칙도 없습니다.
크게 두 가지로 분류됩니다.
Exception:업무 로직상 예측 가능한 오류(포맷 오류, 매개변수 이상 등 포착 후 처리하는 것을 권장)Error:프로그램 치명적 오류(타입 불일치, 널 포인터, 미구현 메서드 등 일반적으로 포착하지 않음)
널이 아닌 모든 객체는 throw로 던질 수 있지만 실무 프로젝트 규칙상 오직 Exception/Error 하위 클래스만 던집니다.
포착되지 않은 예외는 현재 아이솔레이트(isolate)를 종료시키고 프로그램이 크래시됩니다.
throw로 예외 던지기
1. 시스템 내장 예외 던지기
void main() {
// 포맷 예외
throw FormatException("데이터 구간이 최소 한 개 이상 필요합니다");
}
Code language: Dart (dart)
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Unhandled exception:
FormatException: 오류 설명
#0 main (file:///D:/dartdemo/firstdart/bin/firstdart.dart:3:3)
#1 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:314:19)
#2 _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:193:12)Code language: PHP (php)
위 코드는 직접 설명 메시지를 작성한 내장 예외를 던지며 실행 시 오류 메시지를 출력합니다. 처음 보면 활용도가 낮다고 느낄 수 있지만 예외를 던지는 기능은 개발 과정에서 매우 중요합니다. 예를 들어 회원가입 로직에서 사용자 입력값 규칙을 검증하고 기준에 맞지 않는 경우 즉시 예외를 던져 흐름을 중단하고 사용자에게 오류를 알림으로써 잘못된 데이터가 시스템에 유입되는 것을 막고 데이터 규격을 유지할 수 있습니다.
2. 임의 객체 던지기
운영 환경 사용을 권장하지 않습니다
void main() {
throw "텍스트 오류 메시지!";
}Code language: Dart (dart)
실행 결과
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Unhandled exception:
텍스트 오류 메시지!
#0 main (file:///D:/dartdemo/firstdart/bin/firstdart.dart:2:3)
#1 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:314:19)
#2 _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:193:12)Code language: PHP (php)
3. 화살표 함수 내 throw 사용(throw는 표현식으로 취급)
class Point {}
void distanceTo(Point other) => throw UnimplementedError("해당 메서드는 구현되지 않았습니다");
void main() {
distanceTo(Point());
}
Code language: JavaScript (javascript)
3. try /on/catch로 예외 포착하기
문법 차이점 정리
on 예외타입:지정한 타입의 예외만 매치, 예외 객체에 접근 불가catch(e):모든 예외를 포착, 변수 e에 예외 인스턴스 저장catch(e, s):e = 예외 객체, s = 호출 스택 정보 StackTrace
1:on으로 단일 지정 타입 예외 포착
// Exception을 구현한 커스텀 예외 클래스 정의
class OutOfLlamasException implements Exception {}
// 위 커스텀 예외를 던지는 함수
void breedMoreLlamas() {
throw OutOfLlamasException();
}
void buyMoreLlamas() {
print("buyMoreLlamas 함수가 실행되었습니다");
}
void main() {
try {
breedMoreLlamas(); // 위험한 로직 실행
} on OutOfLlamasException {
buyMoreLlamas(); // OutOfLlamasException 포착 시 실행
}
}Code language: Dart (dart)
실행 결과
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
buyMoreLlamas 함수가 실행되었습니다Code language: CSS (css)
try {
// 오류가 발생할 가능성 있는 코드
} on 예외클래스 {
// 오류 포착 후 실행할 로직
}Code language: Dart (dart)
try on은 타입별 예외를 포착하기 위한 전용 문법입니다.
2:다중 분기 계층적 포착
정확한 매치 구문이 우선 실행됨
class OutOfLlamasException implements Exception {}
void breedMoreLlamas() {
throw FormatException("데이터 포맷 오류");
}
void main() {
try {
breedMoreLlamas();
}
// 1. 커스텀 예외 정확 매치
on OutOfLlamasException {
print("OutOfLlamasException 오류 발생");
}
// 2. 모든 Exception 하위 클래스 포착
on Exception catch (e) {
print("일반 업무 예외:$e");
}
// 3. Error 및 기타 임의 던진 객체 포착하는 기본 분기
catch (e) {
print("알 수 없는 심각 오류:$e");
}
}
Code language: Dart (dart)
출력 결과 : 일반 업무 예외:FormatException: 데이터 포맷 오류
여러 개의 on 구문으로 특정 Exception을 개별 포착할 수 있으며 마지막 catch로 모든 케이스를 보충할 수 있습니다.
3:예외 포착 + 호출 스택 StackTrace 출력
void main() {
try {
int a = null;
print(a + 1);
} catch (e, stack) {
print("예외 정보:$e");
print("전체 호출 스택:\n$stack");
}
}
Code language: Dart (dart)
이 코드는 오류 메시지뿐만 아니라 전체 함수 호출 경로 스택도 출력하여 디버깅에 활용할 수 있습니다.
D:\dartdemo\firstdart>dart run
Building package executable...
Failed to build firstdart:firstdart:
bin/firstdart.dart:3:13: Error: A value of type 'Null' can't be assigned to a variable of type 'int'.
int a = null;Code language: PHP (php)
4. rethrow 재던지기
일부 로직 처리 후 상위로 예외 전달
역할:현재 함수에서 일부 복구 로직을 처리한 뒤 호출자에게 예외를 다시 넘겨 추가 처리하도록 함
현재 함수 내에서만 오류 처리를 마치는 것이 아니라 호출한 상위 코드에서도 별도 처리해야 하는 경우 사용합니다.
void misbehave() {
try {
dynamic foo = true;
print(foo++); // true는 증감 연산 불가, 런타임 오류 발생
} catch (e) {
print("misbehave 내 지역 처리:${e.runtimeType}");
rethrow; // 다시 던져 외부 try에서 포착
}
}
void main() {
try {
misbehave();
} catch (e) {
print("main 최종 처리:${e.runtimeType}");
}
}
Code language: Dart (dart)
실행 흐름:
misbehave 내 지역 처리:NoSuchMethodError
main 최종 처리:NoSuchMethodError
위 예시는 misbehave 함수와 main 함수 두 곳에서 모두 예외 처리 로직이 실행됩니다.
5. finally 최종 블록
오류 발생 여부와 상관없이 무조건 실행
두 가지 사용 시나리오:
- try + finally만 사용:예외를 포착하지 않고 finally 실행 후 프로그램 크래시
- try + catch + finally:오류를 먼저 처리한 뒤 finally 실행(리소스 해제 전용)
1:try + finally
void cleanLlamaStalls() => print("리소스 정리, 무조건 실행되는 보조 로직");
void breedMoreLlamas() => throw Exception("오류가 발생했습니다");
void main() {
try {
breedMoreLlamas();
} finally {
cleanLlamaStalls();
}
}
Code language: Dart (dart)
정리 로직은 실행되지만 포착되지 않은 예외로 프로그램은 최종 크래시됩니다.
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
리소스 정리, 무조건 실행되는 보조 로직
Unhandled exception:
Exception: 오류가 발생했습니다
#0 breedMoreLlamas (file:///D:/dartdemo/firstdart/bin/firstdart.dart:2:27)
#1 main (file:///D:/dartdemo/firstdart/bin/firstdart.dart:6:5)
#2 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:314:19)
#3 _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:193:12)Code language: PHP (php)
2:try + catch + finally
파일 열기 후 닫는 등 리소스 해제 표준 문법
void cleanLlamaStalls() => print("파일 닫기 / 점유 리소스 해제");
void breedMoreLlamas() => throw FormatException("파싱 실패");
void main() {
try {
breedMoreLlamas();
} catch (e) {
print("예외 포착:$e");
} finally {
cleanLlamaStalls();
}
}
Code language: Dart (dart)
실행 순서:예외 발생 → catch에서 오류 처리 → finally 실행
실행 결과
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
예외 포착:FormatException: 파싱 실패
파일 닫기 / 점유 리소스 해제
6. assert 단정문
개발 디버깅 단계에서만 작동, 운영 환경에서는 완전히 비활성화
assert(불린 조건, [선택적 오류 메시지])
조건이 false일 경우 AssertionError를 던집니다. 운영 빌드에서는 완전히 무시되며 성능 부하가 전혀 없습니다.
1:기본 단정 예시
void main() {
String? text;
int number = 120;
String url = "http://foxdevelop.com";
assert(text != null); // 널이 아닌지 검증
assert(number < 100); // 숫자 범위 검증
assert(url.startsWith("https")); // 링크 프로토콜 검증
}
Code language: JavaScript (javascript)
이 코드를 그대로 실행하면 아무 출력도 나오지 않습니다!
기본적으로 단정 기능이 꺼져있기 때문에 실행 플래그로 직접 활성화해야 합니다. 아래 내용을 참고하세요.
2:커스텀 오류 메시지 지정
void main() {
String url = "http://test.com";
assert(
url.startsWith("https"),
"링크 $url은 https로 시작해야 합니다",
);
}
Code language: JavaScript (javascript)
단정 실패 시 표시할 오류 메시지를 직접 작성할 수 있습니다.
단정 활성화 방법
- Flutter Debug 모드:기본적으로 단정 활성화
- dart 명령 실행:
dart --enable-asserts 파일이름.dart - 운영 빌드:모든 단정 코드가 제거되며 성능 저하 없음
D:\dartdemo\firstdart\bin>dart --enable-asserts firstdart.dart
Unhandled exception:
'file:///D:/dartdemo/firstdart/bin/firstdart.dart': Failed assertion: line 6 pos 10: 'text != null': is not true.
#0 _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:67:4)
#1 _AssertionError._throwNew (dart:core-patch/errors_patch.dart:49:5)
#2 main (file:///D:/dartdemo/firstdart/bin/firstdart.dart:6:10)
#3 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:314:19)
#4 _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:193:12)Code language: PHP (php)
dart run과 단순 dart 명령은 기본적으로 단정이 비활성화되므로 --enable-asserts 플래그를 추가해야 합니다.
실행 명령 예시
dart run --enable-asserts main.dartCode language: CSS (css)
터미널 경로가 프로젝트 루트일 경우 간략하게 실행 가능:
실행 명령:
dart run --enable-asserts
dart run:Dart 소스 직접 실행, 개발 디버깅용
dart 파일명:단일 파일 간편 실행 구문
dart compile exe:독립 실행 파일 컴파일, 컴파일 결과물은 언제나 assert 비활성화
Dart 소스는 프로젝트 내 bin 폴더에 위치하므로 bin으로 진입해 단일 파일을 실행하거나 상위 프로젝트 루트에서 전체 프로젝트를 실행할 수 있습니다.
dart run –enable-asserts
아래는 bin 폴더에서 직접 실행하는 방식과 프로젝트 루트에서 실행하는 두 가지 예시입니다.
D:\dartdemo\firstdart\bin>dart --enable-asserts firstdart.dart
Unhandled exception:
'file:///D:/dartdemo/firstdart/bin/firstdart.dart': Failed assertion: line 6 pos 10: 'text != null': is not true.
#0 _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:67:4)
#1 _AssertionError._throwNew (dart:core-patch/errors_patch.dart:49:5)
#2 main (file:///D:/dartdemo/firstdart/bin/firstdart.dart:6:10)
#3 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:314:19)
#4 _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:193:12)
D:\dartdemo\firstdart\bin>dart run
Building package executable...
Built firstdart:firstdart.
D:\dartdemo\firstdart\bin>cd ..
D:\dartdemo\firstdart>dart run --enable-asserts
Building package executable...
Built firstdart:firstdart.
Unhandled exception:
'file:///D:/dartdemo/firstdart/bin/firstdart.dart': Failed assertion: line 6 pos 10: 'text != null': is not true.
#0 _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:67:4)
#1 _AssertionError._throwNew (dart:core-patch/errors_patch.dart:49:5)
#2 main (file:///D:/dartdemo/firstdart/bin/firstdart.dart:6:10)
#3 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:314:19)
#4 _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:193:12)Code language: PHP (php)
단정 조건이 true일 경우 예외가 발생하지 않습니다.
예시 코드 수정:url의 http를 https로 변경, text != null을 text == null로 수정, number 값을 99로 변경
void main() {
String? text;
int number = 99;
String url = "https://foxdevelop.com";
assert(text == null); // 널 검증
assert(number < 100); // 숫자 범위 검증
assert(url.startsWith("https")); // 링크 프로토콜 검증
}Code language: Dart (dart)
다시 실행하면 아무 예외도 발생하지 않습니다.
D:\dartdemo\firstdart>dart run --enable-asserts
Building package executable...
Built firstdart:firstdart.Code language: CSS (css)
7. 종합 예제 코드
void cleanResource() => print("finally:점유 리소스 해제");
void testError() {
try {
int num = 150;
assert(num < 100, "숫자는 100보다 작아야 합니다");
throw FormatException("데이터 포맷이 올바르지 않음");
} on FormatException catch (e) {
print("포맷 예외 포착:$e");
rethrow;
} finally {
cleanResource();
}
}
void main() {
try {
testError();
} catch (e) {
print("main에서 재던진 예외 포착:$e");
}
}
Code language: PHP (php)
실행 결과
D:\dartdemo\firstdart>dart run --enable-asserts
Building package executable...
Built firstdart:firstdart.
finally:점유 리소스 해제
main에서 재던진 예외 포착:'file:///D:/dartdemo/firstdart/bin/firstdart.dart':
Failed assertion: line 6 pos 12: 'num < 100': 숫자는 100보다 작아야 합니다Code language: PHP (php)
전체 예외 오류 처리 흐름 예시