점프 투 파이썬 DAY12(예외처리)

정지범·2026년 9월 21일

점프 투 파이썬

목록 보기
22/37

05-4 예외 처리

목차

  • 1. 오류는 언제 발생하는가?

  • 2. 오류 예외 처리 기법

    • 2-1. try-except 문
    • 2-2. try-finally 문
    • 2-3. 여러 개의 오류 처리하기
    • 2-4. try-else 문
  • 3. 오류 회피하기

  • 4. 오류 일부러 발생시키기

  • 5. 예외 만들기


1. 오류는 언제 발생하는가?

프로그램을 만들다 보면 다양한 오류가 발생한다.

파이썬에서는 오류가 발생하면 프로그램 실행을 중단하고 오류 메시지를 보여 준다.

대표적인 오류를 살펴보자.


존재하지 않는 파일 열기

f = open("나없는파일", "r")

실행 결과

FileNotFoundError

존재하지 않는 파일을 열려고 하면 FileNotFoundError가 발생한다.


0으로 나누기

4 / 0

실행 결과

ZeroDivisionError: division by zero

숫자를 0으로 나눌 수 없기 때문에 ZeroDivisionError가 발생한다.


존재하지 않는 리스트 인덱스 접근

a = [1, 2, 3]

a[3]

실행 결과

IndexError: list index out of range

리스트 a에는

a[0] → 1
a[1] → 2
a[2] → 3

까지만 존재한다.

따라서 존재하지 않는 a[3]에 접근하면 IndexError가 발생한다.


2. 오류 예외 처리 기법

오류가 발생하더라도 프로그램을 중단하지 않고 적절하게 처리하려면 예외 처리(Exception Handling) 를 사용한다.

대표적으로 try-except문을 사용한다.


2-1. try-except 문

기본 구조는 다음과 같다.

try:
    수행할 문장
except 발생오류:
    오류 발생 시 수행할 문장

동작 구조

try 실행
   ↓
오류 발생?
 ├─ YES → except 실행
 └─ NO  → except 실행하지 않음

방법 1. 오류 종류를 지정하지 않는 방법

try:
    4 / 0
except:
    print("오류가 발생했습니다.")

실행 결과

오류가 발생했습니다.

except:만 사용하면 오류 종류와 상관없이 모든 오류를 처리한다.


방법 2. 발생할 오류를 지정하는 방법

try:
    4 / 0
except ZeroDivisionError:
    print("0으로 나눌 수 없습니다.")

실행 결과

0으로 나눌 수 없습니다.

이 경우 ZeroDivisionError가 발생했을 때만 except가 실행된다.


방법 3. 오류 메시지까지 확인하는 방법

try:
    4 / 0
except ZeroDivisionError as e:
    print(e)

실행 결과

division by zero

여기서

as e

는 발생한 오류 정보를 e라는 변수에 저장하는 것이다.

즉,

ZeroDivisionError 발생
        ↓
오류 정보 → e
        ↓
print(e)

가 된다.


💡 try-except 정리

try:
    ...
except:
    ...

모든 오류 처리

try:
    ...
except ZeroDivisionError:
    ...

특정 오류 처리

try:
    ...
except ZeroDivisionError as e:
    print(e)

특정 오류 + 오류 메시지 확인


2-2. try-finally 문

finally는 오류 발생 여부와 관계없이 항상 실행된다.

기본 구조

try:
    수행할 문장
finally:
    항상 수행할 문장

예를 들어 파일을 열었다면 오류가 발생하더라도 파일을 닫아야 할 수 있다.

try:
    f = open("foo.txt", "w")
finally:
    f.close()

try에서 오류가 발생하더라도

f.close()

는 항상 실행된다.


except와 함께 사용

try:
    print("나누기 전")

    4 / 0

    print("나누기 후")

except ZeroDivisionError:
    print("오류가 발생했습니다.")

finally:
    print("finally 실행!")

실행 결과

나누기 전
오류가 발생했습니다.
finally 실행!

4 / 0에서 오류가 발생했기 때문에

print("나누기 후")

는 실행되지 않는다.

하지만 finally는 무조건 실행된다.

try
 ↓
오류 발생
 ↓
except
 ↓
finally

finally는 오류가 발생하든 발생하지 않든 항상 실행된다.


2-3. 여러 개의 오류 처리하기

하나의 try 안에서도 여러 종류의 오류가 발생할 수 있다.

이럴 때 여러 개의 except를 사용할 수 있다.

try:
    a = [1, 2]

    print(a[3])

    4 / 0

except ZeroDivisionError:
    print("0으로 나눌 수 없습니다.")

except IndexError:
    print("인덱싱 할 수 없습니다.")

실행 결과

인덱싱 할 수 없습니다.

왜 ZeroDivisionError는 발생하지 않았을까?

print(a[3])

에서 먼저 IndexError가 발생했기 때문이다.

오류가 발생하는 순간 try문의 나머지 코드는 실행되지 않는다.

print(a[3])
   ↓
IndexError 발생
   ↓
4 / 0 실행 X
   ↓
except IndexError 실행

오류 메시지까지 확인하기

try:
    a = [1, 2]

    print(a[3])

    4 / 0

except ZeroDivisionError as e:
    print(e)

except IndexError as e:
    print(e)

실행 결과

list index out of range

여러 오류를 한 번에 처리하기

두 오류를 같은 방식으로 처리하고 싶다면 괄호로 묶을 수 있다.

try:
    a = [1, 2]

    print(a[3])

    4 / 0

except (ZeroDivisionError, IndexError) as e:
    print(e)

실행 결과

list index out of range

즉,

except (오류1, 오류2):

형태로 여러 오류를 한 번에 처리할 수 있다.


2-4. try-else 문

else는 try문에서 오류가 발생하지 않았을 경우에만 실행된다.

기본 구조

try:
    수행할 문장

except 발생오류:
    오류 발생 시 수행

else:
    오류가 없을 때 수행

예제

try:
    age = int(input("나이를 입력하세요: "))

except:
    print("입력이 정확하지 않습니다.")

else:
    if age <= 18:
        print("미성년자는 출입금지입니다.")
    else:
        print("환영합니다.")

숫자가 아닌 값을 입력하면

나이를 입력하세요: python

실행 결과

입력이 정확하지 않습니다.

정상적인 숫자를 입력하면

나이를 입력하세요: 20

실행 결과

환영합니다.

동작 구조

try
 ↓
오류 발생?
 ├─ YES → except
 │
 └─ NO  → else

else를 사용하는 이유

다음처럼 작성할 수도 있다.

try:
    age = int(input("나이를 입력하세요: "))

    if age <= 18:
        print("미성년자는 출입금지입니다.")

하지만 try 안에 너무 많은 코드를 넣으면 예상하지 못한 오류까지 except가 처리할 수 있다.

따라서 오류가 발생할 가능성이 있는 코드만 try에 넣고, 정상적으로 실행됐을 때 수행할 코드는 else에 작성할 수 있다.


3. 오류 회피하기

경우에 따라 오류가 발생해도 프로그램을 계속 실행하고 싶을 수 있다.

예를 들어 여러 파일을 읽는 중 하나의 파일이 없어도 다음 파일을 처리해야 할 수 있다.

students = ["김철수", "이영희", "박민수", "최유진"]

for student in students:
    try:
        with open(f"{student}_성적.txt", "r") as f:
            score = f.read()

            print(f"{student}의 성적: {score}")

    except FileNotFoundError:
        print(f"{student}의 성적 파일이 없습니다. 건너뜁니다.")

        continue

파일이 없다면

continue

를 사용해 다음 반복으로 넘어간다.


pass를 이용해 오류 무시하기

오류가 발생해도 아무것도 하지 않고 그냥 넘어갈 수도 있다.

try:
    with open("설정파일.txt", "r") as f:
        config = f.read()

except FileNotFoundError:
    pass

print("프로그램이 정상적으로 실행됩니다.")

파일이 없어 FileNotFoundError가 발생해도

pass

를 사용했기 때문에 프로그램이 종료되지 않는다.

실행 결과

프로그램이 정상적으로 실행됩니다.

pass는 오류를 무시하고 넘어갈 때 사용할 수 있지만 중요한 오류까지 무시할 수 있으므로 주의해야 한다.


4. 오류 일부러 발생시키기

파이썬에서는 raise를 사용해 의도적으로 오류를 발생시킬 수 있다.

raise 오류이름

예를 들어 다음과 같은 Bird 클래스를 만들어 보자.

class Bird:
    def fly(self):
        raise NotImplementedError

NotImplementedError는 반드시 구현해야 하는 기능이 아직 구현되지 않았을 때 사용할 수 있는 예외이다.


상속

class Eagle(Bird):
    pass

eagle = Eagle()

eagle.fly()

Eagle은 Bird를 상속받았지만 fly()를 새로 구현하지 않았다.

따라서

eagle.fly()

를 실행하면 부모 클래스의

raise NotImplementedError

가 실행된다.

실행 결과

NotImplementedError

메서드 오버라이딩

오류가 발생하지 않도록 자식 클래스에서 fly()를 다시 정의한다.

class Eagle(Bird):
    def fly(self):
        print("very fast")

사용

eagle = Eagle()

eagle.fly()

실행 결과

very fast

즉,

Bird.fly()
    ↓
raise NotImplementedError
    ↓
자식 클래스에서 반드시 구현하도록 유도

하고

Eagle.fly()
    ↓
메서드 오버라이딩

으로 직접 기능을 구현한다.

부모 클래스의 메서드를 자식 클래스에서 다시 정의하는 것을 메서드 오버라이딩이라고 한다.


5. 예외 만들기

파이썬에서는 직접 새로운 예외를 만들 수도 있다.

예외는 기본적으로 Exception 클래스를 상속받아 만든다.

class MyError(Exception):
    pass

MyError라는 새로운 오류를 만든 것이다.


사용자 정의 예외 발생시키기

class MyError(Exception):
    pass


def say_nick(nick):
    if nick == "바보":
        raise MyError()

    print(nick)

실행

say_nick("천사")
say_nick("바보")

실행 결과

천사
MyError

"바보"라는 값이 들어오면

raise MyError()

가 실행되면서 직접 만든 MyError가 발생한다.


직접 만든 오류 처리하기

MyError도 다른 오류와 마찬가지로 try-except로 처리할 수 있다.

try:
    say_nick("천사")
    say_nick("바보")

except MyError:
    print("허용되지 않는 별명입니다.")

실행 결과

천사
허용되지 않는 별명입니다.

동작 과정

say_nick("천사")
       ↓
"천사" 출력
       ↓
say_nick("바보")
       ↓
raise MyError()
       ↓
except MyError
       ↓
"허용되지 않는 별명입니다."

오류 메시지 만들기

다음처럼 작성해 보자.

try:
    say_nick("천사")
    say_nick("바보")

except MyError as e:
    print(e)

하지만 현재 MyError에는 출력할 오류 메시지가 없다.

오류 메시지를 직접 지정하려면 __str__() 메서드를 사용할 수 있다.

class MyError(Exception):
    def __str__(self):
        return "허용되지 않는 별명입니다."

이제

try:
    say_nick("천사")
    say_nick("바보")

except MyError as e:
    print(e)

를 실행하면

천사
허용되지 않는 별명입니다.

가 출력된다.


__str__()이란?

__str__()은 객체를 문자열 형태로 표현할 때 호출되는 특수 메서드이다.

여기서는

print(e)

가 실행될 때

MyError.__str__()

이 호출되고

return "허용되지 않는 별명입니다."

가 반환된다.

즉,

print(e)
   ↓
MyError의 __str__() 호출
   ↓
"허용되지 않는 별명입니다."

가 된다.


💡 try-except-else-finally 한 번에 정리

전체 구조를 보면 다음과 같다.

try:
    # 오류가 발생할 가능성이 있는 코드

except 오류:
    # 오류 발생 시 실행

else:
    # 오류가 없을 때 실행

finally:
    # 오류 여부와 관계없이 항상 실행

동작 구조

             try
              ↓
         오류 발생 여부
          ↙       ↘
       발생       없음
        ↓           ↓
     except        else
          ↘       ↙
           finally
              ↓
          항상 실행

💡 주요 오류 정리

오류발생 상황
FileNotFoundError존재하지 않는 파일을 열 때
ZeroDivisionError숫자를 0으로 나눌 때
IndexError존재하지 않는 리스트 인덱스에 접근할 때
NotImplementedError구현해야 하는 기능이 구현되지 않았을 때

💡 예외 처리 핵심 정리

문법설명
try오류가 발생할 가능성이 있는 코드
except오류가 발생했을 때 실행
except 오류 as e발생한 오류 정보를 변수에 저장
else오류가 발생하지 않았을 때 실행
finally오류 여부와 관계없이 항상 실행
pass오류가 발생해도 아무 작업 없이 넘어감
raise오류를 의도적으로 발생
Exception사용자 정의 예외를 만들 때 상속하는 기본 클래스

💡 가장 중요하게 기억할 부분

기본적인 예외 처리는

try:
    4 / 0

except ZeroDivisionError:
    print("0으로 나눌 수 없습니다.")

이다.

try
 ↓
4 / 0 실행
 ↓
ZeroDivisionError 발생
 ↓
except ZeroDivisionError
 ↓
오류 처리

오류 메시지까지 확인하려면

try:
    4 / 0

except ZeroDivisionError as e:
    print(e)

를 사용한다.

그리고 예외 처리 문법의 역할을 한 줄씩 기억하면 다음과 같다.

try     → 일단 실행해 본다.
except  → 오류가 발생하면 처리한다.
else    → 오류가 없으면 실행한다.
finally → 무조건 마지막에 실행한다.
raise   → 내가 직접 오류를 발생시킨다.
profile
성실하자:)

0개의 댓글