
22. 에러와 예외처리 - 프로그램이 죽지 않게
프로그램은 실행 중에 에러를 만날 수밖에 없다. 사용자가 숫자 대신 문자를 입력하거나, 없는 파일을 열려고 하거나. 중요한 건 에러가 나지 않게 하는 것이 아니라, 에러가 나도 프로그램이 죽지 않게 하는 것이다.
에러의 종류
파이썬에서 에러는 크게 문법 에러와 예외(Exception)로 나뉜다.
문법 에러 (SyntaxError)
코드 자체가 잘못되어 실행조차 안 되는 에러. 오타, 괄호 미닫힘, 콜론 빠뜨림 등.
# SyntaxError 예시
print("Hello" # 괄호 안 닫음
if True # 콜론 빠뜨림
print("hi")
def func( # 괄호 안 닫음
⚠️ SyntaxError는
try/except로 잡을 수 없다. 코드를 수정해야 한다.
예외 (Exception)
코드 문법은 맞지만, 실행 중에 발생하는 에러.
# TypeError — 타입이 맞지 않음
print("나이: " + 25) # str + int 불가
# ValueError — 값이 적절하지 않음
int("hello") # 문자열을 정수로 변환 불가
# IndexError — 인덱스 범위 초과
lst = [1, 2, 3]
print(lst[10]) # 존재하지 않는 인덱스
# KeyError — 딕셔너리에 없는 키
d = {"name": "철수"}
print(d["age"]) # 존재하지 않는 키
# ZeroDivisionError — 0으로 나눔
print(10 / 0) # 0으로 나눌 수 없음
# FileNotFoundError — 파일이 없음
open("없는파일.txt") # 존재하지 않는 파일
# NameError — 정의되지 않은 이름
print(undefined_variable) # 정의 안 된 변수

try / except — 예외 잡기
try 블록 안의 코드에서 에러가 발생하면, 프로그램이 죽지 않고 except 블록으로 이동한다.
try:
result = 10 / 0
except:
print("에러가 발생했습니다!")
print("프로그램 계속 실행...")
# 에러가 발생했습니다!
# 프로그램 계속 실행...
특정 에러만 잡기
try:
num = int(input("숫자 입력: "))
result = 100 / num
print(f"결과: {result}")
except ValueError:
print("숫자를 입력해주세요!")
except ZeroDivisionError:
print("0으로 나눌 수 없습니다!")
에러 메시지 가져오기
try:
result = 10 / 0
except ZeroDivisionError as e:
print(f"에러 발생: {e}")
# 에러 발생: division by zero
여러 에러 한번에 잡기
try:
# 여러 종류의 에러가 날 수 있는 코드
data = [1, 2, 3]
print(data[int("abc")])
except (ValueError, IndexError) as e:
print(f"에러: {e}")
try / except / else / finally

try:
num = int(input("숫자 입력: "))
result = 100 / num
except ValueError:
print("숫자를 입력해주세요!")
except ZeroDivisionError:
print("0으로 나눌 수 없습니다!")
else:
# 에러가 없을 때만 실행
print(f"결과: {result}")
finally:
# 에러 여부와 상관없이 항상 실행
print("프로그램 종료")
else — 에러가 없을 때만
try:
f = open("data.txt", "r")
except FileNotFoundError:
print("파일이 없습니다!")
else:
# 파일 열기 성공 시에만 실행
content = f.read()
print(content)
f.close()
finally — 무조건 실행
finally는 에러가 나든 안 나든 무조건 실행된다. 파일 닫기, 연결 해제 등 정리 작업에 사용한다.
f = None
try:
f = open("data.txt", "r")
content = f.read()
print(content)
except FileNotFoundError:
print("파일이 없습니다!")
finally:
if f:
f.close()
print("파일을 닫았습니다.")
raise — 직접 에러 발생시키기
raise로 의도적으로 에러를 발생시킬 수 있다.
def set_age(age):
if age < 0:
raise ValueError("나이는 음수일 수 없습니다!")
if age > 150:
raise ValueError("나이가 너무 큽니다!")
print(f"나이가 {age}으로 설정되었습니다.")
try:
set_age(25) # 나이가 25으로 설정되었습니다.
set_age(-5) # ValueError 발생!
except ValueError as e:
print(f"에러: {e}")
# 에러: 나이는 음수일 수 없습니다!
def divide(a, b):
if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):
raise TypeError("숫자만 입력 가능합니다!")
if b == 0:
raise ZeroDivisionError("0으로 나눌 수 없습니다!")
return a / b
try:
print(divide(10, 3)) # 3.333...
print(divide("10", 3)) # TypeError!
except (TypeError, ZeroDivisionError) as e:
print(f"에러: {e}")
커스텀 예외 만들기
Exception을 상속받아 나만의 예외를 만들 수 있다.
class InsufficientBalanceError(Exception):
"""잔액 부족 예외"""
def __init__(self, balance, amount):
self.balance = balance
self.amount = amount
super().__init__(f"잔액 부족! 잔액: {balance}원, 출금: {amount}원")
class InvalidAmountError(Exception):
"""잘못된 금액 예외"""
pass
def withdraw(balance, amount):
if amount <= 0:
raise InvalidAmountError("출금액은 0보다 커야 합니다!")
if amount > balance:
raise InsufficientBalanceError(balance, amount)
return balance - amount
# 사용
try:
balance = 10000
balance = withdraw(balance, 3000)
print(f"출금 성공! 잔액: {balance}원") # 7000원
balance = withdraw(balance, 50000)
except InsufficientBalanceError as e:
print(f"출금 실패: {e}")
print(f" 현재 잔액: {e.balance}원, 요청 금액: {e.amount}원")
except InvalidAmountError as e:
print(f"입력 오류: {e}")
# 출금 실패: 잔액 부족! 잔액: 7000원, 출금: 50000원
# 현재 잔액: 7000원, 요청 금액: 50000원
실전 패턴
패턴 1: 사용자 입력 검증
def get_integer(prompt):
"""정수를 입력받을 때까지 반복"""
while True:
try:
return int(input(prompt))
except ValueError:
print("정수를 입력해주세요!")
# age = get_integer("나이 입력: ")
# print(f"입력한 나이: {age}")
패턴 2: 파일 읽기 안전하게
def read_file_safe(filepath):
"""파일을 안전하게 읽기"""
try:
with open(filepath, "r", encoding="utf-8") as f:
return f.read()
except FileNotFoundError:
print(f"파일을 찾을 수 없습니다: {filepath}")
return None
except PermissionError:
print(f"파일 읽기 권한이 없습니다: {filepath}")
return None
except UnicodeDecodeError:
print(f"파일 인코딩 오류: {filepath}")
return None
content = read_file_safe("data.txt")
if content:
print(content)
패턴 3: 딕셔너리 접근
user = {"name": "김철수", "age": 25}
# KeyError 방지 방법 1: try/except
try:
email = user["email"]
except KeyError:
email = "이메일 없음"
# KeyError 방지 방법 2: get() 메서드 (더 간편)
email = user.get("email", "이메일 없음")
print(email) # 이메일 없음
패턴 4: 리스트 안전 접근
def safe_get(lst, index, default=None):
"""리스트에서 안전하게 값 가져오기"""
try:
return lst[index]
except IndexError:
return default
numbers = [10, 20, 30]
print(safe_get(numbers, 1)) # 20
print(safe_get(numbers, 10)) # None
print(safe_get(numbers, 10, 0)) # 0
패턴 5: 여러 작업 순차 처리
def process_data(data_list):
"""데이터를 하나씩 처리, 에러 나면 건너뛰기"""
results = []
errors = []
for i, item in enumerate(data_list):
try:
result = int(item) ** 2
results.append(result)
except (ValueError, TypeError) as e:
errors.append(f"[{i}] {item}: {e}")
return results, errors
data = ["3", "5", "abc", "7", None, "10"]
results, errors = process_data(data)
print(f"성공: {results}") # [9, 25, 49, 100]
print(f"실패: {errors}")
# ['[2] abc: invalid literal...', '[4] None: int() argument...']
직접 해보기
문제 1. 사용자에게 두 수를 입력받아 나눗셈을 수행하는 프로그램을 만들어보자. ValueError와 ZeroDivisionError를 처리할 것.
문제 2. 리스트에서 인덱스로 값을 가져오는 safe_index(lst, idx) 함수를 만들어보자. IndexError 시 "인덱스 초과"를 반환.
문제 3. 점수(0~100)를 입력받아 학점을 반환하는 함수를 만들어보자. 범위 밖이면 ValueError를 raise할 것.
문제 4. NegativeNumberError라는 커스텀 예외를 만들고, 음수가 입력되면 이 예외를 발생시키는 sqrt_safe(n) 함수를 만들어보자.
문제 5. 문자열 리스트를 정수로 변환하되, 변환 실패한 항목은 0으로 처리하는 convert_to_int(str_list) 함수를 만들어보자.
정답 보기
# 문제 1
def safe_divide():
try:
a = float(input("첫 번째 수: "))
b = float(input("두 번째 수: "))
result = a / b
except ValueError:
print("숫자를 입력해주세요!")
except ZeroDivisionError:
print("0으로 나눌 수 없습니다!")
else:
print(f"{a} / {b} = {result}")
# safe_divide()
# 문제 2
def safe_index(lst, idx):
try:
return lst[idx]
except IndexError:
return "인덱스 초과"
data = [10, 20, 30]
print(safe_index(data, 1)) # 20
print(safe_index(data, 10)) # 인덱스 초과
# 문제 3
def get_grade(score):
if not isinstance(score, (int, float)):
raise TypeError("숫자만 입력 가능합니다!")
if score < 0 or score > 100:
raise ValueError(f"점수는 0~100 사이여야 합니다: {score}")
if score >= 90: return "A"
elif score >= 80: return "B"
elif score >= 70: return "C"
elif score >= 60: return "D"
else: return "F"
try:
print(get_grade(85)) # B
print(get_grade(150)) # ValueError!
except ValueError as e:
print(f"에러: {e}")
# 문제 4
class NegativeNumberError(Exception):
pass
import math
def sqrt_safe(n):
if n < 0:
raise NegativeNumberError(f"음수의 제곱근은 구할 수 없습니다: {n}")
return math.sqrt(n)
try:
print(sqrt_safe(16)) # 4.0
print(sqrt_safe(-4)) # NegativeNumberError!
except NegativeNumberError as e:
print(f"에러: {e}")
# 문제 5
def convert_to_int(str_list):
result = []
for item in str_list:
try:
result.append(int(item))
except (ValueError, TypeError):
result.append(0)
return result
data = ["10", "abc", "30", None, "50", "xyz"]
print(convert_to_int(data)) # [10, 0, 30, 0, 50, 0]
오늘의 정리
| 항목 | 내용 |
|---|---|
| SyntaxError | 문법 오류. 코드 자체가 잘못됨. try/except 불가 |
| 예외 (Exception) | 실행 중 발생하는 에러. TypeError, ValueError 등 |
| try/except | 에러 발생 시 except 블록으로 이동. 프로그램 유지 |
| else | 에러가 없을 때만 실행되는 블록 |
| finally | 에러 여부와 상관없이 항상 실행. 정리 작업용 |
| raise | 의도적으로 에러 발생. raise ValueError("메시지") |
| 커스텀 예외 | class MyError(Exception): pass |
| as e | except Error as e:로 에러 메시지 가져오기 |
다음 편 예고: 클래스와 객체지향 (1) - 나만의 타입 만들기
파이썬의 모든 것은 객체다. 이제 직접 클래스를 만들어보자. 객체지향 프로그래밍의 첫 발을 내딛는다.
태그: 파이썬 Python 파이썬독학 예외처리 try except finally 에러 raise 파이썬기초 IT교육
'Python' 카테고리의 다른 글
| 24. 정규표현식 - 문자열 처리의 끝판왕 (0) | 2026.02.22 |
|---|---|
| 23. 파일 읽기/쓰기 - 데이터를 저장하자 (0) | 2026.02.22 |
| 21. 모듈과 패키지 - 남이 만든 코드 활용하기 (0) | 2026.02.22 |
| 20. 데코레이터와 제너레이터 - 중급으로 가는 관문 (1) | 2026.02.22 |
| 19. 재귀 함수 - 자기 자신을 호출하는 함수 (0) | 2026.02.22 |
댓글