10. 예외 처리 입문

🎯 이 장의 목표
  • 에러(예외)가 무엇이고 왜 프로그램을 멈추는지 이해한다.
  • try/except로 에러를 잡아 우아하게 대처한다.
  • 자주 만나는 예외 종류를 알고 구분해 처리한다.
  • else·finally와 함께 안전한 입력 처리 패턴을 익힌다.

에러는 적이 아니다

지금까지 여러 장에서 빨간 에러 메시지를 봤습니다. ValueError, TypeError, IndexError, KeyError, IndentationError… 이런 에러를 Python에서는 예외(exception)라고 부릅니다.

예외가 발생하면 프로그램은 그 자리에서 즉시 멈춥니다. 예를 들어 사용자가 나이에 "abc"를 입력하면:

PYTHON
age = int(input("나이: "))    # 사용자가 "abc" 입력
print("다음 단계로...")        # 이 줄은 실행되지 못함!
TEXT
ValueError: invalid literal for int() with base 10: 'abc'

프로그램이 멈추고 뒤 코드는 실행되지 않습니다. 사용자 입장에선 프로그램이 "터진" 것처럼 보이죠. 예외 처리는 이런 상황에서 프로그램이 멈추지 않고 침착하게 대응하도록 만드는 기술입니다.

💡 팁
개발 중에 보는 에러는 버그를 알려주는 친절한 신호입니다. 메시지를 끝까지 읽으면 에러종류: 설명 형태로 무엇이 왜 잘못됐는지 알려줍니다. 에러를 두려워하지 말고 읽는 습관을 들이세요.

트레이스백 읽는 법

에러가 나면 트레이스백(traceback)이라는 여러 줄 메시지가 나옵니다. 겁먹지 말고 맨 아래부터 읽으면 됩니다.

TEXT
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 블록에 적습니다.

PYTHON
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
ZeroDivisionError0으로 나눔10 / 0
IndexError리스트의 없는 인덱스[1,2][5]
KeyError딕셔너리의 없는 키{"a":1}["z"]
NameError정의 안 된 변수 사용print(undefined_var)

여러 except로 종류별 대처

에러 종류마다 다르게 대응하려면 except를 여러 개 둡니다.

PYTHON
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 변수로 에러 객체를 받아 그 내용을 쓸 수 있습니다.

PYTHON
try:
    n = int("hello")
except ValueError as e:
    print(f"문제가 생겼어요: {e}")
# 문제가 생겼어요: invalid literal for int() with base 10: 'hello'

else와 finally

try/except에는 선택적으로 두 블록을 더 붙일 수 있습니다.

블록언제 실행되나
elsetry에러 없이 끝났을 때만
finally에러가 나든 안 나든 항상 (마무리·정리용)
PYTHON
try:
    age = int("42")
except ValueError:
    print("입력 오류")
else:
    print(f"성공! 나이는 {age}")     # 에러 없을 때만
finally:
    print("처리 완료")               # 무조건 실행
# 출력:
# 성공! 나이는 42
# 처리 완료

finally는 파일을 닫거나 자원을 정리하는 등 "무슨 일이 있어도 꼭 해야 하는 마무리"에 씁니다. (파일 처리와 with 구문은 중급편에서 다룹니다.)

⭐ 실전 패턴: 올바른 입력 받을 때까지

예외 처리는 8장의 while과 결합할 때 가장 빛납니다. "올바른 숫자를 입력할 때까지 다시 묻기"는 가장 흔한 실전 패턴입니다.

PYTHON
while True:
    try:
        age = int(input("나이를 입력하세요: "))
        break                           # 성공하면 반복 탈출
    except ValueError:
        print("숫자만 입력해 주세요. 다시 시도하세요.")

print(f"입력된 나이: {age}")

실행하면 이렇게 동작합니다.

TEXT
나이를 입력하세요: abc
숫자만 입력해 주세요. 다시 시도하세요.
나이를 입력하세요: 스물다섯
숫자만 입력해 주세요. 다시 시도하세요.
나이를 입력하세요: 25
입력된 나이: 25

정상 입력이 들어오면 int()가 성공하고 break로 반복을 빠져나옵니다. 실패하면 except가 안내 메시지를 출력하고 while True가 다시 묻습니다.

📌 핵심
핵심: "사용자가 제대로 입력할 때까지 다시 묻기"는 while True + try/except + break의 조합이 정석이다.

나쁜 예 ❌ vs 좋은 예 ✅

PYTHON
# ❌ 나쁜 예: 모든 에러를 뭉뚱그려 삼킴 (진짜 버그도 숨겨짐)
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으로 나눌 수 없습니다"를 출력하도록 고치세요.

PYTHON
a = 10
b = 0
print(a / b)

문제 2. 다음 트레이스백에서 (a) 에러의 종류와 (b) 발생한 줄 번호는?

TEXT
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. 다음 코드의 출력 순서는?

PYTHON
try:
    print("A")
    x = int("100")
    print("B")
except ValueError:
    print("C")
else:
    print("D")
finally:
    print("E")

문제 5. while Truetry/except를 사용해, 1 이상 10 이하의 정수를 입력받을 때까지 반복하는 코드를 작성하세요. (힌트: 숫자가 아니면 ValueError 처리, 범위 밖이면 안내 후 continue)

<details>

<summary>✅ 정답·해설 보기</summary>

1.

PYTHON
a = 10
b = 0
try:
    print(a / b)
except ZeroDivisionError:
    print("0으로 나눌 수 없습니다")

2. (a) IndexError (리스트의 범위 밖 인덱스), (b) line 7. 트레이스백은 맨 아랫줄에서 종류를, 그 위에서 위치를 알려줍니다.

3.

PYTHON
def to_int(text):
    try:
        return int(text)
    except ValueError:
        return None
print(to_int("42"))    # 42
print(to_int("abc"))   # None

4. ABDE. try가 에러 없이 끝나서 B까지 출력되고, 에러가 없으므로 except(C)는 건너뛰고 else(D)가 실행되며, finally(E)는 항상 실행됩니다.

5.

PYTHON
while True:
    try:
        n = int(input("1~10 사이 정수: "))
    except ValueError:
        print("숫자를 입력하세요")
        continue
    if 1 <= n <= 10:
        break
    print("범위를 벗어났습니다")
print(f"입력값: {n}")

</details>

◀️ 이전 장: 09. 함수 | ▶️ 다음: 99. 부록 — 치트시트와 다음 단계