본문으로 건너뛰기
개발 머꼬
개발 노트Python
hohyeon.dev23

TypeError와 ValueError 중 무엇이 떴는지로 원인을 반씩 좁히는 법

  • #Debugging
  • #Engineering Note
  • #Python

문제 발생

비슷해 보이는 두 오류가 다른 원인이었는데 처음에는 구분 없이 "입력이 이상하다"로만 읽고 있었습니다.

int("12a")        # ValueError: invalid literal for int() with base 10: '12a'
int(["12"])       # TypeError: int() argument must be a string, a bytes-like object or a real number, not 'list'
"3" + 3           # TypeError: can only concatenate str (not "int") to str

원인 분석

파이썬 문서가 두 예외의 경계를 명확히 정합니다.

  • TypeError — "부적절한 타입의 객체에 연산이나 함수가 적용될 때" 발생합니다.
  • ValueError — "타입은 맞지만 값이 부적절한 인자를 받았고, IndexError 같은 더 구체적인 예외로 설명되지 않을 때" 발생합니다.

문서의 요약이 그대로 기준입니다 — 잘못된 타입의 인자는 TypeError, 잘못된 값의 인자는 ValueError.

그래서 예외 이름만으로 수사 범위가 갈립니다.

예외의심할 곳
TypeError값이 어디서 왔는가. 타입이 다르다는 건 대개 잘못된 변수를 넘겼거나 변환을 빠뜨린 것
ValueError자체가 무엇인가. 형식·범위 검증이 빠진 자리

해결 방안

  1. 메시지의 뒷부분을 봅니다. TypeError는 기대한 타입과 실제 타입을 함께 알려 줍니다(... not 'list'). ValueError는 문제가 된 값을 그대로 보여 줍니다('12a').
  2. 경계에서 검증하고 알맞은 예외를 던집니다. 라이브러리를 만들 때 이 구분을 지키면 쓰는 쪽이 같은 방식으로 진단할 수 있습니다.
def set_age(value):
    if not isinstance(value, int):
        raise TypeError(f"age는 int여야 합니다: {type(value).__name__}")
    if value < 0:
        raise ValueError(f"age는 0 이상이어야 합니다: {value}")
  1. except를 뭉뚱그리지 않습니다. 두 예외를 한 번에 잡으면 위 정보가 사라집니다. 처리 방법이 다르면 따로 잡습니다.
try:
    quantity = int(raw)
except ValueError:
    return "숫자를 입력해 주세요"      # 사용자 입력 문제
except TypeError:
    raise                              # 우리 코드의 버그 — 감추지 않는다
  1. 지원할 생각이 있는 연산인데 아직 구현하지 않았다면 NotImplementedError를 씁니다. 문서가 권하는 구분입니다 — TypeError는 "지원하지 않는 연산", NotImplementedError는 "지원할 예정이지만 아직 없음"입니다.
  2. 원인 예외를 잃지 않습니다. 다시 던질 때 raise ... from e로 연결해 두면 로그에 원래 오류가 함께 남습니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.