Dart 튜토리얼 6편: 에러 처리 전략(try/catch/throw/assert)
요약
핵심 요지
문서가 설명하는 범위
try/catch/on/finally문법과 사용 흐름throw,rethrow10로 예외를 발생·전파하는 방법assert로 개발 중 가정을 검증하는 방식
읽는 시간: 15분 | 난이도: 초급
참고 자료
- Error handling - exceptions, catch/on, rethrow, assertions
문제 상황
프로그램은 외부 입력과 환경에 영향을 받습니다.
파일이 없거나, 네트워크가 끊기거나, 예상과 다른 데이터가 들어올 수 있습니다.
이때 실패를 “없던 일”로 만들 수는 없으니, 실패를 어떻게 다룰지 규칙이 필요합니다.
Dart는 “예외(exception)“를 통해 오류 상황을 표현합니다.
예외를 잡지 않으면 호출 스택을 타고 위로 전파되고, 마지막까지 처리되지 않으면 실행이 멈출 수 있습니다.
해결 방법
입문자가 바로 쓸 수 있는 에러 처리 전략을 4단계로 정리합니다.
단계 1: try/catch로 실패를 잡고 계속 진행하기
Why
NOTE입력/환경은 항상 예측대로 움직이지 않습니다.
그래서 “실패할 수 있는 구간”을 표시하고, 실패했을 때의 흐름을 코드로 정해둬야 합니다.void main() {// 실패 가능: 문자열이 숫자가 아닐 수 있다.int.parse('not a number');}
What
NOTE
try/catch는 실패가 발생할 수 있는 코드를 감싸고, 실패를 한 곳에서 받아 처리하는 문법입니다.
How
TIP
try블록은 “실패할 수 있는 코드”를 감싸는 공간입니다.
catch는try안에서 발생한exception을 받아 처리합니다.
catch는 예외 객체와 함께StackTrace11도 받을 수 있습니다.void main() {try {final value = int.parse('not a number');print(value);} catch (e, s) {print('파싱 실패: $e');print('스택 트레이스: $s');}}
Watch out
WARNING
catch에서 예외를 “조용히” 무시하면, 문제를 늦게 발견할 수 있습니다.
최소한 실패가 있었다는 사실은 로그로 남기는 편이 안전합니다.try {int.parse('not a number');} catch (e) {print('실패를 무시하지 말고 기록합니다: $e');}
결론: 실패를 잡아 로그를 남기고, 프로그램 전체가 즉시 종료되는 것을 막을 수 있습니다.
단계 2: on으로 “특정 타입”만 골라서 처리하기
Why
NOTE예외를 한 덩어리로 처리하면, “예상한 실패”와 “예상하지 못한 실패”가 섞입니다.
그래서 먼저 “예상한 실패 타입”을 분리해 두는 편이 읽기 쉽습니다.// 숫자 파싱에서는 FormatException 같은 예외를 예상할 수 있다.
What
NOTE
on은 특정 예외 타입만 골라서 처리하는 문법입니다.
How
TIP
on을 사용하면 “특정 예외 타입”만 골라서 처리할 수 있습니다.
이 방식은 “어떤 실패를 예상했고, 어디서 처리할지”를 더 분명하게 만듭니다.void main() {try {final value = int.parse('not a number');print(value);} on FormatException catch (e) {print('형식이 맞지 않습니다: $e');} catch (e) {print('그 외 예외: $e');}}
Watch out
WARNING
on으로 잡지 못하는 예외도 있을 수 있습니다.
그래서 마지막에 일반catch를 두고, 예상 밖 실패를 한 곳으로 모으는 편이 안전합니다.
결론: 예상한 실패는 구체적으로 처리하고, 그 외 예외는 한 곳에서 모아 처리할 수 있습니다.
단계 3: finally로 성공·실패와 상관없이 마무리하기
Why
NOTE실패가 났을 때 가장 위험한 상황은 “정리 작업이 빠져서 상태가 꼬이는 것”입니다.
그래서 성공/실패와 무관하게 반드시 실행해야 하는 코드는 별도로 분리합니다.// 성공/실패와 무관하게 실행돼야 하는 정리 로직이 있다.
What
NOTE
finally는 예외 발생 여부와 상관없이 항상 실행되는 블록입니다.
How
TIP
finally는try가 성공하든 실패하든 항상 실행됩니다.
즉, 자원을 정리하거나 상태를 원래대로 돌려놓는 작업을 여기에 둡니다.void main() {try {print('작업 시작');throw Exception('문제 발생');} catch (e) {print('예외 처리: $e');} finally {print('항상 실행되는 정리 작업');}}
Watch out
WARNING
finally는 “항상 실행”되므로, 여기에서 예외가 나면 원래 예외를 가릴 수 있습니다.
따라서finally에는 정리 작업만 넣고, 복잡한 로직은 피하는 편이 안전합니다.
결론: 실패가 나더라도 정리 작업이 빠지지 않아 “중간에 멈춘 상태”를 줄일 수 있습니다.
단계 4: throw/rethrow로 실패를 표현하고 전파하기
Why
NOTE실패를 잡는 것만큼 중요한 것이 “실패를 올바르게 표현하는 것”입니다.
이 단계가 흐릿하면, 어디서 실패가 시작됐는지 추적하기 어렵습니다.void saveUser(String? name) {if (name == null) throw ArgumentError('name must not be null');}
What
NOTE
throw는 예외를 발생시키고,rethrow는 잡은 예외를 스택 정보를 유지한 채 다시 던집니다.
How
TIP
throw로 예외를 발생시키고, 필요하면 위로 전파할 수 있습니다.
또한rethrow는catch에서 받은 예외를 “다시 던지는” 용도입니다.
이때 스택 정보가 유지된다는 점이 중요합니다.void saveUser(String? name) {if (name == null) {throw ArgumentError('name must not be null');}// 저장 로직...}void main() {try {saveUser(null);} catch (e) {print('저장 실패: $e');}}
catch에서 로그를 남기고, 처리는 상위 계층에 맡기고 싶다면rethrow를 사용합니다.void runJob() {try {doJob();} catch (e) {print('여기서 기록만 하고 위로 올립니다: $e');rethrow;}}
Watch out
WARNING로그만 남기고 처리 책임을 위로 올리려면, 새 예외를 만들기보다
rethrow가 더 자연스럽습니다.
그래야 “어디서 처음 실패했는지”가 호출 경로에 남습니다.
결론: 실패를 “표현”(throw)과 “처리”(catch)를 분리해, 책임을 단계별로 나눌 수 있습니다.
개발 중에는 assert로 “가정”을 검증하기
assert는 개발 중 조건을 검증하는 도구입니다.
조건이 false이면 AssertionError12를 던지며, 도구/환경에 따라 활성화 여부가 달라질 수 있습니다.
또한 프로덕션에서는 assert가 무시되고, 인자 평가도 하지 않을 수 있습니다.
void main() { final text = 'Dart'; assert(text.isNotEmpty);}결론: “이 시점에는 반드시 성립해야 하는 조건”을 코드로 남겨, 개발 단계에서 더 빨리 실패를 발견할 수 있습니다.
Footnotes
-
exception(예외): 프로그램 실행 중 발생한 오류 상황을 표현하는 객체다. ↩
-
try(트라이): 예외가 발생할 수 있는 코드를 감싸는 블록이다. ↩
-
catch(캐치): 발생한 예외를 받아 처리하는 블록이다. ↩
-
on(온): 특정 예외 타입만 골라 처리하는 문법이다. ↩
-
finally(파이널리): 예외 발생 여부와 상관없이 항상 실행되는 블록이다. ↩
-
throw(스로우): 예외를 발생시키는 키워드다. ↩
-
Exception(예외 타입): 일반적으로 복구 가능한 오류 상황을 나타내는 예외 유형이다. ↩
-
Error(에러 타입): 보통 프로그램의 버그나 잘못된 사용처럼 즉시 수정이 필요한 상황을 나타내는 유형이다. ↩
-
assert(어설트): 개발 중 조건을 검증하고, 실패 시 예외를 발생시키는 문이다. ↩
-
rethrow(리스로우): catch에서 잡은 예외를 스택 정보를 유지한 채 다시 던지는 키워드다. ↩
-
StackTrace(스택 트레이스): 예외가 발생하기까지의 호출 경로를 담는 정보다. ↩
-
AssertionError(어설션 에러): assert 조건이 실패할 때 발생하는 예외다. ↩
공유
이 글이 도움이 되었다면 다른 사람과 공유해주세요!