Python Try Except: 예제로 설명

파이썬에서 try-except 구문은 프로그램 실행 중 예외 상황을 안전하게 처리하기 위한 핵심 메커니즘입니다. 예외 처리를 통해 코드의 안정성을 높이고 프로그램이 예상치 못한 오류로 인해 중단되는 상황을 방지할 수 있습니다. 본문에서는 예외 처리의 개념과 실제 적용 시나리오를 살펴보고, 예외를 직접 발생시키는 방법까지 상세히 다룰 것입니다.

예외 처리란 무엇인가?

예외란 프로그램이 실행되는 동안 발생하는 예상치 못한 상황이나 오류를 의미합니다. 만약 이러한 예외를 적절히 처리하지 못하면 프로그램은 비정상적으로 종료될 수 있습니다. 따라서 예외 처리는 프로그램이 예외 발생 시에도 멈추지 않고 계속 실행될 수 있도록 하는 중요한 기법입니다.

예외가 발생하는 상황을 예시를 통해 더 자세히 알아보겠습니다.

user_input = input("숫자를 입력하세요: ")
num = int(user_input)
print("입력하신 숫자의 두 배는:", num * 2)
  

위의 코드는 얼핏 보기에 별다른 문제가 없어 보입니다. 사용자로부터 입력을 받고, 그 값을 정수로 변환한 다음, 변환된 정수의 두 배를 출력하는 간단한 프로그램입니다. 만약 사용자가 “5”를 입력하면, 프로그램은 정상적으로 실행될 것입니다.

하지만 만약 사용자가 “5” 대신 “hello”와 같은 문자열을 입력하면 어떻게 될까요? 이때는 문자열 “hello”를 정수로 변환하는 과정에서 오류가 발생하며 프로그램이 예외를 발생시키고 종료됩니다.

예외가 발생하는 이유와 처리해야 하는 이유

프로그램은 일반적으로 여러 개의 함수로 구성되며, 각 함수는 특정 작업을 수행합니다. 함수들은 서로 호출하면서 데이터를 주고받고, 때로는 예상치 못한 상황에 마주칠 수 있습니다.

위의 예시 코드에서, 우리는 input() 함수를 호출하여 사용자 입력을 받고, int() 함수를 호출하여 문자열을 정수로 변환했습니다. 그리고 마지막으로 print() 함수를 호출하여 결과를 출력했습니다. 만약 int() 함수가 문자열을 정수로 변환하는 데 실패한다면, 오류가 발생했다는 신호를 프로그램에 전달해야 합니다. 이때 예외가 발생합니다.

예외는 호출된 함수가 오류가 발생했음을 호출한 코드에 알리는 통신 메커니즘이라고 할 수 있습니다. 이러한 예외를 제대로 처리하지 않으면 프로그램이 중단될 수 있으므로, 예외 처리는 안정적인 프로그램을 만드는 데 필수적입니다.

다양한 종류의 예외

모든 예외가 동일한 것은 아닙니다. 예외는 발생하는 오류의 유형에 따라 여러 종류로 나뉩니다. 예를 들어, 숫자를 0으로 나누려고 하면 ZeroDivisionError 예외가 발생하고, 잘못된 데이터 유형으로 연산을 시도하면 TypeError 예외가 발생합니다. 파이썬에는 다양한 예외 유형이 있으며, 전체 목록은 파이썬 공식 문서에서 확인할 수 있습니다.

예외를 처리하는 방법

앞서 언급했듯이 예외는 함수가 호출 코드에 보내는 오류 신호입니다. 따라서 우리는 코드 내에서 이러한 신호를 감지하고 적절히 대응해야 합니다. 파이썬에서는 try-except 구문을 사용하여 예외를 처리합니다. 이 구문의 기본 구조는 다음과 같습니다.

try:
    # 예외가 발생할 가능성이 있는 코드
except:
    # 예외가 발생했을 때 실행할 코드
finally:
    # 예외 발생 여부와 관계없이 항상 실행할 코드
  

위 구문은 세 가지 핵심 키워드인 try, except, finally로 구성됩니다.

try

try 키워드는 try-except 구문의 시작을 알립니다. 이 키워드 다음에 오는 코드 블록은 예외가 발생할 가능성이 있는 코드를 포함합니다. 파이썬 인터프리터는 try 블록 내부의 코드를 실행하다가 예외가 발생하면, 즉시 실행을 중단하고 except 블록으로 이동하여 해당 코드를 실행합니다.

except

except 키워드는 try 블록에서 예외가 발생했을 때 실행될 코드 블록을 정의합니다. 여러 종류의 예외에 대해 서로 다른 except 블록을 정의할 수 있습니다. 이 부분은 아래에서 더 자세히 다루겠습니다.

finally

finally 키워드는 예외 발생 여부와 상관없이 항상 실행될 코드 블록을 정의합니다. 이 블록은 파일 닫기, 네트워크 연결 종료 등과 같이 예외 발생 시에도 반드시 수행해야 하는 작업을 정의할 때 유용하게 사용됩니다.

예시

다음은 앞에서 다룬 숫자 입력 예제를 try-except 구문을 사용하여 예외를 처리하도록 수정한 코드입니다.

try:
    user_input = input("숫자를 입력하세요: ")
    num = int(user_input)
    print("입력하신 숫자의 두 배는:", num * 2)
except:
    print("오류가 발생했습니다.")
finally:
    print("이 코드는 항상 실행됩니다.")
  

위 코드를 실행하고 유효한 입력인 “5”를 입력하면 다음과 같은 결과를 얻습니다.

하지만 “hello”와 같은 문자열을 입력으로 사용하면 다음과 같은 결과가 나타납니다.

try 블록에서 예외가 발생하지 않으면, 코드는 finally 블록으로 이동합니다. 하지만 try 블록에서 예외가 발생하면, 코드는 except 블록으로 이동한 다음 finally 블록으로 이동합니다.

특정 유형의 오류에 대해 예외를 처리할 수도 있습니다. 예를 들어, ValueErrorKeyboardInterrupt 예외를 특별히 처리하려면 다음과 같이 코드를 수정할 수 있습니다.

try:
    user_input = input("숫자를 입력하세요: ")
    num = int(user_input)
    print("입력하신 숫자의 두 배는:", num * 2)
except ValueError:
    print("정수로 변환할 수 없는 값입니다.")
except KeyboardInterrupt:
    print("키보드 인터럽트가 발생했습니다.")
except:
    print("기타 예외가 발생했습니다.")
finally:
    print("이 코드는 항상 실행됩니다.")
  

위 코드에는 세 개의 except 블록이 있습니다. 첫 번째 except 블록은 ValueError 예외만 처리하고, 두 번째 블록은 KeyboardInterrupt 예외만 처리합니다. 마지막 except 블록은 처음 두 블록에서 포착되지 않은 모든 나머지 예외를 처리합니다.

위 코드를 실행하면 다음과 유사한 출력이 표시됩니다.

예외가 발생했을 때, 예외 객체에서 예외에 대한 더 자세한 정보를 얻을 수 있습니다. 예외 객체에 접근하려면 as 키워드를 사용합니다. 사용 방법은 다음과 같습니다:

try:
    user_input = input("숫자를 입력하세요: ")
    num = int(user_input)
    print("입력하신 숫자의 두 배는:", num * 2)
except ValueError as e:
    print("값 에러:", e)
except KeyboardInterrupt as e:
    print("키보드 인터럽트:", e)
except Exception as e:
    print("기타 예외", e)
  

예외를 발생시키는 방법

지금까지는 다른 함수에서 발생하는 예외를 처리하는 방법에 대해 알아봤습니다. 하지만 코드 내에서 직접 예외를 발생시키는 것도 가능합니다. 예외를 발생시키려면 raise 키워드를 사용해야 합니다. raise 키워드 뒤에는 발생시킬 예외의 유형과 예외에 대한 사람이 읽을 수 있는 메시지를 나타내는 클래스를 지정해야 합니다.

다음 예에서는 Exception 클래스를 사용하여 일반적인 예외를 발생시키고, 메시지를 클래스 생성자에 전달합니다.

raise Exception('오류가 발생했습니다.')
  

위 코드를 프로그램으로 실행하면 다음과 같은 출력이 나타납니다.

특정 종류의 예외를 지정할 수도 있습니다. 예를 들어, 값의 데이터 유형이 잘못된 경우 TypeError 예외를 발생시킬 수 있습니다.

def double(x):
    if isinstance(x, int):
        return x * 2
    else:
        raise TypeError('x는 정수여야 합니다.')
  

또는 지정된 값이 허용 가능한 범위를 벗어나면 ValueError를 발생시킬 수 있습니다.

def say_hello(name):
    if name == '':
        raise ValueError('값이 범위를 벗어났습니다.')
    else:
        print('안녕하세요,', name)
  

Exception 클래스를 상속하여 사용자 정의 예외 유형을 만들 수도 있습니다. 다음은 예시입니다.

class InvalidHTTPMethod(Exception):
    pass
  

위의 예에서는 Exception 클래스를 상속받는 InvalidHTTPMethod 클래스를 정의했습니다. 이 클래스를 사용하여 예외를 발생시킬 수 있습니다.

raise InvalidHTTPMethod('GET 또는 POST여야 합니다.')
  

예외 처리의 일반적인 사용 사례

예외 처리는 다양한 시나리오에서 활용됩니다. 앞서 예시에서는 사용자 입력으로 인한 예외를 처리하는 방법을 알아보았습니다. 이 섹션에서는 예외 처리가 유용한 추가적인 두 가지 상황, 즉 실패한 네트워크 요청으로 인한 예외 처리와 파일 읽기 중 발생하는 예외 처리에 대해 다룹니다.

네트워크 요청 처리

아래 코드는 Google에 네트워크 요청을 보내고, 발생하는 예외를 처리하는 방법을 보여줍니다. requests.exceptions 객체에는 네트워크 요청 시 발생할 수 있는 다양한 예외들이 정의되어 있습니다.

import requests

try:
    response = requests.get("https://google.com")

    if 200 <= response.status_code < 300:
        print("요청이 성공했습니다!")
    else:
        print(f"요청 실패, 상태 코드: {response.status_code}")
except requests.exceptions.RequestException as e:
    print(f"RequestException 발생: {e}")
except requests.exceptions.ConnectionError as e:
    print(f"ConnectionError 발생: {e}")
except requests.exceptions.Timeout as e:
    print(f"Timeout 발생: {e}")
except requests.exceptions.TooManyRedirects as e:
    print(f"TooManyRedirects 발생: {e}")
except requests.exceptions.HTTPError as e:
    print(f"HTTPError 발생: {e}")
except Exception as e:
    print(f"예상치 못한 오류 발생: {e}")
  

파일에서 데이터 읽기

마지막 예에서는 파일에서 데이터를 읽어오는 동안 발생할 수 있는 일반적인 예외, 예를 들어 FileNotFoundErrorIOError를 처리하는 방법을 보여줍니다.

try:
    with open(file_path, 'r') as file:
        data = file.read()
        print("파일 내용:")
        print(data)
except FileNotFoundError as e:
    print(f"FileNotFoundError 발생: {e}")
except IOError as e:
    print(f"IOError 발생: {e}")
except Exception as e:
    print(f"예상치 못한 오류 발생: {e}")
  

결론

본문에서는 예외가 무엇이고 왜 발생하는지, 그리고 예외를 어떻게 처리해야 하는지 알아보았습니다. 예외 처리는 코드의 안정성을 높이고 프로그램이 예상치 못한 오류로 인해 중단되는 것을 방지하는 데 매우 중요한 역할을 합니다. 또한 예외를 처리하는 방법과 예외를 직접 발생시키는 방법에 대해서도 다루었습니다.

다음으로 흔한 파이썬 오류 유형과 해결 방법을 알아보세요.