27. Python에서는 구조적인 예외 처리를 제공합니다.

안녕하세요. 언휴예요.

프로그래밍을 하다 보면 프로그램의 다양한 상황에 따라 처리할 때가 생깁니다. 특히 원하지 않는 상태에 도달하여 프로그램을 더 이상 진행하지 못하는 상황도 발생합니다. 그리고 사용자가 잘못 사용하여 어떻게 처리해야 할 지 결정하지 못할 때도 발생합니다.

이럴 때 간단한 조건문으로 해결할 수 있는 문제도 있지만 그렇지 않을 때도 있어요. Python에서는 예기치 못한 상황에 도달했을 때 문제를 해결하기 위한 방법으로 구조적인 예외 처리를 제공합니다.

구조적인 예외 처리를 살펴보기 전에 예외라는 용어를 간단히 짚고 넘어갈게요. 오류, 버그, 예외는 프로그램이 정상적이지 못한 상태에서 발생하는 것들입니다. 오류는 사용자가 잘못 사용하여 발생하는 문제이며 버그는 개발자가 프로그램 코드를 논리적으로 잘못 작성한 것입니다.

예외는 오류나 버그는 아니지만 프로그램 내부 혹은 시스템 내부 혹은 외부의 문제로 더 이상 진행하지 못하는 상황을 말합니다. 예를 들어 도서 판매 시스템에서 결제를 진행하려고 하는데 외부 결제 시스템이 먹통이라면 더 이상 진행하지 못할 거예요. 이는 사용자가 잘못 사용한 것도 아니고 프로그램의 논리적 버그도 아닙니다. 그럼에도 다른 요인으로 프로그램을 더 이상 진행하지 못하는 상황이 발생한 것이죠. 이러한 것을 예외라고 말합니다.

Python에서 구조적인 예외 처리를 위해 try, except, else, finally, raise 문을 제공하고 있어요.

Python에서는 예외 상황이 발생하면 raise 문을 이용하여 예외를 발생하고 있습니다. Python을 이용하여 프로그래밍을 할 때 내장 함수 혹은 라이브러리 내부에서 예외를 발생할 수도 있다는 것입니다. 대표적으로 0으로 나누려고 할 때 발생하는 Division by Zero 예외가 있어요.

만약 Python을 이용하여 프로그램을 작성할 때 내장 함수 혹은 라이브러리 내부에서 예외를 발생하였는데 아무런 처리를 하지 않으면 프로그램은 비정상적으로 종료합니다.

다음은 두 개의 정수를 입력받아 나눈 결과를 출력하는 코드입니다. 만약 사용자가 젯수를 0으로 입력한다면 Division by Zero 예외가 발생합니다. 그리고 아무런 예외 처리를 하지 않았기에 프로그램은 비정상적으로 종료합니다. [그림 1]은 디버그 모드에서 예외가 발생한 것을 확인하는 화면입니다.

#예외가 발생하였지만 아무런 처리를 하지 않을 때

a = int(input("정수:"))
b = int(input("정수:"))

print("나눈 결과:",a/b)
[그림 1] 예외가 발생했지만 예외 처리를 하지 않았을 때
[그림 1] 예외가 발생했지만 예외 처리를 하지 않았을 때

만약 예외가 발생하였을 때 프로그램을 종료하지 않고 개발자가 원하는 코드를 수행하길 원하면 try 문을 사용하세요. try문은 목적에 따라 try except, try except else, try finally를 사용할 수 있어요. 그리고 예외를 발생할 때는 raise 문을 사용합니다.

  • try expert

Python에서 구조적인 예외 처리를 위해 try문을 사용하면 예외가 발생하였을 때 except 문에서 예외 처리할 수 있어요.

try:

수행 코드

expert [예외 형식 [as 변수] ] :

예외 처리 구문

다음은 두 개의 정수를 입력 받아 나눈 결과를 출력하는 DemoA함수와 파일명을 입력 받아 파일 내용을 읽어 출력하는 DemoB함수를 수행하는 예제 코드입니다. 코드를 보면 try 문으로 DemoA와 DemoB 함수를 호출하고 있어요. 그리고 except문에서는 예외가 발생함을 콘솔 화면에 출력하고 있어요.

#try expert 예외 처리

def DemoA():
    a = int(input("정수:"))
    b = int(input("정수:"))
    print("나눈 결과:",a/b)

def DemoB():
    fname = input("파일명:")
    fs = open(fname,"r")
    data = fs.read()
    fs.close()
    print(data)

try:
    DemoA()
    DemoB()
except:
    print("예외 발생!!!")
print("테스트 종료")

다음은 DemoA 함수에서 예외(Division by Zero)가 발생하였을 때의 화면입니다.

[그림 2] DemoA 함수에서 예외가 발생할 때
[그림 2] DemoA 함수에서 예외가 발생할 때

다음은 DemoB함수에서 예외가 발생하였을 때의 화면입니다.

[그림 3] DemoB 함수에서 예외가 발생할 때
[그림 3] DemoB 함수에서 예외가 발생할 때

다음은 예외가 발생하지 않았을 때의 화면입니다.

[그림 4] 예외가 발생하지 않을 때
[그림 4] 예외가 발생하지 않을 때

 앞의 예에서는 expert 문에서 예외 종류에 관계없이 예외를 처리하였습니다. 그런데 예외 종류에 따라 예외 처리 구문을 다르게 수행하길 원하면 expert 문 뒤에 예외 종류를 명시하세요.

다음 예제 코드는 Division by Zero 예외와 File Not Found 예외, 그 외의 예외에 따라 처리하는 구문을 다르게 작성한 예제입니다.

#예외 종류에 따라 예외 처리

def DemoA():
    a = int(input("정수:"))
    b = int(input("정수:"))
    print("나눈 결과:",a/b)

def DemoB():
    fname = input("파일명:")
    fs = open(fname,"r")
    data = fs.read()
    fs.close()
    print(data)

try:
    DemoA()
    DemoB()
except ZeroDivisionError:
    print("Division by Zero!!!")
except FileNotFoundError:
    print("File Not Found!!!")
except:
    print("예외 발생!!!")
print("테스트 종료")
[그림 5] Division by Zero 예외 발생
[그림 5] Division by Zero 예외 발생

다음은 File Not Found 예외가 발생하였을 때의 화면입니다.

[그림 6] File Not Found 예외 발생
[그림 6] File Not Found 예외 발생

다음은 DemoA 함수 호출하였을 때 정수가 아닌 값을 입력하였을 때 예외가 발생하였을 때의 화면입니다. 예제 코드에서는 ZeroDivisionError와 FileNotFoundError 형식 예외를 처리하는 블록은 별도로 지정하였지만 그 외의 예외는 공통으로 예외 처리하게 작성하였음을 확인하세요.

[그림 7] DemoA 함수에서 정수가 아닌 값을 입력하였을 때
[그림 7] DemoA 함수에서 정수가 아닌 값을 입력하였을 때

필요하면 예외 형식 개체를 변수로 참조하여 사용할 수도 있어요.

#예외 개체 참조 변수 사용하기

def DemoA():
    a = int(input("정수:"))
    b = int(input("정수:"))
    print("나눈 결과:",a/b)

def DemoB():
    fname = input("파일명:")
    fs = open(fname,"r")
    data = fs.read()
    fs.close()
    print(data)

try:
    DemoA()
    DemoB()
except ZeroDivisionError as err:
    print(err)
except FileNotFoundError:
    print("File Not Found!!!")
except:
    print("예외 발생!!!")
else:
    print("어느 곳에서도 예외가 발생하지 않았어요.")
print("테스트 종료")
  • try except else

Python에서는 구조적인 예외 처리 구문을 작성할 때 예외가 발생하지 않았을 때만 처리할 구문을 위해 else문을 사용할 수 있어요.

#예외가 발생하지 않았을 때의 별도의 처리 구문 작성

def DemoA():
    a = int(input("정수:"))
    b = int(input("정수:"))
    print("나눈 결과:",a/b)

def DemoB():
    fname = input("파일명:")
    fs = open(fname,"r")
    data = fs.read()
    fs.close()
    print(data)

try:
    DemoA()
    DemoB()
except ZeroDivisionError:
    print("Division by Zero!!!")
except FileNotFoundError:
    print("File Not Found!!!")
except:
    print("예외 발생!!!")
else:
    print("어느 곳에서도 예외가 발생하지 않았어요.")
print("테스트 종료")
  • try finally

Python에서 구조적인 예외 처리를 할 때 예외가 발생하거나 예외가 발생하지 않거나 반드시 수행해야 할 구문이 있다면 finally 문을 사용하세요.

다음 코드는 입력한 파일을 열어 내용을 읽어 콘솔 화면에 출력하는 예제입니다. 파일을 열고 난 후에 파일을 읽는 도중에 어떠한 예외가 발생하거나 혹은 발생하지 않아도 파일을 닫아야 할 것입니다. 이처럼 예외가 발생하거나 발생하지 않거나 공통으로 수행해야 할 구문이 있으면 finally 문을 사용하세요.

#finally 사용

try:
    fname = input("파일명:")
    fs = open(fname,"r")
    try:
        data = fs.read()
        print(data)
    except:
        print("파일 읽기에서 예외가 발생했어요.")
    finally:
        fs.close()
except:
    print("File Not Found!!!")
print("테스트를 종료합니다.")
  • raise

이제까지는 발생한 예외를 처리하는 구문을 작성하는 방법을 살펴보았어요. 그런데 개발자가 작성하는 코드에서 더 이상 진행할 수가 없어 예외를 발생하길 원할 때도 있습니다. 이럴 때는 raise문을 사용하세요.

다음은 Demo 함수에서 자연수를 입력받아 반환하는 함수를 사용하는 예제 코드입니다. 만약 사용자가 0보다 작거나 같은 값을 전달하면 예외를 발생시키고 있어요. raise 문에 사용할 수 있는 형식은 반드시 예외 형식이어야 합니다.

#예외 발생하기

def Demo():
    num = int(input("자연수:"))
    if(num<=0):
        raise AssertionError
    else:
        return num

try:
    Demo()
except:
    print("예외 발생")