최상위 Exception을 정의하라

매일 공부(ML)·2022년 10월 20일
0

파이썬 코딩의 기술

목록 보기
24/27

호출자를 API로부터 보호하기 위해 최상위 Exception을 정의하라

모듈 API에선 모듈 내에 여러분이 정의한 함수 혹은 클래스만큼이나 여러분이 발생시킬 예외도 API의 일부분으로서 중요하고 파이썬 언어와 표준 라이브러리에는 이미 예외 계층 구조가 내장이 되어있기에직접 정의한 새로운 예외 타입을 사용해서 오류를 보고하는 것이나 내장 예외 타입을 사용해 오류를 보고하는 것은 비슷하다.

#my_module.py

def determine_weight(volume, density):
	if density <= 0:
    	raise ValueError('밀도는 0보다 커야 합니다.')
        

API의 경우 새로운 예외 계층 구조를 정의하는 편이 훨씬 강력하고, 모듈에 최상위 Exception을 정의하고 모듈이 발생시키는 다른 모든 예가 최상위 예외를 상속하게 만듦으로써 API에서 발생하는 예외의 계층 구조를 만들 수 있다.

#my_module.py

class Error(Exception):
	"""이 모듈에서 발생할 모든 예외의 상위 클래스."""
    
class InvalidVolumeError(Error):
	"""부피 값이 잘못된 경우."""
    
def determine_weight(volume, density):
	if density < 0:
    	raise InvalidDensityError('밀도는 0보다 커야 합니다.')
    if volume < 0:
    	raise InvalidVolumeError('부피는 0보다 커야 합니다.')
    if volume == 0:
    	density / volume

어떤 모듈 안에 최상위 예외가 있으면 API 사용자들이 이 모듈에서 발생한 모든 오류를 더 쉽게 잡아낼 수 있다.

"""
우리가 정의한 API를 사용하는 사용자가 함수를 호출하면서 try/except문을 사용함으로써 최상위 예외를 잡아낼 수 있다.
"""

try:
	weight = my_module.determine_weight(1,-1)
except my_module.Error:
	logging.exception('예상치 못한 오류')


"""
ERROR:root:예상치 못한 오류
Traceback(most recent call last):
	File 'main.py', line 5 , in <module>
    	weight = my_module.determine_weight(1,-1)
    File "...my_module.py", line14 in determine_weight
    	raise InvalidDensityError('밀도는 0보다 커야 합니다')
my_module.InvalidDensityError: 밀도는 0보다 커야 합니다.
"""

logging.exception 함수가 잡아낸 예외의 전체 스택 트레이스를 출력하므로 더 쉽게 이 상황을 디버깅할 수 있다.
try/except 문을 사용하면 우리 모듈에서 발생한 예외가 모듈을 호출하는 코드로부터 멀리 전달이 되기에 프로그램이 깨지는 상황을 방지할 수 있다.


세 가지 효과

  1. 최상위 예외가 있으면 API를 호출하는 사용자가 API를 호출하는 사용자가 API를 잘못 사용한 경우를 더 쉽게 이해할 수 있게 된다.
try:
	weight = my_module.determine_weight(-1,1)
except my_module.InvalidDensityError:
	weight = 0
except my_module.Error:
	loggoing.exception('호출 코드에 버그가 있음')
    
"""
ERROR:root:호출 코드에 버그가 있음
Traceback(most recent call last):
	File 'main.py', line 5 , in <module>
    	weight = my_module.determine_weight(1,-1)
    File "...my_module.py", line16 in determine_weight
    	raise InvalidDensityError('밀도는 0보다 커야 합니다')
my_module.InvalidDensityError: 밀도는 0보다 커야 합니다.
"""

  1. API 모듈 코드의 버그를 발견할 때 도움이 된다는 점이다.

    두 가지 except문을 사용하면 API 소비자가 API모듈에 수정해야할 버그가 있을 경우 쉽게 감지할 수 있다.

try:
	weight = my_module.determine_weight(0,1)

except my_module.InvalidDensityError:
	weight = 0
except my_module.Error:
	logging.exception('호출 코드에 버그가 있음')
except Exception:
	logging.exception('API 코드에 버그가 있음!")
    raise #예외를 호출자 쪽으로 다시 발생시킴
    
"""
ERROR:root:API 코드에 버그가 있음
Traceback(most recent call last):
	File 'example.py', line 3 , in <module>
    	weight = my_module.determine_weight(1,-1)
    File "...my_module.py", line14 in determine_weight
    	density / volume
        
zeroDivisionError: division by zero
Traceback...
zeroDivisionError: division by zero
"""

  1. 미래의 API를 보호해준다는 점이다.

시간이 지남에 따라 API를 확장해서 특정 상황에서 더 구체적인 예외를 제공하고 싶을 때가 있다.

#my_module.py

...
class NegativeDensityError(InvalidDensityError):
	"""밀도가 음수인 경우."""
...

def determine_weight(volume, density):
	if density < 0:
    	raise NegativeDensityError('밀도는 0보다 커야 합니다.')
   ...

"""
나중에 호출하는 코드에서 새로운 타입의 예외를 더 처리하기로 결정하면, 그에 따라 처리 동작을 적절히 수정할 수 있다.
"""
	
try:
	weight = my_module.determine_weight(1,-1)
except my_module2.NegativeDensityError as exc:
	raise ValueError('밀도로 음수가 아닌 값을 제공해야 합니다') from exc
except my_module.InvalidDensityError:
	weight = 0
except my_module.Error:
	logging.exception('호출 코드에 버그가 있음')
except Exception:
	logging.exception('API 코드에 버그가 있음!')
    False
    
"""
Traceback ...
NegativeDensityError: 밀도는 0보다 커야 합니다.

The above exception was the direct cause of the following->exception

Traceback...
ValueError: 밀도로 음수가 아닌 값을 제공해야 합니다.
"""

최상위 예외 바로 아래 폭 넓은 예외 상황을 표현ㄴ하는 다양한 오류를 제공하면 코드 변경에 대한 보호를 더 강화할 수 있다.

#my_module.py
class Error(Exception):
"""이 모듈에서 발생할 모든 예외의 상위 클래스."""

class WeightError(Error):
"""무게 계산 관련  예외의 상위 클래스."""

class VolumeError(Error):
"""부피 계산 관련 예외의 상위 클래스."""

class DensityError(Error)
"""밀더 계산 관련 예외의 상위 클래스."""

구체적인 예외는 이런 일반적인 예외를 상속하고, API 코드로부터 API를 호출하는 코드를 보호하는 계층을 쉽게 추가할 수 있어서 편하다.


Summary

  • 모듈에서 사용할 최상위 예외를 정의하면 API 사용자들이 자신을 API로부터 보호할 수 있다.

  • 최상위 예외를 잡아내면 API를 소비하는 코드의 버그를 쉽게 찾을 수 있다.

  • 파이썬 Exception 기반 클래스를 잡아내면 API 구현의 버그를 쉽게 찾을 수 있다.

  • 중간 단계의 최상위 예외를 사용하면, 미래에 새로운 타입의 예외를 API에 추가할 때 API를 사용하는 코드가 깨지는 일을 방지할 수 있다.

profile
성장을 도울 아카이빙 블로그

0개의 댓글