프로그래밍/Python

파이썬 예외 처리: 기본부터 고급 기법까지

shimdh 2025. 2. 21. 09:49
728x90

파이썬에서 예외 처리는 프로그램의 안정성을 높이고, 예상치 못한 오류로 인한 비정상적인 종료를 방지하는 중요한 기법입니다. 이번 포스트에서는 파이썬의 예외 처리 개념을 기본적인 예외 유형부터 사용자 정의 예외, 그리고 고급 예외 처리 기법까지 다루어 보겠습니다. 각 개념을 이해하기 쉽도록 예제 코드를 함께 제공하며, 예외 처리를 통해 더 견고한 코드를 작성하는 방법을 배울 수 있습니다.


1. 예외 처리란?

1.1 예외 처리의 개념

예외 처리는 프로그램 실행 중 발생할 수 있는 오류를 관리하고, 이러한 오류가 프로그램의 흐름을 방해하지 않도록 하는 기법입니다. 파이썬에서는 다양한 종류의 예외가 존재하며, 이를 적절히 처리함으로써 사용자에게 보다 나은 경험을 제공할 수 있습니다.

1.2 예외 처리의 중요성

예외 처리는 단순히 오류를 방지하는 것뿐만 아니라, 프로그램의 로직을 더 명확하게 표현하고, 디버깅을 용이하게 하는 데에도 큰 도움을 줍니다. 예를 들어, 파일을 열거나 네트워크 요청을 보내는 등의 작업에서 예외 처리를 통해 예상치 못한 상황에 대비할 수 있습니다.


2. 기본적인 예외 유형

파이썬에는 다양한 내장 예외가 존재하며, 이들은 프로그램 실행 중 발생할 수 있는 일반적인 오류를 나타냅니다. 아래는 자주 접할 수 있는 기본적인 예외 유형들입니다.

2.1 SyntaxError

SyntaxError는 코드의 문법에 오류가 있을 때 발생합니다. 예를 들어, 괄호를 닫지 않거나 잘못된 구문을 사용할 때 이 오류가 발생합니다.

print("Hello World"  # 괄호가 닫히지 않음

2.2 TypeError

TypeError는 데이터 타입 간의 불일치로 인해 발생합니다. 예를 들어, 문자열과 정수를 더하려고 할 때 이 오류가 발생합니다.

result = "10" + 5  # 문자열과 정수를 더하려고 시도함

2.3 ValueError

ValueError는 함수나 연산자가 올바른 타입을 받았지만, 그 값이 적절하지 않을 때 발생합니다. 예를 들어, 문자열을 정수로 변환하려고 할 때 변환할 수 없는 값이 들어오면 이 오류가 발생합니다.

int_value = int("abc")  # 문자열 'abc'는 정수로 변환할 수 없음

2.4 IndexError

IndexError는 리스트나 튜플 등의 인덱스 범위를 초과할 때 발생합니다. 예를 들어, 존재하지 않는 인덱스를 접근하려고 할 때 이 오류가 발생합니다.

my_list = [1, 2, 3]
print(my_list[5])  # 존재하지 않는 인덱스를 접근하려고 함

2.5 KeyError

KeyError는 딕셔너리에서 존재하지 않는 키를 접근하려 할 때 발생합니다. 예를 들어, 딕셔너리에 없는 키를 사용하려고 할 때 이 오류가 발생합니다.

my_dict = {"name": "Alice"}
print(my_dict["age"])  # 'age'라는 키가 없으므로 KeyError 발생

3. 예외 처리 문법

파이썬에서는 try, except, else, 그리고 finally 블록을 사용하여 예외를 처리합니다. 이 구조를 통해 프로그램의 흐름을 제어하고, 오류가 발생했을 때 적절히 대응할 수 있습니다.

3.1 기본 구조

try:
    # 오류가 발생할 가능성이 있는 코드 블록
except ExceptionType:
    # 해당 유형의 예외가 발생했을 때 수행될 코드 블록
else:
    # 오류가 없을 경우 실행되는 코드 블록
finally:
    # 항상 실행되는 코드 블록 (주로 자원 해제에 사용)

3.2 실용적인 예제

다음은 두 숫자를 나누는 간단한 함수입니다. 이 함수는 입력값에 대한 유효성을 검사하고, 필요한 경우 적절한 메시지를 출력하여 사용자에게 알립니다.

def divide_numbers(num1, num2):
    try:
        result = num1 / num2
    except ZeroDivisionError:
        return "오류: 0으로 나눌 수 없습니다."
    except TypeError:
        return "오류: 숫자를 입력하세요."
    else:
        return f"결과는 {result} 입니다."
    finally:
        print("나눗셈 연산 완료.")

# 테스트 케이스
print(divide_numbers(10, 5))      # 결과는 2.0 입니다.
print(divide_numbers(10, 0))      # 오류: 0으로 나눌 수 없습니다.
print(divide_numbers(10, 'a'))    # 오류: 숫자를 입력하세요.

3.3 finally 블록의 중요성

finally 블록은 예외 발생 여부와 상관없이 항상 실행됩니다. 이는 주로 파일을 닫거나 데이터베이스 연결을 해제하는 등의 자원 해제 작업에 사용됩니다.

try:
    file = open("example.txt", "r")
    content = file.read()
    print(content)
except FileNotFoundError:
    print("파일을 찾을 수 없습니다.")
finally:
    file.close()
    print("파일이 닫혔습니다.")

4. 사용자 정의 예외

프로그램의 특정 상황에 맞춰서 직접 정의한 에러 유형도 있습니다. 이는 사용자가 만든 클래스 형태로 구현됩니다. 사용자 정의 예외를 사용하면 프로그램의 로직을 더 명확하게 표현할 수 있습니다.

4.1 사용자 정의 예외 만들기

아래는 음수를 입력했을 때 발생하는 사용자 정의 예외를 만드는 예제입니다.

class NegativeNumberError(Exception):
    """음수 입력 시 발생하는 사용자 정의 예외"""
    pass

def sqrt(num):
    if num < 0:
        raise NegativeNumberError("오류: 음수의 제곱근은 계산할 수 없습니다.")

    return num ** 0.5

try:
    print(sqrt(-9))
except NegativeNumberError as e:
    print(e)

4.2 사용자 정의 예외의 활용

사용자 정의 예외는 비즈니스 로직이나 특정 도메인의 규칙을 표현하는 데 유용합니다. 예를 들어, 은행 애플리케이션에서 잔고가 부족할 때 발생하는 예외를 정의할 수 있습니다.

class InsufficientBalanceError(Exception):
    """잔고가 부족할 때 발생하는 사용자 정의 예외"""
    pass

def withdraw(balance, amount):
    if amount > balance:
        raise InsufficientBalanceError("오류: 잔고가 부족합니다.")
    return balance - amount

try:
    print(withdraw(100, 150))
except InsufficientBalanceError as e:
    print(e)

5. 고급 예외 처리 기법

예외 처리를 더 효과적으로 활용하기 위해 몇 가지 고급 기법을 알아보겠습니다.

5.1 예외 체인

예외 체인은 하나의 예외가 다른 예외를 발생시킬 때 사용됩니다. 이를 통해 예외의 원인을 더 명확하게 추적할 수 있습니다.

try:
    try:
        raise ValueError("원본 오류")
    except ValueError as e:
        raise TypeError("새로운 오류") from e
except TypeError as e:
    print(f"새로운 오류: {e}, 원본 오류: {e.__cause__}")

5.2 예외 그룹

파이썬 3.11부터는 ExceptionGroup을 사용하여 여러 예외를 그룹화할 수 있습니다. 이를 통해 복잡한 예외 상황을 더 효과적으로 처리할 수 있습니다.

try:
    raise ExceptionGroup("그룹 오류", [ValueError("오류 1"), TypeError("오류 2")])
except* ValueError as e:
    print(f"ValueError: {e}")
except* TypeError as e:
    print(f"TypeError: {e}")

6. 결론

예외 처리는 소프트웨어 개발 과정에서 매우 중요한 요소입니다. 다양한 종류의 오류를 이해하고 이에 대한 적절한 대처 방법을 아는 것이 필요합니다. 위에서 설명한 여러 가지 기본적인 오류 및 사용자 정의 오류 사례들을 통해 여러분은 보다 견고하고 신뢰성 있는 코드를 작성할 수 있게 됩니다.

728x90