10. 예외 처리 입문
- 에러(예외)가 무엇이고 왜 프로그램을 멈추는지 이해한다.
try/except로 에러를 잡아 우아하게 대처한다.- 자주 만나는 예외 종류를 알고 구분해 처리한다.
else·finally와 함께 안전한 입력 처리 패턴을 익힌다.
에러는 적이 아니다
지금까지 여러 장에서 빨간 에러 메시지를 봤습니다. ValueError, TypeError, IndexError, KeyError, IndentationError… 이런 에러를 Python에서는 예외(exception)라고 부릅니다.
예외가 발생하면 프로그램은 그 자리에서 즉시 멈춥니다. 예를 들어 사용자가 나이에 "abc"를 입력하면:
age = int(input("나이: ")) # 사용자가 "abc" 입력 print("다음 단계로...") # 이 줄은 실행되지 못함!
ValueError: invalid literal for int() with base 10: 'abc'
프로그램이 멈추고 뒤 코드는 실행되지 않습니다. 사용자 입장에선 프로그램이 "터진" 것처럼 보이죠. 예외 처리는 이런 상황에서 프로그램이 멈추지 않고 침착하게 대응하도록 만드는 기술입니다.
에러종류: 설명 형태로 무엇이 왜 잘못됐는지 알려줍니다. 에러를 두려워하지 말고 읽는 습관을 들이세요.트레이스백 읽는 법
에러가 나면 트레이스백(traceback)이라는 여러 줄 메시지가 나옵니다. 겁먹지 말고 맨 아래부터 읽으면 됩니다.
Traceback (most recent call last):
File "main.py", line 3, in <module>
age = int("abc")
ValueError: invalid literal for int() with base 10: 'abc'
- 맨 아랫줄:
ValueError: ...→ 에러의 종류와 이유. 가장 중요! - 그 위: 몇 번째 줄(
line 3)에서, 어떤 코드(int("abc"))에서 났는지
try / except: 에러 잡기
에러가 날 수 있는 코드를 try 블록에 넣고, 에러가 났을 때 할 일을 except 블록에 적습니다.
try: age = int(input("나이: ")) # 여기서 에러가 날 수 있음 print(f"내년이면 {age + 1}살") except ValueError: print("숫자를 입력해 주세요") # 에러가 나면 여기로
이제 사용자가 "abc"를 입력해도 프로그램은 멈추지 않고 "숫자를 입력해 주세요"를 출력한 뒤 정상적으로 계속됩니다.
동작 흐름을 그림으로 봅시다.
flowchart TD
Try["try 블록 실행"]:::proc --> Q{"에러 발생?"}:::proc
Q -->|아니오| Normal["정상 진행 ✅"]:::result
Q -->|예| Except["except 블록 실행<br/>(대처)"]:::data
Except --> Continue["프로그램 계속"]:::result
Normal --> Continue
classDef proc fill:#7fd8d8,stroke:#2a9d8f,color:#14532d
classDef result fill:#b8e6c1,stroke:#34a853,color:#14532d
classDef data fill:#fff3b0,stroke:#e0a800,color:#5c4500
except:만 쓰거나 except Exception:으로 모든 에러를 뭉뚱그려 잡으면, 정작 예상 못한 버그까지 삼켜버려 디버깅이 어려워집니다. 잡을 에러 종류를 구체적으로 적는 게 좋습니다(except ValueError:).자주 만나는 예외들
이미 앞 장들에서 만난 예외들입니다. 정리해 둡시다.
| 예외 | 언제 발생하나 | 예 |
|---|---|---|
ValueError | 자료형은 맞지만 값이 부적절 | int("abc") |
TypeError | 자료형이 안 맞음 | "나이" + 25 |
ZeroDivisionError | 0으로 나눔 | 10 / 0 |
IndexError | 리스트의 없는 인덱스 | [1,2][5] |
KeyError | 딕셔너리의 없는 키 | {"a":1}["z"] |
NameError | 정의 안 된 변수 사용 | print(undefined_var) |
여러 except로 종류별 대처
에러 종류마다 다르게 대응하려면 except를 여러 개 둡니다.
def safe_divide(a, b): try: return a / b except ZeroDivisionError: return "0으로 나눌 수 없습니다" except TypeError: return "숫자만 입력하세요" print(safe_divide(10, 2)) # 5.0 print(safe_divide(10, 0)) # 0으로 나눌 수 없습니다 print(safe_divide(10, "x")) # 숫자만 입력하세요
발생한 에러와 일치하는 첫 except가 실행됩니다. 7장 if/elif가 위에서부터 검사되던 것과 비슷합니다.
에러 메시지 받아오기: as
except ... as 변수로 에러 객체를 받아 그 내용을 쓸 수 있습니다.
try: n = int("hello") except ValueError as e: print(f"문제가 생겼어요: {e}") # 문제가 생겼어요: invalid literal for int() with base 10: 'hello'
else와 finally
try/except에는 선택적으로 두 블록을 더 붙일 수 있습니다.
| 블록 | 언제 실행되나 |
|---|---|
else | try가 에러 없이 끝났을 때만 |
finally | 에러가 나든 안 나든 항상 (마무리·정리용) |
try: age = int("42") except ValueError: print("입력 오류") else: print(f"성공! 나이는 {age}") # 에러 없을 때만 finally: print("처리 완료") # 무조건 실행 # 출력: # 성공! 나이는 42 # 처리 완료
finally는 파일을 닫거나 자원을 정리하는 등 "무슨 일이 있어도 꼭 해야 하는 마무리"에 씁니다. (파일 처리와 with 구문은 중급편에서 다룹니다.)
⭐ 실전 패턴: 올바른 입력 받을 때까지
예외 처리는 8장의 while과 결합할 때 가장 빛납니다. "올바른 숫자를 입력할 때까지 다시 묻기"는 가장 흔한 실전 패턴입니다.
while True: try: age = int(input("나이를 입력하세요: ")) break # 성공하면 반복 탈출 except ValueError: print("숫자만 입력해 주세요. 다시 시도하세요.") print(f"입력된 나이: {age}")
실행하면 이렇게 동작합니다.
나이를 입력하세요: abc 숫자만 입력해 주세요. 다시 시도하세요. 나이를 입력하세요: 스물다섯 숫자만 입력해 주세요. 다시 시도하세요. 나이를 입력하세요: 25 입력된 나이: 25
정상 입력이 들어오면 int()가 성공하고 break로 반복을 빠져나옵니다. 실패하면 except가 안내 메시지를 출력하고 while True가 다시 묻습니다.
while True + try/except + break의 조합이 정석이다.나쁜 예 ❌ vs 좋은 예 ✅
# ❌ 나쁜 예: 모든 에러를 뭉뚱그려 삼킴 (진짜 버그도 숨겨짐) try: result = risky_operation() except: pass # 무슨 에러였는지 알 수 없고, 조용히 무시됨 # ✅ 좋은 예: 예상되는 에러만 구체적으로 잡고, 의미 있게 대처 try: result = risky_operation() except ValueError as e: print(f"값이 잘못되었습니다: {e}") except FileNotFoundError: print("파일을 찾을 수 없습니다")
except: pass는 대부분 나쁜 신호다.이 장에서 배운 것
- 에러(예외)가 발생하면 프로그램은 즉시 멈춘다. 트레이스백은 맨 아래(에러 종류)부터 읽는다.
try/except로 에러를 잡아 프로그램이 멈추지 않고 대처하게 한다. 잡을 에러는 구체적으로 지정한다.- 자주 만나는 예외:
ValueError·TypeError·ZeroDivisionError·IndexError·KeyError·NameError. except를 여러 개 두어 종류별로 대처하고,as e로 에러 내용을 받는다.else(성공 시)·finally(항상)로 흐름을 보강한다. 입력 검증은while True+try/except+break가 정석이다.
🧪 실습 문제
문제 1. 다음 코드를 try/except로 감싸, 0으로 나눌 때 프로그램이 멈추는 대신 "0으로 나눌 수 없습니다"를 출력하도록 고치세요.
a = 10 b = 0 print(a / b)
문제 2. 다음 트레이스백에서 (a) 에러의 종류와 (b) 발생한 줄 번호는?
Traceback (most recent call last):
File "app.py", line 7, in <module>
print(scores[10])
IndexError: list index out of range
문제 3. 문자열을 받아 정수로 변환해 돌려주되, 변환할 수 없으면 None을 돌려주는 함수 to_int(text)를 작성하세요. to_int("42")와 to_int("abc")의 결과를 출력하세요.
문제 4. 다음 코드의 출력 순서는?
try: print("A") x = int("100") print("B") except ValueError: print("C") else: print("D") finally: print("E")
문제 5. while True와 try/except를 사용해, 1 이상 10 이하의 정수를 입력받을 때까지 반복하는 코드를 작성하세요. (힌트: 숫자가 아니면 ValueError 처리, 범위 밖이면 안내 후 continue)
<details>
<summary>✅ 정답·해설 보기</summary>
1.
a = 10 b = 0 try: print(a / b) except ZeroDivisionError: print("0으로 나눌 수 없습니다")
2. (a) IndexError (리스트의 범위 밖 인덱스), (b) line 7. 트레이스백은 맨 아랫줄에서 종류를, 그 위에서 위치를 알려줍니다.
3.
def to_int(text): try: return int(text) except ValueError: return None print(to_int("42")) # 42 print(to_int("abc")) # None
4. A → B → D → E. try가 에러 없이 끝나서 B까지 출력되고, 에러가 없으므로 except(C)는 건너뛰고 else(D)가 실행되며, finally(E)는 항상 실행됩니다.
5.
while True: try: n = int(input("1~10 사이 정수: ")) except ValueError: print("숫자를 입력하세요") continue if 1 <= n <= 10: break print("범위를 벗어났습니다") print(f"입력값: {n}")
</details>
◀️ 이전 장: 09. 함수 | ▶️ 다음: 99. 부록 — 치트시트와 다음 단계