학부생 시절 JAVA로 배울 때만 해도 이런 생각이 들었었다.
예외를 왜 던지고 잡고 하는거지...?
아니 그냥 에러 발생하면 바로 처리하면 되는거 아녀??
try-catch랑 raise는 왜 따로 만든거지?
그리고 throw랑 throws는 뭐가 다른데...
아직도 JAVA와는 서먹한 사이지만 파이썬과 좀 가까워진 뒤로 그 이유를 알게 되었다.
혹여나 JAVA 코드를 참고하시려는 분들께는 죄송하게 됐습니다..
아래 예시 코드를 보자.
# First example.
book_info = {
"title": "Le Petit Prince",
"author": "Saint-Exupéry",
"illustrator": "Saint-Exupéry",
"language": "French",
}
answer = {}
answer["title"] = book_info["title"]
answer["country"] = book_info["country"]
answer["genre"] = book_info["genre"]
print(answer)
위 코드는 당연히도 버그가 터집니다.
아래와 같은 출력이 나오죠.
Traceback (most recent call last):
File "test.py", line 16, in <module>
answer["country"] = book_info["country"]
KeyError: 'country'
이걸 예외 처리 없이 해결하려면?
if 문을 써야겠죠!!
# First example.
book_info = {
"title": "Le Petit Prince",
"author": "Saint-Exupéry",
"illustrator": "Saint-Exupéry",
"language": "French",
}
answer = {}
if "title" in book_info:
answer["title"] = book_info["title"]
if "country" in book_info:
answer["country"] = book_info["country"]
if "genre" in book_info:
answer["genre"] = book_info["genre"]
print(answer)
> {'title': 'Le Petit Prince'}
저 수많은 if 문...
별로 안 거슬리신다구요?
그럼 어쩔 수 없죠 뭐. 계속 위와 같은 방식으로 쓰셔도 될 겁니다.
그렇지만 쓸 수 밖에 없는 경우가 있죠.
파이썬에서 http 요청을 보낼 때 가장 많이 쓰이는
requests
모듈을 예로 들어봅시다!
가장 많이 라는 단어는 개인적인 의견입니다!
# Second example.
import requests
response = requests.get(
url="http://some.com/api/user-data"
)
print(response.status_code)
자, 아무런 문제가 없어보이는 코드입니다.
정말로 그럴까요?
Traceback (most recent call last):
File "my_request.py", line 5, in <module>
requests = get(url="http://some.com/api/user-data")
...
...
requests.exceptions.Timeout: Oops, timeout exception!!
짜잔 !!
나는 아무런 문제 없이 코드를 작성했는데
뜬금없이 위와 같은 에러가 발생할 수 있습니다!
아잇, 뭐 짧게 짠 코드인데 실패하면 중단시키거나 내가 다시 실행시키면 되지
라는 마인드를 가질 수도 있지만,
코드가 몇 백줄이 넘어가고!
엄청나게 복잡한 시스템이고!
에러가 일일이 중단시키고 재실행하기 너무 힘들고!
뭐 이런 상황이 벌어질 수 있다는 거죠.
그리고 실제로도 벌어지고...
내가 짠 코드에서 직접 발생하는 에러가 아니라
시스템적인 문제라든지, 내가 사용하는 모듈에서 발생하는 문제는
최소한의 예외 처리가 없으면 대환장납니다!! 와우!!
위의 코드에 예외 처리를 적용해봅시다.
# First example.
book_info = {
"title": "Le Petit Prince",
"author": "Saint-Exupéry",
"illustrator": "Saint-Exupéry",
"language": "French",
}
answer = {}
try:
answer["title"] = book_info["title"]
answer["country"] = book_info["country"]
answer["genre"] = book_info["genre"]
except KeyError as error:
print(error)
print(answer)
이 코드는 {'title': 'Le Petit Prince'}
외에 하나의 출력이 더 나옵니다.
바로 'country'
인데요.
print(error)
라인에서 출력됩니다.
> 'country'
> {'title': 'Le Petit Prince'}
최종 출력은 위와 같습니다.
예외 처리되어 출력된 문구가 너무 밋밋하죠?
딱 봤을 때 왜 틀렸는지 알고 싶다!
그러면 아래처럼 꾸며줄 수 있습니다.
# First example.
book_info = {
"title": "Le Petit Prince",
"author": "Saint-Exupéry",
"illustrator": "Saint-Exupéry",
"language": "French",
}
answer = {}
try:
answer["title"] = book_info["title"]
answer["country"] = book_info["country"]
answer["genre"] = book_info["genre"]
except KeyError as error:
print(str(error) + " key is not in book_info!")
print(answer)
이렇게 하면 아래와 같이 나오죠.
> 'country' key is not in book_info!
> {'title': 'Le Petit Prince'}
아 이거도 너무 부족하다... 싶거나
저 에러가 나오게 된 경위를 모두 추적하고 싶다면?
즉, Stack trace
를 출력하고 싶다면?
파이썬에서는 traceback
이라는 라이브러리를 사용하면 됩니다.
# First example.
import traceback
book_info = {
"title": "Le Petit Prince",
"author": "Saint-Exupéry",
"illustrator": "Saint-Exupéry",
"language": "French",
}
answer = {}
try:
answer["title"] = book_info["title"]
answer["country"] = book_info["country"]
answer["genre"] = book_info["genre"]
except KeyError as error:
traceback.print_exc()
print(answer)
어떻게 출력되는지 볼까요?
Traceback (most recent call last):
File "test.py", line 16, in <module>
answer["country"] = book_info["country"]
KeyError: 'country'
{'title': 'Le Petit Prince'}
처음 작성했던 코드와 다를게 없지 않냐?
에러가 그대로 뜨지 않냐?
라고 할 수 있지만 엄연히 다릅니다!
처음 작성했던 코드에서의 결과는
Traceback (most recent call last):
File "test.py", line 16, in <module>
answer["country"] = book_info["country"]
KeyError: 'country'
입니다.
즉, 마지막 print(answer)
문이 실행되지 않고 프로세스가 끝난거죠.
하지만 위 코드는 print(answer)
문이 실행됩니다.
예외가 발생해도 우리가 처리를 해줬기 때문에 프로세스가 중단되지 않고 그대로 진행되는 겁니다!
뭐 간단하게 끝낼 수 있습니다!
# Second example.
import requests
try:
response = requests.get(
url="http://some.com/api/user-data"
)
except requests.exception.Timeout as error:
print(error)
print(response.status_code)
방금 우리가 마주쳤던 Timeout
에러는 처리될 겁니다.
단, print(response.status_code)
에서 200
이 아니라 500
같은 결과가 나오겠죠..
혹은 또다른 에러가 뜰 수도 있고요!
그런데 문득 궁금해집니다.
Timeout
이 아니라 다른 에러가 터지면 어쩌지?
- 에러가 발생하지 않았을 때에만 어떤 코드를 실행시키고 싶어!
- 에러가 발생하든 안하든 어떤 코드를 무조건 실행시키고 싶어!
- 예외 처리 코드를 더 깔끔하게 작성하고 싶어!
1번부터 차근히 보죠!
간단하게도, 내가 처리하고 싶은 예외를 추가해주면 됩니다.
# Second example.
import requests
try:
response = requests.get(
url="http://some.com/api/user-data"
)
except requests.exception.Timeout as error:
print(error)
except requests.exception.TooManyRedirects as error:
print(error)
except requests.exceptions.RequestException as error:
print(error)
print(response.status_code)
이렇게 여러 예외를 처리해야 할 경우에 주의해야 할 점!
상위
예외일 수록 처리하는 구문이 뒤에 가야 합니다!
위 코드로 설명드리자면,
requests.exception.Timeout
와
requests.exception.TooManyRedirects
는 모두
requests.exceptions.RequestException
를 상속받습니다.
그래서 RequestException
예외가 가장 뒤에 있죠.
예외 처리를 할 때 위와 같은 계층 구조를 확인하는 것을 추천드립니다!
그럼,
이 글을 쓴 진짜 이유를 얘기하면서
나머지 궁금증도 계속 해결하...
기에는 글이 너무 길어진 것 같군요!
다음 글에 계속~~ 하하하핳