에러는 파이썬이 상황을 알려주는 방식이다
파이썬의 모든 에러는 하나의 객체다. 타입과 메시지, 그리고 traceback(에러가 발생하기까지 호출된 함수들의 연쇄)을 가지고 있다. 이걸 잘 읽어내는 것이야말로 디버깅 실력에서 가장 크게 차이를 만드는 포인트다. 이 문서에서는 실제로 자주 마주치게 될 파이썬 에러 종류와, 이를 빠르게 해결하는 습관들을 함께 살펴본다.
try/except의 동작 원리나 직접 예외를 발생시키는 방법이 궁금하다면 예외 처리 문서에서 문법을 다루고 있으니 참고하자. 이 페이지는 구체적인 에러들과 그것을 읽어내는 방법에 집중한다.
Traceback 읽는 법
일단 에러가 나는 코드를 한번 돌려보자:
def divide(a, b):
return a / b
def report(values):
for v in values:
print(divide(10, v))
report([5, 2, 0])
파이썬은 다음과 비슷하게 출력합니다:
Traceback (most recent call last):
File "script.py", line 8, in <module>
report([5, 2, 0])
File "script.py", line 6, in report
print(divide(10, v))
File "script.py", line 2, in divide
return a / b
ZeroDivisionError: division by zero
아래에서 위로 읽어 올라가면 됩니다:
ZeroDivisionError: division by zero— 예외 타입과 메시지. 무엇이 잘못됐는지 알려주는 부분이죠.divide함수 2번 줄의return a / b— 실제로 예외가 터진 줄입니다.report함수 6번 줄의print(divide(10, v))— 이 예외를 촉발한 호출이고요.- 모듈 최상단 8번 줄의
report([5, 2, 0])— 모든 게 시작된 지점입니다.
수정해야 할 곳은 보통 가장 아래쪽 프레임입니다. 라이브러리 내부 깊숙한 곳에서 에러가 올라오는 경우라면, 트레이스백을 거슬러 올라가면서 내가 작성한 코드에 해당하는 첫 프레임을 찾아보세요. 잘못된 값을 넘긴 지점이 바로 그곳이니까요.
자주 마주치는 에러들
NameError
"Name 'mesage' is not defined." 오타가 났거나, 변수에 값을 할당하기 전에 사용했을 때 발생합니다. 최신 버전의 파이썬이라면 친절하게도 비슷한 이름을 추천해 줍니다 ("Did you mean 'message'?").
해결법: 철자와 스코프를 확인하세요. 함수 안에서만 선언된 이름은 함수 밖에서 읽을 수 없다는 점도 기억해 두세요.
TypeError
"Can only concatenate str (not 'int') to str." 연산에 맞지 않는 타입을 썼을 때 뜨는 에러다. 단골 사례는 문자열에 숫자를 더하거나, 호출할 수 없는 걸 호출하거나, 함수에 인자 개수를 잘못 넘기는 경우.
해결법: str(30), int("30")처럼 타입을 명시적으로 변환하거나, 실제로 넘기고 있는 값이 뭔지 먼저 확인해 보자. 서로 다른 타입을 이어 붙일 때는 + 연결보다 f-string이 훨씬 읽기 편하다. 예: f"age: {30}".
ValueError
"Invalid literal for int() with base 10: 'hello'." 타입은 맞습니다. int()는 문자열을 받으니까요. 다만 값 이 변환 가능한 형태가 아닌 거죠. int(), float(), datetime 파싱, 그리고 인자 범위가 정해진 함수들에서 자주 마주치는 에러입니다.
해결법은 두 가지입니다. 변환 전에 값을 검증하거나, 에러를 except로 잡아서 처리하면 됩니다:
KeyError
"KeyError: 'charlie'." 해당 키가 딕셔너리에 없다는 뜻이에요. 상황에 맞게 골라 쓰는 파이썬다운 해결법 세 가지를 소개할게요:
키가 없을 때 자동으로 초기화되길 원한다면 collections.defaultdict를 한번 살펴보는 것도 좋습니다.
IndexError
"List index out of range." 시퀀스에 존재하지 않는 위치를 요청했다는 뜻입니다.
해결 방법: 먼저 길이를 확인하거나, 마지막 요소를 참조할 때는 -1을 쓰거나, 슬라이싱(numbers[5:6])을 활용하면 됩니다. 슬라이싱은 예외를 던지지 않고 빈 리스트 []를 돌려줍니다.
AttributeError
"'NoneType' object has no attribute 'upper'." 없는 메서드를 호출한 경우예요. 변수 타입이 예상과 다르다는 뜻인데, 실제 값이 들어 있을 거라 기대했던 곳에 None이 들어 있는 경우가 대부분입니다.
해결 방법은 None이 어디서 흘러 들어왔는지 추적하는 거예요. 에러가 나는 줄 바로 앞에 print(type(var))를 찍어 보거나 브레이크포인트를 걸어 보는 게 제일 빠릅니다. "가끔 실패하는" 함수들은 보통 실패할 때 None을 반환하니까, 반환값에 메서드를 호출하기 전에 먼저 확인하는 습관을 들이세요.
ModuleNotFoundError (그리고 ImportError)
import fastapi
No module named 'fastapi' 에러는 지금 실행 중인 Python 인터프리터에 해당 패키지가 설치되어 있지 않다는 뜻입니다. 주로 두 가지 경우가 있어요.
- 아예 설치를 안 한 경우. 올바른 환경에서
python -m pip install fastapi를 실행하세요. - 설치는 했는데 다른 Python에 설치된 경우. 특히 macOS에서
pip와python이 서로 다른 설치본을 가리킬 때 자주 생기는 문제입니다.
가장 확실한 해결책은 실행할 때 쓰는 인터프리터와 동일한 인터프리터로 설치하는 것입니다.
python -m pip install fastapi
가상 환경을 사용 중이라면(사용하는 게 맞습니다), pip install을 실행할 때와 python script.py를 실행할 때 모두 가상 환경이 활성화되어 있는지 꼭 확인하세요.
FileNotFoundError
with open("settings.yaml") as f:
config = f.read()
"[Errno 2] No such file or directory: 'settings.yaml'." 같은 메시지가 떴다면, 스크립트가 실행되는 위치를 기준으로 해당 경로가 존재하지 않는다는 뜻입니다.
해결법: 스크립트 맨 위에서 os.getcwd()를 출력해 파이썬이 실제로 어느 디렉터리를 바라보고 있는지 먼저 확인해 보세요. 그리고 절대 경로를 쓰거나, pathlib.Path(__file__).parent / "settings.yaml"처럼 스크립트 파일 자체의 위치를 기준으로 경로를 잡아 주는 게 안전합니다.
EOFError
name = input("Name: ")
"EOF when reading a line." input()이 stdin에서 값을 읽으려 했는데 아무것도 받지 못한 경우다. 빈 소스에서 파이프로 입력이 들어왔거나, 프롬프트에서 Ctrl-D를 눌렀을 때 발생한다.
해결 방법: 파이프 입력이 의도된 동작이라면 호출부를 다음과 같이 감싸주자:
try:
name = input("Name: ")
except EOFError:
name = "anonymous"
문서 내 브라우저 에디터는 입력을 시뮬레이션하기 때문에 이 문제를 직접 겪을 일은 없습니다.
IndentationError와 SyntaxError
IndentationError: expected an indented block after function definition on line 2
이 에러들은 좀 특별합니다. 프로그램이 실행되기도 전에 터지거든요. 파이썬이 파일을 아예 파싱조차 하지 못한 상황이죠.
IndentationError—def,if,for같은 블록의 본문이 비어 있거나 들여쓰기가 어긋났을 때 발생합니다. 요즘 에디터들은 대부분 들여쓰기를 시각적으로 보여주니, "공백 문자 표시(show whitespace)" 옵션을 켜두면 탭과 스페이스가 섞인 것도 금방 찾을 수 있어요.SyntaxError— 콜론(:)을 빠뜨렸거나, 괄호 짝이 안 맞거나, 키워드 철자를 틀렸을 때 납니다. 최신 파이썬 버전은 문제의 문자를 화살표(^)로 콕 집어서 알려줍니다.
해결법: 에러 메시지에 줄 번호가 찍혀 있으니 일단 가서 확인하세요. 그런데 그 줄만 봐서는 도저히 문제가 없어 보인다면, 그 위쪽 줄을 살펴보세요. 몇 줄 위에서 괄호를 안 닫은 게 한참 뒤에 가서야 문법 오류로 튀어나오는 경우가 의외로 많습니다.
RuntimeError
"실행 중에 뭔가 잘못됐는데, 딱히 더 구체적인 에러 종류는 아닌" 경우를 전부 아우르는 포괄적인 에러입니다. RecursionError(재귀 깊이 초과)가 대표적인 하위 사례죠. 라이브러리 코드도 내부 상태가 꼬였을 때 RuntimeError를 던지곤 합니다.
해결법: 메시지를 잘 읽어보세요. 보통 설명이 꽤 친절합니다. 재귀가 원인이라면 반복문으로 바꿔 쓰는 게 정석이고, 정말 드문 경우에만 sys.setrecursionlimit으로 한도를 올리는 방법도 있습니다.
print로 디버깅하기
본격적인 디버거를 꺼내기 전에, print() — 더 좋게는 f"{var=}" 형태 — 만으로도 웬만한 버그는 잡을 수 있습니다:
f"{var=}"를 쓰면 표현식 원문과 값이 같이 출력되기 때문에, 포맷 문자열 안에 변수명을 또 적을 필요가 없습니다. 스크립트를 실행하고 출력을 훑어보면서 값이 이상해지는 지점을 찾아보세요. "도대체 왜 이러지?" 싶은 버그 대부분은 적재적소에 심어둔 print 서너 개면 금방 드러납니다.
커밋하기 전에는 심어둔 print들을 정리해 주세요. 실제로 배포할 코드에는 print보다 logging.debug(...)가 훨씬 낫습니다. 코드를 건드리지 않고도 디버그 로그를 켰다 껐다 할 수 있거든요.
breakpoint()와 pdb로 디버깅하기
print만으로 부족할 때는 breakpoint()를 호출하면 그 지점에서 파이썬 내장 대화형 디버거로 바로 진입합니다:
def discount(price, percent):
breakpoint()
return price * (1 - percent / 100)
discount(100, 20)
실제 터미널에서 스크립트를 실행해 보세요. 그러면 (Pdb) 프롬프트가 뜹니다. 알아두면 좋은 명령어 몇 가지:
p 변수명— 변수 값을 출력합니다.n— 다음 줄로 넘어갑니다.s— 함수 호출 안으로 들어갑니다.c— 다음 중단점이나 끝까지 계속 실행합니다.q— 종료합니다.l— 현재 위치 근처의 소스 코드를 보여줍니다.
VS Code나 PyCharm 같은 IDE의 디버거는 같은 기능을 GUI로 감싼 것뿐입니다. 여백을 클릭해 중단점을 찍고, 사이드바에서 변수 값을 확인할 수 있죠. 손에 덜 걸리적거리는 쪽을 쓰면 됩니다.
에러를 줄여주는 코딩 습관
- 변수 이름을 설명적으로 짓기.
TypeError나AttributeError로 고생하는 일의 절반은, 변수에 뭐가 들었는지 까먹지 않을 이름을 붙이는 것만으로도 사라집니다. - 경계에서 입력값 검증하기. 사용자 입력이나 파일 내용은 함수 맨 위에서 한 번 파싱하고 검사하세요. 그 이후의 코드는 값을 믿고 쓸 수 있습니다.
- 조용히 넘기지 말고 시끄럽게 실패시키기.
except Exception: pass로 뭉뚱그리면 정작 봐야 할 에러를 놓칩니다. 구체적인 예외만 잡아서 의도적으로 처리하고, 나머지는 그대로 올라가게 두세요. - 트레이스백은 맨 아래부터 읽기. 트레이스백 읽는 법을 익히는 데 쓴 1분은 나중에 100배로 돌아옵니다.
대부분의 에러는 미스터리가 아닙니다. 오타 한 줄이거나, 타입을 잘못 넣은 건데 메시지가 이미 정확히 짚어주고 있는 경우가 대부분이죠. 에러 메시지를 믿으세요. 거의 항상 맞습니다.
여기까지 오셨네요
레퍼런스 섹션은 여기서 끝입니다. "파이썬이 뭔가요?"에서 출발해 변수, 제어 흐름, 컬렉션, 함수, 클래스, 반복, 실전 데이터 처리, 에러까지 달려왔습니다. 이제부터는 프로젝트 중심으로 움직일 차례입니다. 만들고 싶은 걸 하나 정하고, 거기서부터 거꾸로 짚어가며 더 깊이 파야 할 부분을 공부하세요. 이 페이지들은 구체적인 질문을 들고 다시 찾아올 때도 그대로 기다리고 있을 겁니다.
자주 묻는 질문
파이썬 KeyError는 왜 발생하나요?
KeyError는 딕셔너리에 없는 키를 조회할 때 발생합니다. 예를 들어 users["missing"]을 하면 KeyError: 'missing'이 나죠. 안전하게 처리하려면 users.get("missing", default)로 기본값을 주거나, users.get("missing")(없으면 None 반환)을 쓰거나, if key in users:로 먼저 확인하는 방법이 있습니다.
EOFError는 언제 발생하나요?
EOFError는 input()이 읽을 입력이 더 이상 없을 때 발생하는 "end-of-file" 에러입니다. 보통 input()을 쓰는 스크립트에 데이터 없이 파이프를 연결했거나, 대화형 프롬프트에서 Ctrl-D를 눌렀을 때 자주 보게 됩니다. 파이프 입력을 받아야 한다면 try/except EOFError로 감싸주세요.
ModuleNotFoundError는 어떻게 해결하나요?
ModuleNotFoundError는 import X를 했는데 X 모듈을 찾지 못했다는 뜻입니다. 패키지가 아예 설치되어 있지 않거나(pip install X 필요), 지금 실행 중인 파이썬과 다른 파이썬에 설치되어 있는 경우가 많습니다(여러 버전을 깔아뒀을 때 흔한 실수예요). 후자의 경우 python -m pip install X로 같은 인터프리터에 설치하면 해결됩니다.
파이썬 트레이스백은 어떻게 읽어야 하나요?
트레이스백은 아래에서 위로 읽습니다. 맨 아래 줄이 예외 종류와 메시지 — 실제로 뭐가 잘못됐는지를 알려주는 핵심이죠. 그 위쪽 줄들은 거기까지 도달한 호출 스택이고, 내 코드는 보통 아래쪽에 있습니다. 내 파일 중 가장 아래쪽 프레임의 파일명과 줄 번호로 이동하세요. 거기가 바로 고쳐야 할 지점입니다.