더북(TheBook)

2.12 예외 처리

프로그램이 동작하다 에러가 발생한 상황을 예외(exception)라고 합니다. 예를 들어 0으로 정수를 나누는 것은 불가능하므로 다음 코드를 실행하면 에러(예외)가 발생합니다.

> var = 10 / 0
(...)
ZeroDivisionError: division by zero

프로그래밍을 하다 보면 에러는 항상 따라다니지만 문제는 예외가 발생하면 프로그램이 비정상적으로 종료되어 버립니다. 갑자기 의도치 않은 순간에 비정상적인 종료가 일어나면 시스템 운영에 큰 걸림돌이 됩니다. 예를 들어 트레이딩 시스템을 동작시키는데 갑자기 예외가 발생하여 매도해야 할 종목을 매도하지 못한다면 이는 금전적인 손해로 이어질 수 있습니다. 따라서 예외가 발생했을 때는 프로그램 전체를 종료시키지 않고 적절한 처리를 하는 방법이 필요한데, 이를 예외 처리(try-except)라고 합니다. 그러면 예외 처리 사용 방법을 알아보겠습니다.

try:
    실행할 코드 ------ 예외가 발생할 수도 있는 영역
except Exception:
    예외가 발생하면 처리하는 코드 ------ 예외가 발생하면 처리할 부분

try라는 키워드 밑에는 실행할 코드를 작성하고, except 키워드 밑에는 try 블록 코드를 실행하다 예외가 발생하면 이를 처리하는 코드를 작성합니다. try 밑에 등장하는 코드 블록은 조건문이나 반복문을 사용했던 때처럼 들여쓰기를 해야 합니다.

except 다음에 오는 Exception이란 파이썬 내 정의된 예외입니다. 파이썬 내에는 다양한 내장 예외가 존재하며 파이썬 공식 문서(https://docs.python.org/ko/3/library/exceptions.html)에서 확인할 수 있습니다. 다양한 예외가 발생하면 이를 세분화해서 예외마다 적절한 처리를 할 수도 있습니다. 예를 들어 다음과 같습니다.

try:
    var = 10 / 0
except ZeroDivisionError: ------ 0을 이용해서 나누려고 할 때 발생하는 예외
    print("0으로 나눌 수 없습니다.")
except MemoryError: ------ 메모리 문제로 발생하는 예외
    print("메모리 에러입니다.")

이와 같이 try는 중복해서 사용할 수 없지만 try 다음에 등장하는 except는 예외별로 구분해서 중복하여 사용할 수 있습니다. 첫 번째로 등장하는 ZeroDivisionError라는 예외는 숫자를 0으로 나누려고 할 때 발생하고, MemoryError는 메모리 문제가 생겼을 때 발생하는 예외입니다. 이 코드에서는 정수 100으로 나누려고 한 것이므로 ZeroDivisionError 예외가 발생하고, except ZeroDivisionError에서 예외를 처리합니다. 따라서 프로그램을 실행해 보면 발생한 예외가 except ZeroDivisionError에서 처리되어 다음 결과가 출력됩니다.

0으로 나눌 수 없습니다.

다음과 같이 try 구문 안에서 발생하는 해당 예외에 대한 except 처리가 없다면, except 구문이 있더라도 적절한 예외 처리가 되지 않아 프로그램이 비정상 종료됩니다.

> try:
>     var = 10 / 0
> except MemoryError: ------ 메모리 문제로 발생하는 예외
>     print("메모리 에러입니다.")
(...)
ZeroDivisionError: division by zero

이렇게 try 안에서 발생할 수 있는 예외를 정확히 알지 못하면 try-except 구문을 사용하는 것이 무의미할 수 있습니다. 하지만 그렇다고 발생할 수 있는 모든 예외 처리를 할 수도 없는 노릇입니다. 이때 모든 예외를 커버할 수 있는 Exception 사용으로 예외를 처리할 수 있습니다.

> try:
>     var = 10 / 0
> except Exception:
>     print("예외가 발생했습니다.")
예외가 발생했습니다.

코드를 동작시키면 except Exception에서 예외를 처리하여 “예외가 발생했습니다.”가 출력되는 것을 알 수 있습니다. Exception에서 모든 예외를 처리할 수 있는 이유는 예외들도 계층 구조를 이루는 객체이기 때문입니다. 여기서 Exception이 다른 모든 예외의 부모이기 때문에 모든 예외를 대신 처리할 수 있습니다.

Exception을 이용하면 모든 예외를 처리할 수 있는 장점이 있지만, 정확히 무슨 예외가 발생했는지 파악하기는 어렵습니다. 이 경우 다음과 같이 처리하면 예외 내용을 알 수 있습니다.

try:
    코드
except Exception as 변수:
    print(변수)

발생한 예외는 as를 이용하여 변수로 지정합니다. 이후 해당 변수를 출력하면 에러 내용을 알 수 있습니다. 그러면 발생한 에러를 e라는 변수로 명명하여 출력해 보겠습니다.

> try:
>     var = 10 / 0
> except Exception as e:
>     print("예외가 발생했습니다.")
>     print(e)
예외가 발생했습니다.
division by zero

발생한 예외가 division by zero라는 것을 확인할 수 있습니다.

신간 소식 구독하기
뉴스레터에 가입하시고 이메일로 신간 소식을 받아 보세요.