[8일차-2] Python

송정근·2026년 5월 29일

예외 처리 정리

예외(exception)는 프로그램 실행 중 발생할 수 있는 오류 상황을 의미한다. 예외가 발생하면 프로그램은 기본적으로 중단된다. 따라서 예상 가능한 오류는 적절히 처리해 프로그램이 갑자기 종료되지 않도록 만들고, 사용자나 개발자에게 필요한 정보를 제공해야 한다.

1. 예외란?

예외는 실행 중에 발생하는 오류이다. 문법 오류처럼 실행 전에 발견되는 문제와 달리, 예외는 프로그램이 실행되다가 특정 상황에서 발생한다.

print(int(10 / 3))
print(5 / 0)
print(4 / 2)

출력 결과 :

3
Traceback (most recent call last):
  ...
ZeroDivisionError: division by zero

5/0 에서 ZeroDivisionError 가 발생하면 프로그램은 그 지점에서 중단된다. 따라서 그 아래에 있는 print(4 / 2) 는 실행되지 않는다.

자주 만나는 예외는 다음과 같다.

타입발생 상황간단 예제설명
Exception일반 적인 예외except Exception대부분 예외의 부모
Value Error값이 잘못 됨int("abc")타입은 맞지만 값이 잘못됨
TypeError타입이 잘못됨"10" + 10연산 불가능한 타입
ZeroDivisionError0으로 나눔1 / 0 수학적 오류
IndexError리스트 범위 초과list[5]인덱스 범위 오류
KeyError딕셔너리 키 없음d["b"]존재하지 않는 키
AttributeError속성 없음x.append()객체에 없는 메서드
FileNotFoundError파일 없음open("x.txt")파일 경로 오류
ModuleNotFoundError모듈 없음import x모듈 import 실패
StopIteration반복 종료next(iter([]))반복 끝
KeyboardInterruptCtrl + c사용자 입력강제 종료
SystemExit프로그램 종료exit()종료 요청

2. 예외 계층 구조

파이썬 예외는 클래스 계층 구조를 가진다. 대표적인 구조는 다음과 같다.

BaseException
├── Exception
│   ├── ValueError
│   ├── TypeError
│   ├── ZeroDivisionError
│   ├── IndexError
│   └── ...
├── KeyboardInterrupt
└── SystemExit

일반적으로 우리가 처리하는 예외는 대부분 Exception 의 하위 클래스이다.

예외의미
Exception코드 실행 중 발생하는 일반적인 오류의 부모 클래스
KeyboardInterrupt사용자가 Ctrl + C로 프로그램을 중단할 때 발생
SystemExitexit() 또는 sys.exit()로 프로그램 종료를 요청할 때 발생

KeyboardInterrupt 오 SystemExit 는 일반적인 오류라기보다 프로그램을 중단하거나 종료하기 위한 신호에 가깝다. 그래서 보통은 BaseException 이 아니라 Exception 을 잡는다.

try:
  raise KeyboardInterrupt
except Exception:
  print("Exception 잡힘")
except BaseException:
  print("BaseException 잡힘")

출력 결과 :

BaseException 잡힘

except: 처럼 예외 타입을 생략하면 BaseException 계열까지 잡을 수 있어 위험하다. 사용자의 강제 종료 신호까지 막아버릴 수 있기 때문이다.

3. 예외 처리 기본 구조

예외 처리는 try, except , else , finally 를 사용한다.

try:
	예외가 발생할 가능성이 있는 코드
except 예외타입:
	해당 예외가 발생했을 때 실행할 코드
else:
	예외가 발생하지 않을 때 실행할 코드
finally:	
	예외 발생 여부와 관계없이 항상 실행할 코드

가장 기본적인 형태는 try-except 이다.

try:
    print(10 / 3)
    print(5 / 0)
    print(4 / 2)
except Exception:
    print("예외 발생")

print("프로그램을 종료한다.")

출력 결과 :

3.3333333333333335
예외 발생
프로그램을 종료한다.

try 블록에서 예외가 발생하면 그 즉시 except 블록으로 이동한다. 따라서 print(4 / 2) 는 실행되지 않는다.

특정 예외만 처리할 수도 있다.

try:
    print(10 / 3)
    print(5 / 0)
    print(4 / 2)
except ZeroDivisionError:
    print("0으로 나눌 수 없음")

print("프로그램을 종료한다.")

출력 결과 :

3.3333333333333335
0으로 나눌 수 없음
프로그램을 종료한다.

4. 여러 예외 처리하기

상황에 따라 발생할 수 있는 예외가 다르다면 except 를 여러개 사용할 수 있다.

try:
  data = [10, 20, 30, 40, 50]
  print(data[0])
  print(data[5])
  print(data[1])
except IndexError:
  print("인덱스 오류")

print("프로그램을 종료한다.")

출력 결과 :

10
인덱스 오류
프로그램을 종료한다.

여러 예외를 나누어 처리하면 오류 원인에 맞는 메시지를 제공할 수 있다.

try:
    data = [10, 20, 30, 40, 50]
    print(5 / 0)
except IndexError:
    print("인덱스 오류")
except ValueError:
    print("입력 오류")
except ZeroDivisionError:
    print("0으로 나눌 수 없음")
except Exception:
    print("예외 발생")
else:
    print("에러가 발생하지 않은 정상적인 프로그램")
finally:
    print("에러에 관계없이 무조건 실행되는 문장")

print("프로그램을 종료한다.")

출력 결과 :

0으로 나눌 수 있다.
에러에 관계없이 무조건 실행되는 문장
프로그램을 종료한다.

except 는 구체적인 예외부터 작성하는 것이 좋다. Exception 을 가장 위에 쓰면 대부분의 예외가 먼저 잡혀서 아래의 구체적인 except 블록은 실행되지 않는다.

4.1 else 와 finally

else 는 try 블록에서 예외가 발생하지 않았을 때만 실행된다. finally 는 예외 발생 여부와 관계없이 항상 실행된다.

try:
    number = int("10")
except ValueError:
    print("숫자로 변환할 수 없음")
else:
    print("변환 성공:", number)
finally:
    print("변환 시도 종료")

출력 결과 :

변환 성공: 10
변환 시도 종료

finally 는 파일 닫기, DB 연결 해제, 임시 리소스 정리처럼 반드시 실행되어야 하는 작업에 사용한다.

5. 예외 객체 활용하기

except 예외타입 as 변수 형태를 사용하면 발생한 예외 객체를 변수로 받을 수 있다.

try:
    int("abc")
except Exception as e:
    print("에러 메시지:", e)
    print("에러 타입:", type(e))
    print("args:", e.args)

출력 결과 :

에러 메시지: invalid literal for int() with base 10: 'abc'
에러 타입: <class 'ValueError'>
args: ("invalid literal for int() with base 10: 'abc'",)

예외 객체에는 오류 메시지, 타입, 추가 정보가 들어있다.

Exception 객체
├── args        → 에러 메시지나 전달된 값
├── __str__()   → 사용자용 문자열 표현
├── __repr__()  → 개발자용 표현
├── __class__   → 예외 타입
└── traceback   → 에러 발생 위치 정보

as e 를 사용하는 이유는 발생한 예외 객체의 정보를 활용하기 위해서이다.

6. raise 로 예외 발생시키기

raise 는 의도적으로 예외를 발생시키는 키워드이다. 잘못된 입력이나 비정상적인 상태를 명확히 알릴 때 사용한다.

try:
    raise Exception("예외가 발생했어요")
except Exception as e:
    print(e)

출력 결과 :

예외가 발생했어요.

6.1 조건에 따라 예외 발생시키기

def validate_even(number):
    if number % 2 == 1:
        raise ValueError("홀수를 입력했어요")
    print(number)


try:
    validate_even(3)
except ValueError as e:
    print("예외가 발생:", e)

출력 결과 :

예외가 발생: 홀수를 입력했어요

이처럼 함수 내부에서 문제를 발견하면 raise 로 예외를 발생기키고, 함수를 호출한 쪽에서 처리하게 만들 수 있다.

6.2 현재 예외 다시 전달하기

except 블록 안에서 raise 만 사용하면 현재 처리 중인 예외를 다시 바깥으로 전달한다.

def parse_number(text):
    try:
        return int(text)
    except ValueError:
        print("숫자 변환 실패")
        raise


try:
    parse_number("abc")
except ValueError as e:
    print("호출한 쪽에서 다시 처리:", e)

출력 결과 :

숫자 변환 실패
호출한 쪽에서 다시 처리: invalid literal for int() with base 10: 'abc'

7. 예외 전파

예외는 현재 함수에서 처리되지 않으면 호출한 함수 쪽으로 전달된다. 이를 예외 전파라고 한다.

def func1():
    func2()


def func2():
    func3()


def func3():
    print("%d" % "문자열")


try:
    func1()
except TypeError:
    print("타입이 올바르지 않다.")

출력 결과 :

타입이 올바르지 않다. 

func3() 에서 발생한 TypeError 는 func2(), func1() 을 거쳐 최종적으로 바깥의 try-except 에서 처리된다.

함수 내부에서 직접 처리할 수도 있다.

def func1():
    func2()


def func2():
    func3()


def func3():
    try:
        print("%d" % "문자열")
    except TypeError:
        print("타입이 올바르지 않다.")


func1()

출력 결과 :

타입이 올바르지 않다.

어느 위치에서 예외를 처리할지는 프로그램 구조에 따라 달라진다. 보통 예외를 의미 있게 처리할 수 있는 위치에서 잡는 것이 좋다.

8. 좋은 예외 처리 습관

예외 처리는 단순히 오류를 숨기는 문법이 아니다. 프로그램이 실패할 수 있는 상황을 명확히 표현하고, 복구 가능한 오류를 적잘히 처리하기 위한 도구이다.

8.1 너무 넓게 잡지 않기

try:
    result = 10 / 0
except Exception:
    print("문제가 발생했다.")

위 코드는 동작은 하지만 어떤 문제가 발생했는지 알기 어렵다. 가능하면 구체적인 예외를 잡는 것이 좋다.

try:
    result = 10 / 0
except ZeroDivisionError:
    print("0으로 나눌 수 없다.")

8.2 예외를 조용히 무시하지 않기

try:
    result = 10 / 0
except ZeroDivisionError:
    pass

pass 로 예외를 무시하면 문제가 발생했는지 알기 어렵다. 정말 무시해도 되는 상황이 아니라면 로그를 남기거나 사용자에게 안내하는 것이 좋다.

8.3 입력 검증과 예외 처리를 함께 사용하기

def divide(a, b):
    if b == 0:
        raise ZeroDivisionError("b는 0이 될 수 없다.")
    return a / b


try:
    print(divide(10, 0))
except ZeroDivisionError as e:
    print(e)

9. 마무리

예외 처리는 프로그램이 예상치 못한 상황에서도 무너지지 않도록 도와주는 중요한 문법이다.

문법역할
try예외가 발생할 가능성이 있는 코드를 감싼다.
except특정 예외가 발생했을 때 실행할 코드를 작성한다.
else예외가 발생하지 않았을 때 실행된다.
finally예외 발생 여부와 관계없이 항상 실행된다.
raise의도적으로 예외를 발생시키거나 다시 전달한다.
as e발생한 예외 객체를 변수로 받아 정보를 활용한다.

좋은 예외 처리는 오류를 숨기는 것이 아니라, 오류가 발생했을 때 프로그램이 어떤 방식으로 대응할지 명확하게 정하는 것이다.

출처 및 참고 자료

본 정리는 강의 내용과 아래 자료를 참고하여 학습 목적으로 작성하였습니다.

https://ryuzyproject.tistory.com/291

profile
기록하며 성장하는 개발자

0개의 댓글