소개
컴퓨팅 및 프로그래밍에서 예외 처리(Exception Handling)란 특별한 처리가 필요한 비정상적이거나 예외적인 상황이 발생했을 때 이에 대응하는 과정을 의미합니다. Python에는 프로그램 실행 중 오류가 발생할 때 호출되는 많은 내장 예외가 있습니다. 또한, 많은 외부 라이브러리에는 사용자가 직접 처리해야 하는 고유한 사용자 정의 예외가 포함되어 있습니다.
이 실습에서는 Python 예외 처리의 기초를 배웁니다. 기본적인 try 및 except 블록으로 시작하여, 여러 예외를 처리하는 방법, finally 블록의 용도를 이해하고, 마지막으로 자신만의 사용자 정의 예외를 생성 및 관리하는 방법을 학습합니다.
기본 예외 처리
이 단계에서는 try 및 except 블록을 사용하여 Python에서 예외를 처리하는 기초를 배웁니다.
수학적으로 0으로 나누는 것은 불가능합니다. Python에서 이를 시도하면 인터프리터는 내장된 ZeroDivisionError를 발생시킵니다. 실제로 확인해 보겠습니다.
참고: WebIDE(VS Code와 유사) 사용이 익숙하지 않다면, 이 실습을 먼저 학습하시기 바랍니다.
먼저, 왼쪽 파일 탐색기에서 basic_handler.py 파일을 열고 다음 코드를 추가합니다. 이 코드는 간단한 나눗셈 함수를 정의합니다.
def divide(dividend , divisor):
print(dividend / divisor)
divide(dividend=10, divisor=5)
이제 터미널에서 스크립트를 실행합니다.
python3 basic_handler.py
올바른 결과가 출력되는 것을 볼 수 있습니다.
2.0
다음으로, 0으로 나누려고 할 때 어떤 일이 발생하는지 확인해 보겠습니다. basic_handler.py의 마지막 줄을 수정하여 divisor를 0으로 설정하고 divide를 호출합니다.
def divide(dividend , divisor):
print(dividend / divisor)
divide(dividend=10, divisor=0)
스크립트를 다시 실행합니다.
python3 basic_handler.py
이번에는 프로그램이 중단되고 ZeroDivisionError를 나타내는 트레이스백(traceback)이 출력됩니다.
Traceback (most recent call last):
File "/home/labex/project/basic_handler.py", line 4, in <module>
divide(dividend=10, divisor=0)
File "/home/labex/project/basic_handler.py", line 2, in divide
print(dividend / divisor)
ZeroDivisionError: division by zero
프로그램이 중단되는 것을 방지하고 사용자 친화적인 메시지를 표시하려면 try와 except를 사용하여 예외를 "처리"할 수 있습니다. 오류가 발생할 가능성이 있는 코드는 try 블록 안에 넣고, 오류 발생 시 실행할 코드는 except 블록에 넣습니다.
basic_handler.py 파일을 다음 코드로 업데이트하여 ZeroDivisionError를 우아하게 처리해 보세요.
## try-except: 예외를 우아하게 처리
def divide(dividend , divisor):
try: ## 이 코드를 실행 시도
print(dividend / divisor)
except ZeroDivisionError: ## 특정 예외 유형 포착
print('You can not divide by 0')
divide(dividend=10, divisor=0)
이제 스크립트를 다시 한 번 실행합니다.
python3 basic_handler.py
프로그램이 중단되는 대신, 정의한 유용한 메시지가 출력되고 실행이 정상적으로 종료됩니다.
You can not divide by 0
하나의 예외 블록으로 여러 예외 처리하기
이 단계에서는 단일 except 블록 내에서 여러 가지 예외 유형을 처리하는 방법을 알아봅니다. 이는 여러 유형의 오류가 발생할 수 있는 상황에서 오류 처리 코드를 간소화하는 데 유용합니다.
(ZeroDivisionError, TypeError)와 같은 예외 튜플을 포착하여 동일한 로직으로 처리할 수 있습니다.
multiple_handler.py 파일을 열고 다음 코드를 추가합니다. 이 함수는 TypeError(문자열과 정수를 연결하려고 시도할 때) 또는 ZeroDivisionError를 발생시킬 수 있도록 설계되었습니다.
## 하나의 except 블록에서 여러 예외 처리
def divide(dividend , divisor):
try:
if (dividend == 10):
var = 'str' + 1 ## TypeError 발생
else:
print(dividend / divisor)
except (ZeroDivisionError, TypeError) as error: ## 여러 예외 유형 포착
print(error) ## 오류 메시지 출력
divide(dividend=20, divisor=5)
먼저, 예외가 발생하지 않는 시나리오에서 코드를 실행해 보겠습니다. 스크립트를 실행하세요.
python3 multiple_handler.py
출력 결과는 나눗셈의 결과입니다.
4.0
이제 TypeError를 발생시켜 보겠습니다. multiple_handler.py의 마지막 줄을 수정하여 dividend를 10으로 설정합니다.
## ... (코드 생략) ...
divide(dividend=10, divisor=5)
스크립트를 다시 실행합니다.
python3 multiple_handler.py
except 블록이 TypeError를 포착하고 관련 오류 메시지를 출력합니다.
can only concatenate str (not "int") to str
마지막으로 ZeroDivisionError를 발생시켜 보겠습니다. 마지막 줄을 다시 수정하여 0으로 나누기를 시도합니다. TypeError 조건(dividend == 10)은 충족되지 않음에 유의하세요.
## ... (코드 생략) ...
divide(dividend=20, divisor=0)
스크립트를 실행합니다.
python3 multiple_handler.py
이제 동일한 except 블록이 ZeroDivisionError를 포착하고 해당 메시지를 출력합니다.
division by zero
검증을 통과하려면 스크립트의 마지막 호출이 TypeError를 발생시키도록 divide(dividend=10, divisor=5)로 설정되어 있는지 확인하세요.
예외 처리에서의 Finally 코드
이 단계에서는 finally 블록의 목적과 사용법을 이해합니다. finally 절 내부의 코드는 예외 발생 여부나 처리 여부와 관계없이 항상 실행됩니다. 따라서 파일 닫기나 네트워크 연결 종료와 같은 정리 작업에 이상적입니다.
작동 방식을 확인해 보겠습니다. finally_handler.py 파일을 열고 다음 코드를 추가합니다.
## finally 블록: 예외와 관계없이 항상 실행
def divide(dividend , divisor):
try:
print(dividend / divisor)
except ZeroDivisionError:
print('You can not divide by 0')
finally: ## 예외가 발생해도 항상 실행
print('Execution finished')
divide(dividend=10, divisor=5)
터미널에서 스크립트를 실행합니다.
python3 finally_handler.py
이 경우 예외가 발생하지 않습니다. try 블록이 성공적으로 완료된 후 finally 블록이 실행됩니다.
2.0
Execution finished
이제 오류를 발생시켜 보겠습니다. finally_handler.py의 마지막 줄을 수정하여 0으로 나누도록 합니다.
## ... (코드 생략) ...
divide(dividend=10, divisor=0)
스크립트를 다시 실행합니다.
python3 finally_handler.py
이번에는 ZeroDivisionError가 발생하여 except 블록에 의해 포착됩니다. 예외가 발생했음에도 불구하고, except 블록이 완료된 후 finally 블록이 여전히 실행됩니다.
You can not divide by 0
Execution finished
보시다시피, 두 시나리오 모두에서 "Execution finished" 메시지가 출력되며, 이는 finally 블록이 항상 실행되어야 하는 코드를 위한 신뢰할 수 있는 장소임을 보여줍니다.
사용자 정의 예외
이 단계에서는 자신만의 사용자 정의 예외를 만드는 방법을 배웁니다. 이는 프로그램 도메인에 특화된 오류 유형을 생성할 수 있게 하여 애플리케이션의 가독성과 유지보수성을 높여주는 강력한 기능입니다.
사용자 정의 예외는 Python의 기본 Exception 클래스를 상속받는 새 클래스를 생성하여 정의합니다. 그런 다음 raise 키워드를 사용하여 이 예외를 발생시킬 수 있습니다.
custom_exception.py 파일을 열고 다음 코드를 추가하여 간단한 사용자 정의 예외를 정의하고 발생시켜 봅니다.
## 사용자 정의 예외: Exception 클래스를 상속받아 생성
class MyCustomException(Exception):
pass
raise MyCustomException ## 사용자 정의 예외 발생
이 스크립트를 실행하면 즉시 중단되고 새 예외에 대한 트레이스백이 표시됩니다.
python3 custom_exception.py
Traceback (most recent call last):
File "/home/labex/project/custom_exception.py", line 5, in <module>
raise MyCustomException ## 사용자 정의 예외 발생
__main__.MyCustomException
사용자 정의 예외를 발생시킬 때 설명 메시지를 제공할 수도 있습니다. custom_exception.py를 수정하여 예외에 문자열을 전달해 보세요.
class MyCustomException(Exception):
pass
raise MyCustomException('A custom message for my custom exception')
실행하면 트레이스백에 메시지가 표시됩니다.
Traceback (most recent call last):
File "/home/labex/project/custom_exception.py", line 4, in <module>
raise MyCustomException('A custom message for my custom exception')
__main__.MyCustomException: A custom message for my custom exception
사용자 정의 예외를 처리하는 방법은 내장 예외를 처리하는 방법과 동일하게 try...except 블록을 사용합니다. custom_exception.py를 최종 형태로 업데이트하여 사용자 정의 예외를 발생시키고 처리해 보세요.
class MyCustomException(Exception):
pass
try:
raise MyCustomException('A custom message for my custom exception')
except MyCustomException:
print('My custom exception was raised')
이제 스크립트를 마지막으로 실행합니다.
python3 custom_exception.py
프로그램이 더 이상 중단되지 않습니다. 대신 except 블록의 코드가 실행되고 확인 메시지가 출력됩니다.
My custom exception was raised
요약
이 실습에서는 Python에서 예외를 처리하는 필수 기술을 배웠습니다. 기본적인 try 및 except 블록으로 특정 오류를 포착하여 프로그램 중단을 방지하는 방법부터 시작했습니다. 그런 다음 단일 except 블록에서 여러 예외 유형을 효율적으로 처리하는 방법을 배웠습니다. 또한 항상 실행되어야 하는 정리 코드를 위해 필수적인 finally 블록에 대해서도 살펴보았습니다. 마지막으로 자신만의 사용자 정의 예외를 생성, 발생, 처리하는 능력을 갖추게 되어, 애플리케이션에서 더 견고하고 설명적인 오류 처리 로직을 구축할 수 있게 되었습니다.



