10. 오류 처리하기 (일단 해보자)
오류가 나도 프로그램이 멈추지 않게 받아 내요.
이 장에서 배울 것
- 오류가 나면 프로그램이 어떻게 되는지
- 오류를 받아 내기 (
일단 해보자:,오류가 발생했다면:)- 직접 오류 내기 (
발생시키자)- 무슨 일이 있어도 실행하기 (
마무리는 항상:)
오류가 나면 프로그램이 멈춰요
지금까지 본 오류들은 모두 그 자리에서 프로그램을 멈췄어요. 아래 코드는 <숫자로>가 "열"을 숫자로 바꾸지 못해 멈춰요. 마지막 줄은 실행되지 않아요.
'수'를 <숫자로>("열")로 정하자
"이 줄은 실행되지 않아요."를 출력하자
사용자가 숫자 대신 글자를 입력하는 것처럼, 일어날 수 있다고 예상되는 오류에 프로그램 전체가 멈추면 곤란하겠죠. 이럴 때 오류를 받아 내는 안전망을 쳐 둘 수 있어요.
안전망: 일단 해보자 / 오류가 발생했다면
일단 해보자:
'수'를 <숫자로>("열")로 정하자
"바꾸기 성공!"을 출력하자
오류가 발생했다면 ('에러'):
"숫자로 바꿀 수 없는 값이에요."를 출력하자
"프로그램은 계속돼요."를 출력하자
일단 해보자:아래 묶음을 실행해요.- 그 안에서 오류가 나면 바로 멈추고
오류가 발생했다면:아래 묶음으로 가요. ("바꾸기 성공!"은 실행되지 않아요.) - 오류가 없었다면
오류가 발생했다면:묶음은 건너뛰어요. - 어느 쪽이든 그다음 줄부터 프로그램은 계속 실행돼요.
괄호 안의 '에러'에는 일어난 오류가 담겨요. '에러'의 '메시지'로 무슨 오류인지 볼 수 있어요. 필요 없다면 괄호 없이 오류가 발생했다면:만 써도 돼요.
일단 해보자:
(10 / 0)을 출력하자
오류가 발생했다면 ('에러'):
('에러'의 '메시지')를 출력하자
DivideByZeroError: 0으로 나눌 수 없어요.가 나와요. 하리가 낸 오류의 메시지는 앞에 오류 종류가 붙어요. 종류별 뜻은 부록. 자주 만나는 오류에 있어요.
실전: 입력을 안전하게 받기
2장에서 [숫자]로 입력받자에 글자를 넣으면 오류로 멈췄죠. 안전망으로 감싸면 다시 물어볼 수 있어요.
'나이'를 비어있음으로 정하자
('나이'가 비어있음과 같다)인 동안 반복하자:
"나이를 숫자로 입력하세요: "를 이어출력하자
일단 해보자:
'나이'를 [숫자]로 입력받자
오류가 발생했다면:
"숫자만 입력할 수 있어요. 다시 해 볼까요?"를 출력하자
틀"{'나이'}살이군요!"를 출력하자
숫자가 아닌 값을 넣으면 안내를 보여 주고 다시 물어봐요. 숫자를 넣으면 반복이 끝나요.
직접 오류 내기: 발생시키자
내가 정한 규칙을 어겼을 때 일부러 오류를 낼 수도 있어요. 새로운 [오류]("설명")를 발생시키자라고 쓰면 그 자리에서 오류가 나요.
<출금>을 만들자 ('잔액', '금액'):
만약 ('금액'이 '잔액'보다 크다) 라면:
새로운 [오류]("잔액이 부족해요.")를 발생시키자
('잔액' - '금액')를 돌려주자
일단 해보자:
(<출금>(1000, 300))을 출력하자
(<출금>(1000, 5000))을 출력하자
오류가 발생했다면 ('에러'):
틀"출금 실패: {'에러'의 '메시지'}"를 출력하자
700과 출금 실패: 잔액이 부족해요.가 나와요. 함수 안에서 낸 오류도 함수를 부른 곳의 일단 해보자가 받아요. 함수는 “이건 안 돼요”라고 알리기만 하고, 어떻게 대처할지는 부르는 쪽이 정하는 거예요.
무슨 일이 있어도: 마무리는 항상
오류가 났든 안 났든 꼭 실행해야 하는 일은 마무리는 항상: 아래에 써요. 정리하거나 “끝났어요”라고 알릴 때 써요.
일단 해보자:
"작업을 시작해요."를 출력하자
(10 / 0)을 출력하자
오류가 발생했다면:
"오류가 났어요."를 출력하자
마무리는 항상:
"작업을 마쳤어요."를 출력하자
(10 / 0) 줄을 지우고 다시 실행해 보세요. 오류가 없어도 작업을 마쳤어요.는 나와요.
언제 안전망을 쓸까요?
- 쓰면 좋을 때: 사용자 입력, 파일, 네트워크처럼 내가 막을 수 없는 문제가 생길 수 있을 때
- 먼저
만약으로 확인할 수 있을 때: 목록에 그 번째가 있는지, 사전에 그 키가 있는지, 나누는 수가 0인지처럼 미리 검사할 수 있다면만약이 더 분명해요.
'과일'을 ["사과", "배"]로 정하자
'번째'를 3으로 정하자
만약 ('번째'가 '과일'의 '길이' 이하이다) 라면:
('과일'의 '번째'번째)를 출력하자
그렇지 않다면:
"그런 번째는 없어요."를 출력하자
모든 코드를 안전망으로 감싸면 진짜 실수까지 조용히 묻혀 버려요. 꼭 필요한 곳에만 쓰세요.
나만의 오류 종류 (객체지향을 배운 뒤에)
오류의 종류에 따라 다르게 대처하고 싶을 때는 [오류]를 물려받은 나만의 오류를 만들고, [종류]가 발생했다면으로 골라 받을 수 있어요. 클래스와 상속을 쓰니까 객체지향 문법을 배운 뒤에 보세요.
[결제오류]는 [오류]를 바탕으로 하고 설계하자:
처음 만들어질 때 ('초기메시지') 다음과 같이 하자:
부모의 <처음 만들어질 때>('초기메시지')를 실행하자
[재고오류]는 [오류]를 바탕으로 하고 설계하자:
처음 만들어질 때 ('초기메시지') 다음과 같이 하자:
부모의 <처음 만들어질 때>('초기메시지')를 실행하자
일단 해보자:
새로운 [재고오류]("품절되었어요!")를 발생시키자
[결제오류]가 발생했다면 ('에러'):
"결제를 다시 확인해 주세요."를 출력하자
[재고오류]가 발생했다면 ('에러'):
틀"재고 문제: {'에러'의 '메시지'}"를 출력하자
오류가 발생했다면 ('에러'):
(참고) 위에서 받지 않은 나머지 오류는 모두 여기서 받아요.
('에러'의 '메시지')를 출력하자
종류로 골라 받는 것은 이렇게 직접 만든 오류 종류를 던졌을 때만 돼요. 10 / 0처럼 하리가 스스로 내는 오류는 맨 아래의 오류가 발생했다면으로만 받아요.
자주 하는 실수
1. 안전망 밖에서 오류가 나기
일단 해보자: 묶음 안에서 난 오류만 받아요. 오류가 날 수 있는 줄이 묶음 안에 들여 쓰여 있는지 확인하세요.
2. 오류가 난 뒤의 줄도 실행될 거라 생각하기
묶음 안에서 오류가 나면 그 아래 줄들은 건너뛰고 오류가 발생했다면으로 가요.
3. 모든 걸 안전망으로 감싸기
실수까지 숨겨져서 찾기 어려워져요. 예상되는 오류에만 쓰세요.
정리
| 하고 싶은 일 | 쓰는 법 |
|---|---|
| 오류 받아 내기 | 일단 해보자: … 오류가 발생했다면 ('에러'): |
| 무슨 오류인지 | '에러'의 '메시지' |
| 직접 오류 내기 | 새로운 [오류]("설명")를 발생시키자 |
| 항상 실행 | 마무리는 항상: |
연습 문제
1. 글자 "12a"를 숫자로 바꾸려고 해 보고, 실패하면 “숫자가 아니에요”를 출력하세요.
정답 예시
일단 해보자:
'수'를 <숫자로>("12a")로 정하자
'수'를 출력하자
오류가 발생했다면:
"숫자가 아니에요"를 출력하자
2. 나이를 받아서 0보다 작으면 “나이는 0보다 작을 수 없어요.” 오류를 내는 <나이확인>을 만들고, -5를 넣어서 그 오류를 받아 출력해 보세요.
정답 예시
<나이확인>을 만들자 ('나이'):
만약 ('나이'가 0보다 작다) 라면:
새로운 [오류]("나이는 0보다 작을 수 없어요.")를 발생시키자
"올바른 나이예요."를 돌려주자
일단 해보자:
(<나이확인>(-5))를 출력하자
오류가 발생했다면 ('에러'):
('에러'의 '메시지')를 출력하자
3. 아래 코드에서 무엇이 어떤 순서로 출력될지 예상해 보세요.
일단 해보자:
"가"를 출력하자
새로운 [오류]("문제")를 발생시키자
"나"를 출력하자
오류가 발생했다면:
"다"를 출력하자
마무리는 항상:
"라"를 출력하자
"마"를 출력하자
정답 보기
가, 다, 라, 마예요. 오류가 난 뒤의 나는 건너뛰어요.
다음 장에서는 하리에 미리 들어 있는 여러 도구(표준 라이브러리)를 꺼내 쓰는 법을 배워요.