하리Hari

10. 오류 처리하기 (일단 해보자)

오류가 나도 프로그램이 멈추지 않게 받아 내요.

이 장에서 배울 것

  • 오류가 나면 프로그램이 어떻게 되는지
  • 오류를 받아 내기 (일단 해보자:, 오류가 발생했다면:)
  • 직접 오류 내기 (발생시키자)
  • 무슨 일이 있어도 실행하기 (마무리는 항상:)

오류가 나면 프로그램이 멈춰요

지금까지 본 오류들은 모두 그 자리에서 프로그램을 멈췄어요. 아래 코드는 <숫자로>가 "열"을 숫자로 바꾸지 못해 멈춰요. 마지막 줄은 실행되지 않아요.

'수'를 <숫자로>("열")로 정하자
"이 줄은 실행되지 않아요."를 출력하자

사용자가 숫자 대신 글자를 입력하는 것처럼, 일어날 수 있다고 예상되는 오류에 프로그램 전체가 멈추면 곤란하겠죠. 이럴 때 오류를 받아 내는 안전망을 쳐 둘 수 있어요.


안전망: 일단 해보자 / 오류가 발생했다면

일단 해보자:
    '수'를 <숫자로>("열")로 정하자
    "바꾸기 성공!"을 출력하자
오류가 발생했다면 ('에러'):
    "숫자로 바꿀 수 없는 값이에요."를 출력하자
"프로그램은 계속돼요."를 출력하자
  1. 일단 해보자: 아래 묶음을 실행해요.
  2. 그 안에서 오류가 나면 바로 멈추고 오류가 발생했다면: 아래 묶음으로 가요. ("바꾸기 성공!"은 실행되지 않아요.)
  3. 오류가 없었다면 오류가 발생했다면: 묶음은 건너뛰어요.
  4. 어느 쪽이든 그다음 줄부터 프로그램은 계속 실행돼요.

괄호 안의 '에러'에는 일어난 오류가 담겨요. '에러'의 '메시지'로 무슨 오류인지 볼 수 있어요. 필요 없다면 괄호 없이 오류가 발생했다면:만 써도 돼요.

일단 해보자:
    (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. 아래 코드에서 무엇이 어떤 순서로 출력될지 예상해 보세요.

일단 해보자:
    "가"를 출력하자
    새로운 [오류]("문제")를 발생시키자
    "나"를 출력하자
오류가 발생했다면:
    "다"를 출력하자
마무리는 항상:
    "라"를 출력하자
"마"를 출력하자
정답 보기

가, 다, 라, 마예요. 오류가 난 뒤의 나는 건너뛰어요.

다음 장에서는 하리에 미리 들어 있는 여러 도구(표준 라이브러리)를 꺼내 쓰는 법을 배워요.

놀이터
결과
코드를 작성하고 '실행' 버튼을 눌러보세요.