Python에서 예외(Exception)는 인터프리터에 의해 자동으로 발생할 수도 있고, 개발자가 직접 raise 문을 사용해 의도적으로 발생시킬 수도 있습니다. Python이 기본으로 제공하는 내장 예외들을 활용하면 프로그램에서 흔히 발생하는 오류를 효과적으로 잡아낼 수 있습니다.
이 가이드에서는 예외가 무엇인지, 그리고 실제 코드에서 어떻게 활용되는지 살펴보겠습니다. 또한 예외를 직접 발생시키는 예제를 통해 여러분의 코드에서 예외를 다루는 방법을 단계별로 익혀보겠습니다.
예외란 무엇인가?
예외는 '논리 오류(logical error)'라고도 불리며, 프로그램이 실행되는 도중에 발생하는 오류를 의미합니다.
Python에는 두 가지 유형의 오류가 있습니다. 바로 구문 오류(syntax error)와 런타임 오류(runtime error)입니다.
구문 오류는 프로그램이 실행되기 전에 발견됩니다. 코드에 구문 오류가 있다면 프로그램은 아예 실행조차 되지 않습니다. Python이 구문 오류가 있는 코드를 해석할 수 없기 때문입니다.
반면 런타임 오류, 즉 예외는 Python이 프로그램을 실행하는 도중에 마주치게 됩니다. 예를 들어 프로그램의 30번째 줄에서 예외가 발생했다면, 앞의 29줄은 정상적으로 실행된 후 프로그램이 그 지점에서 멈추게 됩니다.
다음은 실제 예외가 발생했을 때 출력되는 메시지의 예입니다.
Traceback (most recent call last): File "main.py", line 6, in <module> s_names.append(n) AttributeError: 'str' object has no attribute 'append'
이 예외 메시지는 Python이 나머지 코드를 더 이상 실행할 수 없다는 것을 알려줍니다. 예외를 만났다면 메시지를 꼼꼼히 읽어보세요. 대부분의 경우 오류 메시지만으로 문제를 해결하는 데 필요한 정보를 얻을 수 있습니다.
오류 메시지의 마지막 문장을 하나씩 분석해 보겠습니다.
AttributeError: 'str' object has no attribute 'append'
먼저 이 오류가 AttributeError라는 것을 알 수 있습니다. 이는 존재하지 않는 속성(attribute)에 접근하려고 했다는 뜻입니다. 오류 메시지에 따르면 우리는 문자열(string) 객체에 append()를 사용하려 했는데, 이는 허용되지 않는 동작입니다.
문자열 객체는 append() 메서드를 지원하지 않으므로, 다음 단계로 문자열에서 append()에 해당하는 기능을 찾아보는 것이 좋습니다. 바로 문자열 연결(concatenation)입니다. 이렇게 하면 문제를 해결할 수 있는 실질적인 방법을 찾게 된 셈입니다.
어떤 상황에서 예외를 만나게 될까?
개발자가 코드 속 오류를 쉽게 잡아낼 수 있도록 Python은 다양한 내장 예외를 제공합니다. 이러한 예외들은 코드에서 문제의 원인을 파악하고 수정하는 데 큰 도움이 됩니다. 아래는 실무에서 자주 만나게 되는 대표적인 예외들입니다.
- TypeError: 해당 타입이 지원하지 않는 함수나 연산을 객체에 적용할 때 발생합니다. (예: TypeError: can only join an iterable)
- SyntaxError: 프로그램 실행 전에 발견되며, 코드에 구문상의 문제가 있음을 알려줍니다. (예: SyntaxError: invalid syntax)
- KeyError: 딕셔너리에 존재하지 않는 키를 참조할 때 발생합니다. (예: KeyError: "usb_ports")
- ImportError: 존재하지 않는 패키지를 import하거나, 패키지 안에 없는 함수를 가져오려 할 때 발생합니다.
물론 위 목록은 Python에서 만날 수 있는 수많은 예외 중 극히 일부에 불과합니다.
예외를 직접 발생시키는 방법
코드에서 직접 커스텀 예외를 정의할 수도 있습니다. Python이 제공하는 내장 예외만으로는 프로그램의 요구 사항을 충족하기 어려울 때 특히 유용합니다.
간단한 예로, 게임용 비밀번호 검증 프로그램을 작성해 보겠습니다. 먼저 사용자에게 비밀번호를 입력받습니다.
password = input("비밀번호를 입력하세요: ")
비밀번호가 유효하려면 길이가 12자보다 길어야 합니다. 사용자가 입력한 비밀번호가 12자 이하라면 예외를 발생시키고 싶습니다.
이를 위해 if 문을 사용해 보겠습니다. 다음 if 문은 사용자가 선택한 비밀번호가 12자 미만인지 확인합니다.
if len(password) > 12:
print("사용할 수 있는 비밀번호입니다.")
else:
raise Exception("비밀번호 길이가 올바르지 않습니다.")
len() 메서드로 비밀번호의 길이를 계산합니다. 사용자가 유효하지 않은 비밀번호를 입력하면, 프로그램은 "비밀번호 길이가 올바르지 않습니다."라는 메시지와 함께 예외를 발생시킵니다.
코드가 실제로 동작하는지 확인해 보겠습니다.
비밀번호를 입력하세요: Bacon120
Traceback (most recent call last):
File "main.py", line 6, in <module>
raise Exception("비밀번호 길이가 올바르지 않습니다.")
Exception: 비밀번호 길이가 올바르지 않습니다.
입력한 비밀번호가 12자 미만이었기 때문에 코드가 중단되었습니다. 이번에는 유효한 비밀번호로 프로그램을 실행해 보겠습니다.
비밀번호를 입력하세요: Bacon120Bacon120 사용할 수 있는 비밀번호입니다.
프로그램이 성공적으로 실행되었습니다.
예외를 처리하는 방법
기본적으로 예외가 발생하면 프로그램의 실행이 중단됩니다. 오류의 원인을 반드시 찾도록 강제한다는 점에서 유용하지만, 때로는 예외가 발생해도 프로그램이 멈추지 않기를 원하는 경우도 있습니다.
예를 들어 리스트에 담긴 데이터 값들이 유효한지 검사하는 상황이라면, 유효하지 않은 데이터를 발견할 때마다 프로그램이 멈추는 것은 바람직하지 않을 수 있습니다.
이럴 때 try…except 블록을 사용해 예외를 처리할 수 있습니다. 딕셔너리에서 항목을 찾는 프로그램을 작성해 보겠습니다. 이 딕셔너리에는 한 반 학생들의 이름과 최근 시험 성적이 저장되어 있습니다.
먼저 학생 이름과 성적 정보를 담은 딕셔너리를 정의합니다.
data = {
"Lucy": 73,
"Carlton": 59,
"Adam": 73
}
다음으로, 사용자에게 성적을 조회할 학생의 이름을 입력받겠습니다. 나중에 예외를 처리할 수 있도록 이 작업은 try 블록 안에서 수행합니다.
try:
student = input("성적을 조회할 학생의 이름을 입력하세요: ")
print(data[student])
이 코드는 사용자가 입력한 이름과 일치하는 학생의 성적을 출력합니다. 하지만 사용자가 잘못된 이름을 입력하면 KeyError가 발생하게 됩니다.
이 문제는 except 블록을 추가하여 처리할 수 있습니다.
try:
student = input("성적을 조회할 학생의 이름을 입력하세요: ")
print(data[student])
except:
print("해당 학생은 성적 목록에 없습니다.")
학생을 찾지 못하면 except 절이 실행됩니다. 프로그램을 실행해 보겠습니다.
성적을 조회할 학생의 이름을 입력하세요: Lucy 73
유효한 학생 이름을 입력하면 프로그램이 정상적으로 동작합니다. 이번에는 존재하지 않는 이름을 입력해 보겠습니다.
성적을 조회할 학생의 이름을 입력하세요: Kaitlin 해당 학생은 성적 목록에 없습니다.
예외는 발생했지만 프로그램은 멈추지 않았습니다. 대신 except 블록의 내용이 실행된 것입니다.
try…except 블록에 대해 더 자세히 알고 싶다면 Python try…except 관련 가이드를 참고해 보세요.
마무리
예외는 코드에 논리 오류가 있다는 사실을 알려주는 신호입니다. 예외가 발생하면 예외의 유형, 발생 위치, 그리고 오류 메시지를 확인할 수 있으며, 이 정보를 활용해 예외의 원인을 추적할 수 있습니다.
raise 문을 사용하면 직접 예외를 발생시킬 수 있고, try…except 블록을 사용하면 발생한 예외를 우아하게 처리할 수 있습니다.
이제 여러분은 프로처럼 Python 예외를 다룰 준비가 되었습니다!