에러 처리: 예외와 assert
에러 처리: 예외와 assert
Dart 코드는 예외를 던지고(throw) 잡을 수(catch) 있어요. 예외는 예상하지 못한 일이 발생했음을 알리는 에러예요. 예외를 잡지 않으면 그 예외를 던진 [isolate]이 중단되고, 보통 그 isolate과 프로그램이 종료됩니다. 이 글에서는 예외를 던지고, 잡고, 정리하는 방법과 개발 중에 쓰는 assert 문까지 살펴볼게요.
예외
자바와 달리 Dart의 예외는 전부 unchecked 예외예요. 메서드는 어떤 예외를 던질 수 있는지 선언하지 않고, 여러분이 어떤 예외든 잡아야 할 의무도 없어요.
Dart는 Exception과 Error 타입과 그 외 수많은 미리 정의된 서브타입을 제공해요. 물론 자체 예외를 정의할 수도 있습니다. 게다가 Dart 프로그램은 Exception과 Error 객체뿐 아니라 null이 아닌 어떤 객체든 예외로 던질 수 있어요.
Throw
예외를 던지거나 raise 하는 예시를 볼게요.
throw FormatException('Expected at least 1 section');
임의의 객체도 던질 수 있어요.
throw 'Out of llamas!';
예외를 던지는 것은 표현식이기 때문에, => 문과 표현식을 쓸 수 있는 다른 어디에서든 예외를 던질 수 있어요.
void distanceTo(Point other) => throw UnimplementedError();
Catch
예외를 잡거나 capture하면(다시 던지지 않는 한) 예외가 전파되는 것을 막습니다. 예외를 잡으면 그것을 처리할 기회가 생겨요.
try {
breedMoreLlamas();
} on OutOfLlamasException {
buyMoreLlamas();
}
둘 이상의 타입 예외를 던질 수 있는 코드를 다루려면 여러 catch 절을 지정할 수 있어요. 던져진 객체의 타입과 일치하는 첫 catch 절이 그 예외를 처리합니다. catch 절이 타입을 지정하지 않으면 그 절은 어떤 타입의 던져진 객체든 처리할 수 있어요.
try {
breedMoreLlamas();
} on OutOfLlamasException {
// A specific exception
buyMoreLlamas();
} on Exception catch (e) {
// Anything else that is an exception
print('Unknown exception: $e');
} catch (e) {
// No specified type, handles all
print('Something really unknown: $e');
}
위 코드에서 보듯 on이나 catch 또는 둘 다를 쓸 수 있어요. 예외 타입을 지정해야 할 때는 on을 쓰고, 예외 처리기에 예외 객체가 필요할 때는 catch를 씁니다.
catch()에는 매개변수 하나 또는 둘을 지정할 수 있어요. 첫 번째는 던져진 예외이고, 두 번째는 스택 트레이스(StackTrace 객체)예요.
try {
// ···
} on Exception catch (e) {
print('Exception details:\n$e');
} catch (e, s) {
print('Exception details:\n$e');
print('Stack trace:\n$s');
}
예외를 부분적으로 처리하면서 전파되도록 두려면 rethrow 키워드를 사용해요.
void misbehave() {
try {
dynamic foo = true;
print(foo++); // Runtime error
} catch (e) {
print('misbehave() partially handled ${e.runtimeType}.');
rethrow; // Allow callers to see the exception.
}
}
void main() {
try {
misbehave();
} catch (e) {
print('main() finished handling ${e.runtimeType}.');
}
}
Finally
예외가 던져지든 아니든 어떤 코드가 실행되게 하려면 finally 절을 써요. 일치하는 catch 절이 없으면 finally 절이 실행된 뒤 예외가 전파됩니다.
try {
breedMoreLlamas();
} finally {
// Always clean up, even if an exception is thrown.
cleanLlamaStalls();
}
finally 절은 일치하는 catch 절들 다음에 실행돼요.
try {
breedMoreLlamas();
} catch (e) {
print('Error: $e'); // Handle the exception first.
} finally {
cleanLlamaStalls(); // Then clean up.
}
Assert
개발 중에는 assert(<condition>, <optionalMessage>); 문을 사용해 불리언 조건이 거짓이면 정상 실행을 방해할 수 있어요.
// Make sure the variable has a non-null value.
assert(text != null);
// Make sure the value is less than 100.
assert(number < 100);
// Make sure this is an https URL.
assert(urlString.startsWith('https'));
assert에 메시지를 붙이려면 assert의 두 번째 인자로 문자열을 추가해요(선택적으로 [trailing comma] 포함).
assert(
urlString.startsWith('https'),
'URL ($urlString) should start with "https".',
);
assert의 첫 인자는 불리언 값으로 평가되는 어떤 표현식이든 될 수 있어요. 표현식의 값이 true면 assert는 성공하고 실행은 계속됩니다. false면 assert는 실패하고 AssertionError 예외가 던져져요.
assert가 정확히 언제 동작할까요? 그것은 사용하는 도구와 프레임워크에 따라 달라져요.
- Flutter는 [debug mode]에서 assert를 활성화한다.
webdev serve같은 개발 전용 도구는 보통 기본으로 assert를 활성화한다.dart run이나dart compile js같은 일부 도구는--enable-asserts커맨드라인 플래그로 assert를 지원한다.
운영 코드에서는 assert가 무시되고, assert의 인자도 평가되지 않아요.
더 알아보기
- 동시성의 isolate — 예외가 isolate에 미치는 영향
- core 라이브러리 예외 문서 — 예외 관련 자세한 내용
- branches — 이전 문서 (조건 분기)