Python for AI, Embedded/Python: Core & Automation

파이썬(Python) 예외 처리 완벽 가이드: try-except 문법부터 커스텀 예외 만들기까지

임베디드 친구 2025. 7. 3. 20:52
반응형

지난 시간까지 우리는 하드디스크에 데이터를 영구적으로 기록하고 불러오는 파일 입출력(I/O) 테크닉을 함께 살펴보았습니다. 프로그램을 개발하다 보면 완벽하게 코드를 짰다고 생각한 순간에도 예기치 못한 복병을 만나기 일쑤입니다. 사용자가 숫자를 넣어야 할 칸에 문자를 입력하거나, 네트워크 연결이 갑자기 끊어지거나, 읽으려는 파일이 경로에서 사라지는 상황들이 대표적입니다. 이때 방어벽이 없다면 프로그램은 보기 싫은 붉은색 에러 로그를 뿜으며 그 자리에서 셧다운 되고 맙니다. 이는 사용자 경험에 치명적인 오점을 남기게 됩니다. 파이썬은 이런 돌발 상황을 우아하게 받아치고 프로그램의 생명력을 유지할 수 있는 강력한 '예외 처리(Exception Handling)' 메커니즘을 제공합니다. 오늘 소프트웨어 공장에서는 기초적인 try-except 문법부터 고급 예외 제어 테크닉까지 하나씩 쉽게 풀어드리겠습니다.

Generated by Gemini AI.

📌 핵심 요약 3줄

  • 예외 처리는 프로그램 실행 중 발생하는 런타임 오류를 가로채어 비정상 종료를 막고, 후속 조치를 취할 수 있도록 돕는 방어 시스템입니다.
  • try-except 구조에 else(에러가 없을 때 실행)와 finally(에러 유무와 상관없이 무조건 실행) 블록을 결합하면 완벽한 자원 관리 사이클을 만들 수 있습니다.
  • 내장 예외로 표현하기 부족한 비즈니스 로직상의 오류는 Exception 클래스를 상속받아 나만의 '사용자 정의 예외'로 깔끔하게 설계할 수 있습니다.

1. 한눈에 보는 파이썬 예외 처리 구조 및 자주 발생하는 예외

파이썬 예외 처리 체계를 이루는 핵심 키워드들과 실무에서 가장 자주 마주치는 대표적인 예외 종류를 표로 정리했습니다.

① 예외 처리 제어 블록 요약

키워드 역할 및 실행 조건 필수 여부 실무 활용 시나리오
try 예외(오류)가 발생할 가능성이 있는 코드를 실행하는 영역 필수 파일 읽기, 웹 크롤링, 데이터 형변환 등
except try 블록 안에서 에러가 터졌을 때 구조 요청을 받는 영역 필수 에러 메시지 출력, 기본값 할당, 로그 기록
else try 블록이 에러 없이 완벽하게 성공했을 때만 실행되는 영역 선택 예외 발생이 없을 때 수행할 후속 비즈니스 로직 배치
finally 에러 발생 여부와 상관없이 무조건 마지막에 실행되는 영역 선택 열린 파일 닫기(close), 데이터베이스 연결 해제

② 실무 단골 파이썬 내장 예외(Exception) 종류

예외 클래스명 발생 원인 예시 상황
ZeroDivisionError 숫자를 0으로 나누려고 시도할 때 10 / 0
ValueError 데이터 타입은 맞지만 변환할 수 없는 부적절한 값일 때 int("Python")
IndexError 리스트나 튜플의 범위를 벗어난 인덱스에 접근할 때 data = [1, 2] 접근 시 data[5]
KeyError 딕셔너리에 존재하지 않는 키를 조회할 때 user = {} 접근 시 user['email']
FileNotFoundError 지정한 경로에 읽으려는 파일이 존재하지 않을 때 open("missing.txt", "r")

2. 예외 처리 기본기 및 다중 예외 분기 매커니즘

하나의 소스코드 안에서도 여러 종류의 에러가 도사리고 있을 수 있습니다. 파이썬에서는 except 문을 여러 개 덧붙여 에러 유형별로 맞춤형 처리가 가능합니다.

Python
 
# 다중 예외 처리 및 예외 객체 활용 패턴
try:
    # 1. ValueError 발생 가능 구간
    age = int(input("나이를 숫자로 입력하세요: "))
    
    # 2. ZeroDivisionError 발생 가능 구간
    score = 100 / age
    print(f"당신의 점수 가중치는 {score}입니다.")

except ValueError as e:
    # 'as e'를 쓰면 파이썬이 제공하는 상세 에러 메시지를 꺼내볼 수 있습니다.
    print(f"[입력 오류] 숫자만 입력해야 합니다. (상세 원인: {e})")

except ZeroDivisionError:
    print("[연산 오류] 나이는 0이 될 수 없습니다.")

except Exception as e:
    # 위에서 걸러지지 않은 예상치 못한 모든 에러를 마지막 방어선에서 포획합니다.
    print(f"[알 수 없는 오류] 시스템에 문제가 발생했습니다: {e}")

3. 완성형 구조: else와 finally의 실전 조합

에러가 없을 때의 흐름과 자원 해제를 보장하는 finally를 조합하면 코드가 몰라보게 안전해집니다.

Python
 
try:
    print("--- 파일 연결 시도 ---")
    file = open("data.txt", "r", encoding="utf-8")
except FileNotFoundError:
    print("[오류] 읽을 파일이 준비되지 않았습니다.")
else:
    # 예외 없이 파일이 정상적으로 열렸을 때만 안심하고 실행할 코드를 둡니다.
    content = file.read()
    print("파일 내용 읽기 성공:", content)
finally:
    # 파일이 열렸든 안 열렸든, 에러가 났든 안 났든 무조건 실행됩니다.
    # 연결을 안전하게 끊어주는 자원 정리의 공간입니다.
    try:
        file.close()
        print("--- 파일 자원 반납 완료 ---")
    except NameError:
        # 파일 열기 자체를 실패해 변수가 생성되지 않았을 때의 예외 방어
        print("--- 파일이 열린 적이 없어 닫지 않고 종료합니다. ---")

4. 고수의 영역: 사용자 정의 예외와 raise 문

현업에서는 문법적인 에러 외에도 "인증되지 않은 유저의 접근", "잔액 부족"처럼 프로그램 비즈니스 규칙 위반을 에러로 다루어야 할 때가 많습니다. 이때는 Exception을 상속받아 커스텀 에러를 선언하고 raise 키워드로 강제 발생시킵니다.

Python
 
# 나만의 예외 클래스 설계 (기본 Exception 클래스를 상속)
class InvalidAgeError(Exception):
    def __init__(self, age, message="가입 불가능한 연령입니다."):
        self.age = age
        self.message = message
        super().__init__(self.message)

def check_voting_age(age):
    if age < 0:
        raise ValueError("나이는 음수일 수 없습니다.")
    if age < 19:
        # 특정 조건에서 내가 만든 커스텀 에러를 의도적으로 터뜨림!
        raise InvalidAgeError(age, f"만 19세 미만({age}세)은 투표권이 없습니다.")
    print("투표 가능 대상자입니다.")

# 커스텀 예외 포획 테스트
try:
    user_age = 16
    check_voting_age(user_age)
except InvalidAgeError as e:
    print(f"[가입 제한] {e} (입력된 나이: {e.age}세)")
except ValueError as e:
    print(f"[데이터 오류] {e}")

5. 개발을 위한 팁

  • try 블록의 몸집은 최대한 가볍게 유지하세요: 에러가 날 것 같은 찜찜한 느낌이 든다고 해서 함수 전체나 수십 줄짜리 비즈니스 로직 전체를 큰 try 구문 하나로 감싸버리는 코딩 스타일은 지양해야 합니다. try 블록이 너무 비대해지면 정확히 어느 라인에서 예외가 발생했는지 추적하기가 매우 까다로워지고, 의도치 않은 행위까지 에러로 묶여 묻혀버릴 수 있습니다. 딱 에러 발생 가능성이 있는 한두 줄의 핵심 코드(예: 형변환, 파일 오픈, API 호출)만 try에 넣고 나머지 정상 흐름 코드는 else 블록으로 과감히 빼는 것이 훌륭한 아키텍처입니다.
  • 커스텀 예외를 만들 때는 접미사로 Error를 붙여주세요: 협업 프로젝트에서 다른 개발자가 내가 만든 예외 클래스를 코드에서 마주쳤을 때, 이것이 단순 클래스인지 예외 객체인지 한눈에 인지할 수 있어야 합니다. 파이썬 공식 스타일 가이드(PEP 8)에 따르면 사용자 정의 예외 클래스의 이름 끝에는 InvalidAgeError, NetworkTimeoutError와 같이 항상 Error라는 접미사를 붙여주는 것이 개발자들 사이의 묵시적인 약속입니다.

6. 흔히 하는 실수

  • 아무것도 명시하지 않은 무분별한 except: 구문 사용: 에러 종류를 적지 않고 except:라고만 적어두면 파이썬의 최상위 예외 시스템이 가동되어 프로그램 내부의 모든 오류를 묻지도 따지지도 않고 흡수해 버립니다. 심지어 개발자가 터미널에서 프로그램을 강제 종료하기 위해 누르는 컨트롤 + C 키(KeyboardInterrupt)마저 먹통으로 만들어 버리는 부작용이 생깁니다. 디버깅할 때 에러 원인을 찾을 수 없게 만드는 주범이므로, 정말 특별한 경우가 아니라면 최소한 except Exception as e: 형태로 작성하여 어떤 에러가 잡혔는지 로그를 남기거나 특정 예외 클래스를 명시해 주어야 합니다.
  • finally 블록 안에서 return 문을 사용하는 대참사: 만약 함수 내부에 예외 처리가 있고 finally 구문 안에 return 값을 적어두면, try나 except 블록에서 아무리 열심히 데이터를 계산해서 return을 날렸어도 그 결과값들이 전부 증발하고 무조건 finally에 적힌 return 값으로 최종 덮어쓰기 처리가 됩니다. 파이썬 매커니즘상 finally는 함수의 탈출 직전에도 무조건 우선 실행권을 쥐기 때문입니다. 흐름을 왜곡하는 치명적인 버그를 유발할 수 있으니 finally 안에는 오직 자원 정리 코드만 넣고 return 문은 절대 배치하지 마세요.

💡 맺음말

이번 포스팅에서는 프로그램의 안정성을 철통보안으로 격상시켜 주는 파이썬의 예외 처리 시스템 메커니즘을 꼼꼼하게 정복해 보았습니다. 오류를 무작정 피하거나 방치하지 않고 try-except-else-finally 파이프라인을 구축해 우아하게 제어하는 순간, 여러분의 소스코드는 한 단계 더 프로페셔널한 완성도를 갖추게 됩니다.

소프트웨어 공장의 파이썬 기초 연재가 드디어 완성도 높은 궤도에 올랐습니다!

예외 처리 도중 복합 에러가 발생했거나 raise 문 활용법이 헷갈리신다면 언제든 아래 댓글 창에 질문을 남겨주세요. 성심껏 답변해 드리겠습니다. 감사합니다!

반응형