오류는 반드시 발생한다
완벽한 코드를 써도 예상 못한 오류는 발생합니다. 파일이 없을 수도, 네트워크가 끊길 수도, 사용자가 이상한 값을 입력할 수도 있습니다.
# ❌ 예외 처리 없음 → 프로그램 전체 종료
number = int(input("숫자 입력: ")) # "abc" 입력하면 ValueError!
print(f"입력값: {number}")
# 이후 코드가 전혀 실행되지 않음
# ✅ 예외 처리 → 오류 후에도 계속 실행
try:
number = int(input("숫자 입력: "))
print(f"입력값: {number}")
except ValueError:
print("숫자를 입력해주세요.")
print("프로그램 계속 실행 중") # 항상 실행됨
try / except 기본 구조
flowchart TB
TRY["try 블록\n오류가 날 수 있는 코드"]
TRY -->|"성공"| ELSE["else 블록\n오류 없을 때 실행"]
TRY -->|"오류 발생"| EXCEPT["except 블록\n오류 처리"]
ELSE & EXCEPT --> FINALLY["finally 블록\n항상 실행"]
try:
result = 10 / 0
except ZeroDivisionError:
print("0으로 나눌 수 없습니다.")
else:
print(f"결과: {result}") # 오류 없을 때만
finally:
print("항상 실행") # 오류 유무 관계없이
주요 내장 예외
# ValueError: 잘못된 값
int("hello") # ValueError: invalid literal
# TypeError: 잘못된 타입
"문자" + 123 # TypeError: can only concatenate str (not "int")
# KeyError: 딕셔너리에 없는 키
d = {"a": 1}
d["b"] # KeyError: 'b'
# IndexError: 리스트 범위 초과
lst = [1, 2, 3]
lst[10] # IndexError: list index out of range
# FileNotFoundError: 파일 없음
open("없는파일.txt") # FileNotFoundError
# ZeroDivisionError: 0으로 나눔
10 / 0 # ZeroDivisionError
# AttributeError: 없는 속성/메서드
None.upper() # AttributeError: 'NoneType' has no attribute 'upper'
여러 예외 처리
def safe_divide(a, b):
try:
result = a / b
return result
except ZeroDivisionError:
print("0으로 나눌 수 없습니다.")
return None
except TypeError:
print("숫자만 입력 가능합니다.")
return None
# 여러 예외를 한 번에 처리
try:
value = int(input("숫자: "))
result = 100 / value
except (ValueError, ZeroDivisionError) as e:
print(f"오류 발생: {e}")
# 모든 예외 잡기 (마지막 수단)
try:
risky_operation()
except Exception as e:
print(f"예상치 못한 오류: {type(e).__name__}: {e}")
예외 정보 활용
try:
with open("data.json", "r") as f:
import json
data = json.load(f)
except FileNotFoundError as e:
print(f"파일을 찾을 수 없습니다: {e.filename}")
except json.JSONDecodeError as e:
print(f"JSON 파싱 오류 (줄 {e.lineno}): {e.msg}")
커스텀 예외 만들기
도메인에 맞는 의미 있는 예외를 정의합니다.
class ValidationError(Exception):
"""입력값 검증 실패"""
pass
class APIError(Exception):
"""API 호출 오류"""
def __init__(self, status_code: int, message: str):
self.status_code = status_code
super().__init__(f"[{status_code}] {message}")
# 사용
def validate_age(age: int):
if age < 0:
raise ValidationError("나이는 0 이상이어야 합니다.")
if age > 150:
raise ValidationError("유효하지 않은 나이입니다.")
return age
try:
validate_age(-5)
except ValidationError as e:
print(f"검증 오류: {e}")
실전 패턴: LLM API 오류 처리
import time
from openai import OpenAI, RateLimitError, APIConnectionError
client = OpenAI()
def call_llm_safely(prompt: str, max_retries: int = 3) -> str:
for attempt in range(max_retries):
try:
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}]
)
return response.choices[0].message.content
except RateLimitError:
wait = 2 ** attempt # 1초, 2초, 4초 ...
print(f"요청 한도 초과. {wait}초 후 재시도 ({attempt + 1}/{max_retries})")
time.sleep(wait)
except APIConnectionError:
print("네트워크 오류. 재시도 중...")
time.sleep(1)
except Exception as e:
print(f"예상치 못한 오류: {e}")
raise # 알 수 없는 오류는 다시 올림
raise Exception("최대 재시도 횟수 초과")
로깅: 오류를 파일에 기록
print로 오류를 출력하면 나중에 추적이 어렵습니다.
import logging
# 로거 설정
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
handlers=[
logging.FileHandler("app.log", encoding="utf-8"),
logging.StreamHandler() # 콘솔에도 출력
]
)
logger = logging.getLogger(__name__)
# 사용
def process_data(data):
logger.info(f"데이터 처리 시작: {len(data)}건")
try:
result = expensive_operation(data)
logger.info("처리 완료")
return result
except Exception as e:
logger.error(f"처리 실패: {e}", exc_info=True) # 스택 트레이스 포함
raise
# 로그 레벨
logger.debug("디버그 정보") # 개발 시 상세 정보
logger.info("일반 정보") # 정상 동작 기록
logger.warning("경고") # 문제 가능성
logger.error("오류") # 오류 발생
logger.critical("심각한 오류") # 프로그램 중단 수준
assert: 개발 중 조건 검증
def calculate_discount(price: float, rate: float) -> float:
assert 0 <= rate <= 1, f"할인율은 0~1 사이여야 합니다. 입력값: {rate}"
assert price >= 0, "가격은 0 이상이어야 합니다."
return price * (1 - rate)
try:
calculate_discount(10000, 1.5) # AssertionError
except AssertionError as e:
print(f"검증 실패: {e}")
개발·테스트 단계에서 잘못된 입력을 빠르게 발견하는 데 유용합니다.
정리
| 구문 | 역할 |
|---|---|
try | 오류 가능성 있는 코드 |
except ExceptionType | 특정 오류 처리 |
except Exception as e | 오류 객체 변수에 저장 |
else | 오류 없을 때만 실행 |
finally | 항상 실행 (정리 작업) |
raise | 예외 직접 발생 |
logging | 오류를 파일에 체계적 기록 |
다음 편에서는 클래스와 객체 — 데이터와 기능을 하나로 묶는 객체지향 프로그래밍을 배웁니다.