[Python] 예외 처리는 중요하다

jul ee·2025년 4월 2일

데이터 성장기

목록 보기
8/139

🖇  무엇이 에러인가
🖇  예외 처리
🖇  에러 발생시키기
🖇  자주 발생하는 에러 종류


예외 처리는 중요하다.

현실의 데이터는 언제나 깨끗하고 완벽하지 않다. 누락된 값, 잘못된 형식, 존재하지 않는 파일, 서버 응답 오류, 나눗셈의 0, 타입 불일치 등 예상치 못한 상황은 언제든 발생할 수 있다.

꼭 파이썬에서만이 아니더라도 시스템이 의도치 않게 동작하는 상황에 대처하기 위해서 예외 처리는 핵심적으로 사용된다.

    실제로 경험했던 상황을 이야기 해보자면,
    JavaScript로 서비스를 구현하면서 예외 처리를 제대로 하지 않은 로직이 있었다.
    서버가 터져서 응답이 들어오지 않았을 때 사용자로부터 UI가 깨진다는 피드백을 받은 적이 있다.

이렇듯 예외 처리 기능은 이렇게 단순한 에러 방지뿐만 아니라, 안정적인 프로그램을 만드는 데 있어 핵심적인 역할을 하게 된다.

이 글에서는
파이썬에서 예외 처리를 해야하는 에러 상황이 무엇인지 정의하고, 올바르게 예외 처리하는 방법을 정리했다.
파이썬을 사용하면서 주요 예외들을 익혀두면 에러 메시지를 빠르게 이해하고 적절히 처리할 수 있을 것이다.



🖇  무엇이 에러인가

[파이썬 공식 문서] 에러와 예외
'에러' 와 '오류' 가 자주 혼용되지만, 공식 문서에 따라 '에러' 로 표기하겠다.

에러란?

:  프로그램을 실행하는 도중 발생하는 문제 상황

:  에러는 프로그램을 강제로 종료시키며, 발생 원인에 따라 두 가지로 나눌 수 있다.

  • 프로그래밍 실행 에 발생하는 에러 → 문법 에러

  • 프로그래밍 실행 에 발생하는 에러 → 예외 또는 런타임 에러


문법 에러(Syntax Error)

마찬가지로 '문법 에러' 와 '구문 에러' 도 자주 혼용되지만, 공식 문에서 따라 '문법 에러' 로 표기하겠다.

문법 에러란?

파이썬 문법에 맞지 않아서 프로그래밍 실행도 전에 오류가 발생하는 것으로,
컴파일 단계에서 발생한다.

  • 자주 발생하는 구문 오류로는 콜론(:) 빠짐, 괄호 및 따옴표 오류, 잘못된 들여쓰기 등이 있다.

프로그래밍이 실행 전에 에러가 나는 것이기 때문에

⇒   따로 예외 처리를 하는 것이 아니라,
⇒   오류가 발생하는 코드를 알맞은 코드로 수정해주는 방법으로 처리

예외(Exception) 또는 런타임 에러(Runtime Error)

예외 또는 런타임 에러란?

실행 중에 발생하는 에러로, 문법은 맞지만 동작 중 에러가 발생하는 것이다.

문법에 문제가 없어도 코드를 실행할 때(런타임) 프로세스에서 발생할 수 있는 예외들을 말한다. 문법 에러를 제외한 나머지 에러 상황을 생각하면 된다.

⇒   이는 예외 처리를 통해 해결한다



🖇  예외 처리

예외 처리(Exceptional Handling)

: 예외(에러 상황)를 해결하는 모든 것

: 예외 발생 시 프로그램이 종료되지 않고 처리될 수 있도록 한다


예외 처리 방법에는 크게 두 가지가 있다.

  1. 조건문
  2. try 구문 사용

조건문

  • 조건문을 활용해서 예외 상황을 처리한다

    • 장점: try 구문을 사용했을 때보다 속도가 약간 빠르다

    • 단점: 예외가 발생할 수 있는 모든 상황을 예측하고, 모두 조건문으로 처리해야 한다

      # 예제. 사용자가 정수를 입력하면 그 정수를 출력하기
      num = input('정수를 입력하세요 : ')
      
      # 입력값이 0과 양의 정수일 때
      if num.isdigit():
          print(f'입력한 정수는 {num}입니다.')
      
      # 입력값이 음의 정수일 때
      # isdigit() 함수는 숫자만 판별하므로 음수를 표시하는 -는 걸러내지 못한다
      elif num.lstrip('-').isdigit():
          print(f'입력한 정수는 {num}입니다.')
      
      # 입력값이 정수가 아닌 경우 예외 처리
      else:
          print('정수가 아닙니다.')

      예제에서 알 수 있듯이,
      조건문을 사용한 예외 처리를 하게 되면 정수가 아닌 입력값을 하나하나 가정하고 모든 상황을 고려한 예외 처리를 해야 한다.



try - except 구문

  • try - except 구문을 활용하면 어떤 상황에서 예외가 발생하는지 완벽하게 이해하지 않아도 프로그램이 강제로 죽어 버리는 상황을 방지할 수 있다.
    try:
        예외가 발생할 수 있는 코드
    except (발생 오류 (as 발생 오류 변수)):
        예외가 발생했을 때 실행할 코드
  • 🧐 Q. try에서 만든 변수는 바깥에서 사용할 수 있을까?
    • try는 함수가 아니므로 스택 프레임을 만들지 않는다.
    • 따라서 try 안에서 변수를 만들더라도 try 바깥에서 사용할 수 있다.
    • 물론 except, else, finally에서도 사용할 수 있다.

try - except (+ pass) 구문

  • 예외가 발생한 코드가 크게 중요하지 않은 경우,
  • 일단 프로그램이 강제 종료되는 것을 막는 목적으로 사용한다.
    • except 구문에 아무 것도 넣지 않고 try 구문 사용

    • 구문 오류를 방지하기 위해 except 구문에  pass 키워드를 넣어 준다

    • 오류 발생 시에도, pass 키워드 이후의 코드는 동작한다

      try:
          예외가 발생할 수 있는 코드
      except:
          pass

try - except - else 구문

  • else를 추가하면 예외가 발생하지 않았을 때 실행할 코드를 지정할 수 있다

    • 예외 발생 가능성이 있는 코드만 try 구문 내부에 넣고,

    • 나머지는 모두 else 구문으로 빼는 경우가 많다

      try:
          예외가 발생할 수 있는 코드
      except:
          에외가 발생했을 때 실행할 코드
      else:
          예외가 발생하지 않았을 때 실행할 코드
  • 🧐 Q. 에러가 발생하면 어차피 try 안의 코드가 중단될 텐데, 굳이 else는 왜 쓰는 걸까?

    • 예외가 날 수 있는 부분과 그렇지 않은 부분을 구분해서 코드 가독성과 명확성을 높이기 위해 사용한다.

      블록역할
      try예외가 발생할 수 있는 (위험한) 코드를 넣는 곳
      except예외가 발생했을 때 실행할 코드
      else예외가 발생하지 않았을 때만 실행할 (안전한) 코드

try - except - else - finally 구문

  • finally 구문은 예외가 발생하든 발생하지 않든 무조건 실행하고 싶은 코드가 있을 경우 사용한다
    try:
        예외가 발생할 수 있는 코드
    except:
        예외가 발생했을 때 실행할 코드
    else:
        예외가 발생하지 않았을 때 실행할 코드
    finally:
        에외와 관계없이 무조건 실행되는 코드


예외 처리 구문의 규칙

예외 처리 구문들은 아래의 규칙을 따른다.

  1. try 구문은 단독으로 쓰일 수 없고 except 또는 finally 구문과 함께 쓰여야 한다.
  2. else 구문은 except와 함께 쓰이고, except 구문 뒤에 사용해야 한다.

예외 처리 구문 규칙에 의해,
아래 다섯 가지 조합 외의 구문은 오류가 발생한다.

try + except

try + except + else

try + except + finally

try + except + else + finally

try + finally



🖇  예외 발생시키기

프로그램에 일부러 예외를 발생하고 싶을 때 사용한다.

  • raise에 예외를 지정하고 에러 메시지를 넣는다

    try:
     x = int(input('3의 배수를 입력하세요: '))
     if x % 3 != 0:                             # x가 3의 배수가 아니면
         raise Exception('3의 배수가 아닙니다.')    # 예외를 발생시킨다
     print(x)
    
     print('예외가 발생했습니다.', e)
    3의 배수를 입력하세요: 5 (입력)
    예외가 발생했습니다. 3의 배수가 아닙니다.

    예제를 살펴보면
    5는 3의 배수가 아니므로 raise Exception('3의 배수가 아닙니다.')로 예외를 발생시킨다.
    이때 Exception에 넣은 에러 메시지는 except Exception as e:e에 들어간다.
    raise로 예외를 발생시키면 raise 아래에 있는 코드는 실행되지 않고 바로 except로 넘어간다.
    [참고] 파이썬 코딩 도장



🖇  자주 발생하는 예외 종류

예외는 클래스 상속으로 구현되며 아래와 같은 계층으로 이루어져 있다.

일반적으로 파이썬에서 새로운 예외를 만들 때는 Exception을 상속받아서 구현한다.

         전체 계층도는 [파이썬 공식 문서] Exception hierarchy 참고

    # 예외 계층 구조
    BaseException
    ├── SystemExit
    ├── KeyboardInterrupt
    ├── GeneratorExit
    └── Exception
        ├── ArithmeticError
        │   ├── ZeroDivisionError
        │   └── OverflowError
        ├── LookupError
        │   ├── IndexError
        │   └── KeyError
        ├── SyntaxError
        │   ├── IndentationError
        ├── TypeError
        ├── ValueError
        ├── NameError
        ├── FileNotFoundError
        ├── ImportError
        ├── AttributeError
        ├── AssertionError
        └── ...

[파이썬 공식 문서] 내장 예외

SyntaxError

  • 문법 에러로, 잘못된 문법으로 코드 작성 시 발생

NameError

  • 참조 변수없음
  • 선언한 적 없는 변수 등 출력 시 발생
print(a)

NameError: name 'a' is not defined

ZeroDivisionError

  • 0으로 나눌 수 없음
  • 어떤 수를 0으로 나눌 경우 발생
  • 문법적으로는 오류가 없지만 실행하면 런타임 오류 발생
print(10 / 0)

ZeroDivisionError: division by zero

IndexError

  • 인덱스 범위 벗어남
  • 리스트 등에 없는 원소를 호출할 경우 발생
a = [10, 20, 30]
print(a[4])

IndexError: list index out of range

ValueError : 참조 값이 없음

  • 리스트 등에 없는 값을 참조하는 경우 발생
a = [10, 20, 30]
a.remove(40)

ValueError: list.remove(x): x not in list

KeyError

  • 키 없음 에러 (주로 딕셔너리 사용시)
  • 딕셔너리에 없는 키를 호출할 때 발생
    • get() 메소드를 활용하여 해결할 수 있다
    • mydict.get('키') -> 키가 없는 경우 None 반환
mydict = {'Kim': 1, 'Lee': 2}
print(mydict['Park'])

KeyError: 'Park'

AttributeError

  • 모듈과 클래스에서 나올 수 있는 오류로 잘못된 속성을 사용할 때 발생
import time
time.month()

AttributeError: module 'time' has no attribute 'month'

FileNotFoundError

  • 파일 못 찾음
  • 해당 경로에 파일이나 디렉토리가 없는 경우 발생
f = open('ex.txt', 'r')

FileNotFoundError: [Errno 2] No such file or directory: 'ex.txt'

TypeError

  • 연산 등을 할 때 타입이 서로 맞지 않을 때 발생
a = [1, 2]
b = "Hello"
print(a + b)

TypeError: can only concatenate list (not "str") to list


참고)  예외 처리 전략 (EAFP vs LBYL)

EAFP

“It’s Easier to Ask Forgiveness than Permission”

  • 일단 수행(try)시키고, 만약 에러가 발생하면 그때 처리(except)하는 전략
    • 항상 예외가 발생하지 않을 것으로 가정하고 나름대로의 완벽한 코딩을 한다.
    • 그 후 코드 실행을 했는데 런타임 에러가 발생한다면, 그때 예외처리 코딩을 하는 게 좋다는 전략이다.

LBYL

“Look Before You Leap”

  • 어떤 코드를 실행하기 전에 에러가 발생할 수 있는 조건을 미리 다 따져보고 그걸 어떻게 처리할지 조건문(if문) 스타일로 코딩하는 전략


언제나 그렇듯 완벽한 정답은 없지만

주어진 상황에서 두 전략을 적절히 고려하여 예외 상황을 효과적으로 처리할 수 있게 되길 바란다.


profile
AI에 관심을 가지고, 데이터로 가치를 만들어 나가는 과정을 기록합니다.

0개의 댓글