파이썬 둘러보기 Exception

파이썬에서 예외(Exception)란 프로그램을 실행하는 도중 발생하는 오류를 의미합니다. 구문 오류(Syntax Error)와 달리, 문법적으로는 올바르지만 실행 중에 예기치 않은 문제로 인해 발생하는 상황을 말한다.

파이썬은 이러한 예외가 발생했을 때 프로그램이 비정상적으로 종료되는 것을 막고, 개발자가 오류를 적절히 처리할 수 있도록 예외 처리(Exception Handling) 메커니즘을 제공한다. 이는 try, except, else, finally 키워드를 사용하여 구현되며, 이를 통해 코드의 안정성과 신뢰성을 크게 향상시킬 수 있다.

1. 예외 처리 기본 구조 (try ~ except)

예외가 발생할 가능성이 있는 코드를 try 블록 안에 넣고, 예외 발생 시 처리할 코드를 except 블록에 작성한다.

  • try: 예외 발생 가능성이 있는 코드 블록이다.
  • except: try 블록에서 예외가 발생했을 때 실행되는 코드 블록입니다. 특정 예외 타입(예: ValueError, ZeroDivisionError)을 지정하여 종류별로 다른 처리를 할 수 있다.
try:
    # 0으로 나누는 예외를 인위적으로 발생
    result = 10 / 0
    print(result)
except ZeroDivisionError as e:
    # ZeroDivisionError가 발생했을 때만 실행됨
    print(f"0으로 나눌 수 없습니다. (오류: {e})")

2. 주요 예외 처리 구문

try 블록에서 예외가 발생하지 않았을 때 실행되는 코드를 담는다. 정상적으로 코드가 실행되었을 때만 수행되어야 하는 후속 작업에 유용하다.

finally 예외 발생 여부와 상관없이 항상 실행되는 코드 블록이다. 주로 파일 닫기, 데이터베이스 연결 해제 등 반드시 실행되어야 하는 정리(clean-up) 코드를 작성할 때 사용된다.

try:
    num = int(input("숫자를 입력하세요: "))
except ValueError:
    print("잘못된 값입니다. 숫자만 입력해주세요.")
else:
    print(f"입력한 숫자는 {num}입니다.")
finally:
    print("프로그램을 종료합니다.")

3. 예외 발생시키기 (raise)

개발자가 의도적으로 특정 조건에서 예외를 발생시켜야 할 때 raise 키워드를 사용합니다. 이는 특정 비즈니스 로직을 위반하는 경우 등에 활용될 수 있다.

def set_age(age):
    if age < 0:
        # 음수 나이는 논리적으로 맞지 않으므로 예외를 발생시킴
        raise ValueError("나이는 음수일 수 없습니다.")
    print(f"나이가 {age}세로 설정되었습니다.")

try:
    set_age(-5)
except ValueError as e:
    print(f"오류 발생: {e}")

4. 주요 내장 예외 종류

  • ValueError: 연산이나 함수에 부적절한 값을 인자로 전달했을 때 발생한다.
  • TypeError: 잘못된 타입의 객체에 연산을 시도할 때 발생한다.
  • IndexError: 시퀀스(리스트, 튜플 등)에서 존재하지 않는 인덱스로 접근할 때 발생한다.
  • KeyError: 딕셔너리에서 존재하지 않는 키로 접근할 때 발생한다.
  • FileNotFoundError: 존재하지 않는 파일을 열려고 할 때 발생한다.
  • ZeroDivisionError: 숫자를 0으로 나누려고 할 때 발생한다.

예외를 처리할 때는 몇 가지 중요한 점을 반드시 고려해야 한다.

  • 구체적인 예외 잡기

가장 중요한 원칙 중 하나는 구체적인 예외를 잡는 것이다. except Exception as e:처럼 포괄적인 예외를 잡는 것은 당장의 오류는 막을 수 있지만, 프로그램의 어떤 부분이 잘못되었는지 파악하기 어렵게 만든다. 이는 예상치 못한 버그를 숨기는 원인이 될 수 있으므로, ValueError나 TypeError처럼 발생할 가능성이 있는 특정 예외를 명시하여 처리하는 것이 바람직하다.

  • 예외를 무시하지 않기

except 블록 안에서 아무런 처리도 하지 않고 pass로 넘기는 것은 가장 피해야 할 습관 중 하나이다. 오류가 발생했다는 사실 자체를 무시하는 행위는 디버깅을 극도로 어렵게 만듭니다. 최소한 오류 상황을 기록하기 위해 로깅(logging)을 사용하거나, 사용자에게 상황을 알리는 등의 조치를 취해야 한다.

  • “예외”는 예외적인 상황에만 사용하기

프로그램의 정상적인 제어 흐름을 위해 예외 처리를 남용해서는 안 된다. 예를 들어, 반복문을 종료시키기 위해 IndexError를 일부러 발생시키는 방식은 코드의 가독성을 해치고 의도를 파악하기 어렵게 만든다. 예외 처리는 이름 그대로 예상치 못한 예외적인 상황을 해결하는 데 집중해야 한다.

  • 정리 작업은 finally 활용하기

파일 핸들이나 데이터베이스 연결과 같이 성공 여부와 관계없이 항상 실행되어야 하는 정리 코드는 finally 블록에 작성하는 것이 안전하다. 이렇게 하면 try 블록에서 예기치 않은 오류가 발생하더라도 리소스가 정상적으로 해제되는 것을 보장할 수 있다.

실제 프로젝트에서 마주치는 복잡한 상황들을 다루는 더욱 상세한 예시 코드들과 함께 예외 처리 심화편을 준비할 예정이다. 컨텍스트 매니저(with 문)를 활용한 고급 리소스 관리, 예외 체인과 중첩 예외 처리, 로깅 시스템과 연동한 체계적인 오류 추적, 그리고 비동기 프로그래밍에서의 예외 처리 패턴까지 다룰 계획이다.