38.3 예외 발생시키기

지금까지 숫자를 0으로 나눴을 때 에러, 리스트의 범위를 벗어난 인덱스에 접근했을 때 에러 등 파이썬에서 정해진 예외만 처리했습니다. 이번에는 우리가 직접 예외를 발생시켜 보겠습니다.

예외를 발생시킬 때는 raise에 예외를 지정하고 에러 메시지를 넣습니다(에러 메시지는 생략할 수 있음).

  • raise 예외('에러메시지')

그럼 3의 배수를 입력받은 뒤 숫자가 3의 배수가 아니면 예외를 발생시켜보겠습니다.

try_except_raise.py

try:
    x = int(input('3의 배수를 입력하세요: '))
    if x % 3 != 0:                                 # x가 3의 배수가 아니면
        raise Exception('3의 배수가 아닙니다.')    # 예외를 발생시킴
    print(x)
except Exception as e:                             # 예외가 발생했을 때 실행됨
    print('예외가 발생했습니다.', e)

소스 코드를 실행한 뒤 5를 입력하고 엔터 키를 누르세요.

실행 결과

3의 배수를 입력하세요: 5 (입력)
예외가 발생했습니다. 3의 배수가 아닙니다.

5는 3의 배수가 아니므로 raise Exception('3의 배수가 아닙니다.')로 예외를 발생시켰습니다. 이때 Exception에 넣은 에러 메시지는 except Exception as e:e에 들어갑니다.

그리고 raise로 예외를 발생시키면 raise 아래에 있는 코드는 실행되지 않고 바로 except로 넘어갑니다. 따라서 tryprint(x)는 실행되지 않습니다.

참고로 이 예제에서는 예외로 Exception을 사용했는데 RuntimeError, NotImplementedError 등 다른 예외를 사용해도 상관없습니다.

38.3.1  raise의 처리 과정

이번에는 raise의 처리 과정을 알아보겠습니다. 다음은 함수 안에서 raise를 사용하지만 함수 안에는 try except가 없는 상태입니다

try_except_function_raise.py

def three_multiple():
    x = int(input('3의 배수를 입력하세요: '))
    if x % 3 != 0:                                 # x가 3의 배수가 아니면
        raise Exception('3의 배수가 아닙니다.')    # 예외를 발생시킴
    print(x)                                       # 현재 함수 안에는 except가 없으므로
                                                   # 예외를 상위 코드 블록으로 넘김
 
try:
    three_multiple()
except Exception as e:                             # 하위 코드 블록에서 예외가 발생해도 실행됨
    print('예외가 발생했습니다.', e)

소스 코드를 실행한 뒤 5를 입력하고 엔터 키를 누르세요.

실행 결과

3의 배수를 입력하세요: 5 (입력)
예외가 발생했습니다. 3의 배수가 아닙니다.

three_multiple 함수는 안에 try except가 없는 상태에서 raise로 예외를 발생시켰습니다. 이렇게 되면 함수 바깥에 있는 except에서 예외가 처리됩니다. 즉, 예외가 발생하더라도 현재 코드 블록에서 처리해줄 except가 없다면 except가 나올 때까지 계속 상위 코드 블록으로 올라갑니다.

만약 함수 바깥에도 처리해줄 except가 없다면 코드 실행은 중지되고 에러가 표시됩니다. 다음은 파이썬 셸에서 직접 three_multiple 함수를 호출했으므로 except가 없는 상태입니다.

>>> three_multiple()
3 배수를 입력하세요: 5 (입력)
Traceback (most recent call last):
  File "<pyshell#5>", line 1, in <module>
    three_multiple()
  File "C:\project\try_except_function_raise.py", line 4, in three_multiple
    raise Exception('3의 배수가 아닙니다.')    # 예외를 발생시킴
Exception: 3 배수가 아닙니다.

38.3.2  현재 예외를 다시 발생시키기

이번에는 try except에서 처리한 예외를 다시 발생시키는 방법입니다. except 안에서 raise를 사용하면 현재 예외를 다시 발생시킵니다(re-raise).

  • raise

다음은 three_multiple 코드 블록의 예외를 다시 발생시킨 뒤 상위 코드 블록에서 예외를 처리합니다.

try_except_raise_in_except.py

def three_multiple():
    try:
        x = int(input('3의 배수를 입력하세요: '))
        if x % 3 != 0:                                 # x가 3의 배수가 아니면
            raise Exception('3의 배수가 아닙니다.')    # 예외를 발생시킴
        print(x)
    except Exception as e:                             # 함수 안에서 예외를 처리함
        print('three_multiple 함수에서 예외가 발생했습니다.', e)
        raise    # raise로 현재 예외를 다시 발생시켜서 상위 코드 블록으로 넘김
 
try:
    three_multiple()
except Exception as e:                                 # 하위 코드 블록에서 예외가 발생해도 실행됨
    print('스크립트 파일에서 예외가 발생했습니다.', e)

소스 코드를 실행한 뒤 5를 입력하고 엔터 키를 누르세요.

실행 결과

3의 배수를 입력하세요: 5 (입력)
three_multiple 함수에서 예외가 발생했습니다. 3의 배수가 아닙니다.
스크립트 파일에서 예외가 발생했습니다. 3의 배수가 아닙니다.

three_multiple 함수 안에서 발생한 예외를 함수 안의 except에서 한 번 처리하고, raise로 예외를 다시 발생시켜서 상위 코드 블록으로 넘겼습니다. 그다음에 함수 바깥의 except에서 예외를 처리했습니다. 이런 방식으로 같은 예외를 계속 처리해줄 수 있습니다.

참고로 raise만 사용하면 같은 예외를 상위 코드 블록으로 넘기지만 raise에 다른 예외를 지정하고 에러 메시지를 넣을 수도 있습니다.

  • raise 예외('에러메시지')
  •         if x % 3 != 0:
                raise Exception('3의 배수가 아닙니다.')
            print(x)
        except Exception as e:
            print('three_multiple 함수에서 예외가 발생했습니다.', e)
            raise RuntimeError('three_multiple 함수에서 예외가 발생했습니다.')
    
    참고 | assert로 예외 발생시키기

    예외를 발생시키는 방법 중에는 assert를 사용하는 방법도 있습니다. assert는 지정된 조건식이 거짓일 때 AssertionError 예외를 발생시키며 조건식이 참이면 그냥 넘어갑니다. 보통 assert는 나와서는 안 되는 조건을 검사할 때 사용합니다.

    다음은 3의 배수가 아니면 예외 발생, 3의 배수이면 그냥 넘어갑니다.

    assert 조건식

    assert 조건식, 에러메시지

    assertion.py

    x = int(input('3의 배수를 입력하세요: '))
    assert x % 3 == 0, '3의 배수가 아닙니다.'    # 3의 배수가 아니면 예외 발생, 3의 배수이면 그냥 넘어감
    print(x)
    


    실행 결과

    3의 배수를 입력하세요: 5 (입력)
    Traceback (most recent call last):
      File "C:\project\assertion.py", line 2, in <module>
        assert x % 3 == 0, '3의 배수가 아닙니다.'
    AssertionError: 3의 배수가 아닙니다.
    

    assert는 디버깅 모드에서만 실행됩니다. 특히 파이썬은 기본적으로 디버깅 모드이며(__debug__의 값이 True) assert가 실행되지 않게 하려면 python-O 옵션을 붙여서 실행합니다(영문 대문자 O).

    python -O 스크립트파일.py