파이썬 교육 챕터 49 예외 처리 Try Except

예외 처리(Exception Handling): try-except (오류 방지)

1. 서론: 예상치 못한 상황에 대비하는 지혜

지금까지 우리는 파이썬 코드를 작성하고 실행하며, 다양한 기능을 구현하는 방법을 배웠습니다. 하지만 아무리 완벽하게 작성된 코드라도 프로그램이 실행되는 동안 예상치 못한 문제에 직면할 수 있습니다. 예를 들어, 사용자가 숫자를 입력해야 하는 곳에 문자를 입력하거나, 프로그램이 존재하지 않는 파일을 열려고 시도하거나, 네트워크 연결이 끊어지는 등의 상황이 발생할 수 있습니다. 이러한 예상치 못한 문제들을 ‘오류(Error)’ 또는 ‘예외(Exception)’라고 합니다. 예외가 발생하면 프로그램은 일반적으로 강제 종료되어 버립니다. 이는 사용자에게 불편함을 주고, 프로그램의 신뢰성을 떨어뜨립니다. 이때 필요한 것이 바로 ‘예외 처리(Exception Handling)’입니다. 예외 처리는 프로그램 실행 중 발생할 수 있는 오류를 미리 예측하고, 해당 오류가 발생했을 때 프로그램이 강제 종료되지 않고 정상적으로 처리될 수 있도록 하는 메커니즘입니다. 파이썬은 try-except 문을 사용하여 예외를 처리합니다. 이 챕터에서는 예외의 개념과 try-except 문의 기본 구조, 그리고 다양한 예외를 처리하는 방법에 대해 깊이 있게 알아보겠습니다. 예외 처리를 마스터하는 것은 여러분의 파이썬 프로그램을 더욱 견고하고 안정적으로 만드는 데 필수적인 단계가 될 것입니다.

2. 오류(Error)와 예외(Exception)란 무엇인가?

프로그래밍에서 오류는 크게 두 가지로 나눌 수 있습니다.

  • 구문 오류 (Syntax Error): 코드의 문법이 잘못되었을 때 발생합니다. 파이썬 인터프리터가 코드를 실행하기 전에 발견하며, 프로그램이 시작조차 되지 않습니다. (예: 괄호를 닫지 않거나, 오타 등)
    python
    # print("Hello" # SyntaxError: unexpected EOF while parsing
  • 런타임 오류 (Runtime Error) / 예외 (Exception): 코드의 문법은 올바르지만, 프로그램이 실행되는 도중에 발생하는 오류입니다. 이러한 오류를 ‘예외(Exception)’라고 부릅니다. 예외가 발생하면 프로그램은 일반적으로 중단됩니다.
    “`python
    # ZeroDivisionError: 0으로 나누는 경우
    # result = 10 / 0

    NameError: 정의되지 않은 변수를 사용하는 경우

    print(undefined_variable)

    TypeError: 잘못된 자료형으로 연산을 시도하는 경우

    “hello” + 10

    FileNotFoundError: 존재하지 않는 파일을 열려고 하는 경우

    open(“non_existent_file.txt”, “r”)

    ValueError: 함수의 인자로 유효하지 않은 값을 전달하는 경우

    int(“abc”)

    “`

예외 처리는 이러한 런타임 오류(예외)가 발생했을 때 프로그램이 비정상적으로 종료되는 것을 막고, 오류를 복구하거나 사용자에게 친절한 메시지를 보여주는 등의 처리를 할 수 있도록 돕습니다.

3. try-except 문의 기본 구조

파이썬에서 예외를 처리하는 가장 기본적인 방법은 try-except 문을 사용하는 것입니다.

try:
    # 예외가 발생할 가능성이 있는 코드 블록
except 예외타입 as 변수:
    # 예외가 발생했을 때 실행될 코드 블록
    # (예외를 처리하거나, 사용자에게 메시지를 보여주는 등)

설명:

  • try: 예외 발생 가능성이 있는 코드를 이 블록 안에 작성합니다. 파이썬은 try 블록의 코드를 실행하다가 예외가 발생하면 즉시 try 블록의 실행을 중단하고 except 블록으로 넘어갑니다.
  • except 예외타입 as 변수: 특정 예외타입의 예외가 발생했을 때 실행될 코드 블록입니다. as 변수는 선택 사항으로, 발생한 예외 객체를 변수에 할당하여 예외에 대한 상세 정보를 얻을 수 있습니다.

예시: ZeroDivisionError 처리

try:
    num1 = 10
    num2 = 0
    result = num1 / num2 # ZeroDivisionError 발생
    print(f"나눗셈 결과: {result}") # 이 줄은 실행되지 않습니다.
except ZeroDivisionError:
    print("오류: 0으로 나눌 수 없습니다.")

print("프로그램이 정상적으로 종료되었습니다.")

# 출력:
# 오류: 0으로 나눌 수 없습니다.
# 프로그램이 정상적으로 종료되었습니다.

위 예시에서 num1 / num2에서 ZeroDivisionError가 발생하면, try 블록의 나머지 부분은 실행되지 않고 즉시 except ZeroDivisionError 블록으로 제어가 넘어갑니다. except 블록의 코드가 실행된 후에는 try-except 문을 벗어나 프로그램의 다음 코드가 정상적으로 실행됩니다.

4. 다양한 예외 처리 방법

4.1. 여러 종류의 예외 처리

하나의 try 블록에 대해 여러 개의 except 블록을 사용하여 다양한 종류의 예외를 처리할 수 있습니다. 이때 예외의 종류에 따라 다른 처리를 할 수 있습니다.

try:
    numbers = [1, 2, 3]
    index = int(input("인덱스를 입력하세요: ")) # ValueError 발생 가능
    print(numbers[index]) # IndexError 발생 가능
    result = 10 / index # ZeroDivisionError 발생 가능
except ValueError:
    print("오류: 유효한 숫자를 입력해주세요.")
except IndexError:
    print("오류: 리스트 범위를 벗어난 인덱스입니다.")
except ZeroDivisionError:
    print("오류: 0으로 나눌 수 없습니다.")
except Exception as e: # 모든 종류의 예외를 처리하는 일반적인 except 블록
    print(f"알 수 없는 오류 발생: {e}")

# 사용자가 'abc' 입력: 오류: 유효한 숫자를 입력해주세요.
# 사용자가 '5' 입력: 오류: 리스트 범위를 벗어난 인덱스입니다.
# 사용자가 '0' 입력: 오류: 0으로 나눌 수 없습니다.

주의: 여러 except 블록을 사용할 때는 더 구체적인 예외를 먼저 처리하고, 더 일반적인 예외(Exception)는 나중에 처리해야 합니다. 만약 except Exception을 먼저 두면, 모든 예외가 이 블록에서 처리되어 다른 구체적인 except 블록은 실행되지 않습니다.

4.2. 예외 객체 정보 얻기

except 예외타입 as 변수 형태로 예외 객체를 변수에 할당하여 예외에 대한 상세 정보를 얻을 수 있습니다. 이는 디버깅이나 사용자에게 더 자세한 오류 메시지를 제공할 때 유용합니다.

try:
    file_name = input("파일 이름을 입력하세요: ")
    with open(file_name, "r") as f:
        content = f.read()
        print(content)
except FileNotFoundError as e:
    print(f"오류: 파일을 찾을 수 없습니다. ({e})")
except Exception as e:
    print(f"예상치 못한 오류 발생: {e}")

# 사용자가 'non_existent.txt' 입력:
# 오류: 파일을 찾을 수 없습니다. ([Errno 2] No such file or directory: 'non_existent.txt')

4.3. 예외를 발생시키기 (raise)

때로는 특정 조건이 만족되지 않을 때 개발자가 직접 예외를 발생시켜 프로그램의 흐름을 제어해야 할 때가 있습니다. 이때 raise 키워드를 사용합니다.

def validate_age(age):
    if not isinstance(age, (int, float)):
        raise TypeError("나이는 숫자여야 합니다.")
    if age < 0:
        raise ValueError("나이는 음수일 수 없습니다.")
    if age > 150:
        raise ValueError("나이가 너무 많습니다.")
    print(f"유효한 나이: {age}")

try:
    validate_age(25)
    validate_age(-5) # ValueError 발생
    validate_age("abc") # TypeError 발생
except (TypeError, ValueError) as e:
    print(f"유효성 검사 오류: {e}")

# 출력:
# 유효한 나이: 25
# 유효성 검사 오류: 나이는 음수일 수 없습니다.

5. 예외 처리의 중요성

  • 프로그램 안정성: 예외 발생 시 프로그램이 강제 종료되는 것을 방지하고, 안정적으로 계속 실행될 수 있도록 합니다.
  • 사용자 경험 향상: 사용자에게 친절하고 의미 있는 오류 메시지를 제공하여 혼란을 줄이고, 프로그램 사용성을 높입니다.
  • 디버깅 용이성: 예외가 발생한 지점과 원인을 명확히 파악할 수 있도록 도와 디버깅 시간을 단축시킵니다.
  • 코드의 견고성: 예상치 못한 상황에 대한 대비를 통해 프로그램의 견고성을 높입니다.

6. 결론: 견고한 프로그램의 필수 요소, 예외 처리

이 챕터를 통해 여러분은 파이썬에서 예외의 개념과 try-except 문을 사용하여 오류를 처리하는 기본적인 방법에 대해 깊이 있게 학습했습니다. 구문 오류와 런타임 오류(예외)의 차이점을 이해하고, try 블록에서 예외 발생 가능성이 있는 코드를 작성하며, except 블록에서 특정 예외를 처리하는 방법을 배웠습니다. 또한, 여러 종류의 예외를 처리하는 방법, 예외 객체 정보를 얻는 방법, 그리고 raise 키워드를 사용하여 직접 예외를 발생시키는 방법까지 살펴보았습니다.

예외 처리는 프로그램의 안정성과 신뢰성을 높이는 데 필수적인 기술입니다. 사용자 입력 오류, 파일 접근 오류, 네트워크 문제 등 실제 프로그램에서는 수많은 예외 상황이 발생할 수 있습니다. try-except 문을 효과적으로 사용하면 이러한 예외 상황에 유연하게 대처하고, 프로그램이 비정상적으로 종료되는 것을 막아 사용자에게 더 나은 경험을 제공할 수 있습니다.

이제 여러분은 파이썬 프로그램의 안정성을 높이는 예외 처리의 기본을 마스터했습니다. 다음 챕터에서는 try-except 문에 elsefinally 블록을 추가하여 예외 처리 로직을 더욱 정교하게 만드는 방법을 배우게 될 것입니다. 오늘 배운 try-except 문을 활용하여 다양한 예외 상황을 가정하고 직접 프로그램을 만들어 보면서, 여러분의 파이썬 실력을 더욱 단단하게 다지세요!

파이썬 교육 챕터 49 예외 처리 Try Except