티스토리 뷰

파이썬에서는 `raise` statement를 이용하여 예외를 던질 수 있다. 예를 들면 `raise KeyError` 와 같은 식이다. 이렇게 raise한 exception은 인스턴스가 만들어지면서 던져지게 된다. flask에서는 `errorhandler` 데코레이터를 이용하여 에러 핸들러를 등록할 수 있도록 하고, route 함수에서 예외가 raise 되었을 때 등록된 에러 핸들러들 중에 적합한 핸들러가 있는지 찾아 해당 핸들러 루틴을 실행하도록 되어 있다. (Flask 클래스의 handle_http_exception 메소드 참고)


flask 문서에서 소개되는 방법은 숫자로만 등록하는 예제를 보여준다. 흔한 에러 코드 400, 401, 404와 같은 것들이 각각 한 가지의 메세지만 전달하는 경우에는 이 방법이 전혀 문제가 되지 않는다. 하지만 같은 에러 코드임에도 불구하고 상황에 따라 다른 메세지를 보여주고 싶은 경우가 있다. 예를 들면 401 에러 낼 때, 세션 유지 시간이 초과된 경우와 가진 권한이 부족한 경우를 분리하여 다른 메세지를 전달해주고 싶을 때가 있을 것이다. 이 경우 에러 코드만으로는 표현이 안 된다.


다행히도 flask에서 에러 핸들러를 등록할 때 사용자가 정의한 클래스도 등록 가능하다. raise를 활용하기 위해서는 Exception 클래스를 상속받은 클래스를 등록하도록 하면 좋을 것이다. 다음 코드를 참고하자.



이렇게 여러가지 예외들을 정의해둔 뒤, route 함수에서 해당 예외를 raise 해주면 response의 body에 에러 핸들러에서 정의한 response의 내용이 잘 출력된다. (예: `raise ExpiredError`) 


사실 이 글을 쓰기 전에 삽질했던 내용은 flask-restful에서의 문제였다. 위에서 보인 에러 핸들러는 flask의 route 함수에서의 raise한 예외는 잘 전달받는다. 하지만 flask-restful의 Api 클래스를 이용하여 만든 API 함수들에 대해서는 debug 모드일 경우에만 에러 핸들러가 작동하는 문제가 있었다. flask-restful의 Api 클래스는 생성시 `errors` dictionary를 인자로 받을 수 있게 되어 있다. flask의 설정으로 debug 모드가 켜져있지 않으면 해당 errors에 존재하는 내용들을 제한적으로 response로 전달할 수 있는 것으로 보였다. 이것을 이용하여 기존 방법을 그대로 활용할 방법을 찾느라 시간을 많이 낭비했다. 



이는 내 착각에 의해 발생한 문제였다. 기본적으로 flask-restful에서는 예외를 만났을 때 `handle_error` 함수가 실행되고, 해당 함수에서는 잡힌 예외가 `HTTPException`의 자식 클래스들이 아닐 경우 등록된 Flask 객체의 `propagate_exceptions` 속성을 확인하여 flask의 에러 핸들러로 넘어가거나, 아니면 flask-restful에 등록된 errors를 참조하여 예외를 출력하도록 되어 있다. flask에서 `propagate_exceptions` 속성값이 결정되는 부분을 보면 `PROPAGATE_EXCEPTIONS` 설정값을 켜놓았거나 아니면 debug 모드일 경우 참을 반환하게 되어 있다. 그 동안 개발을 위해 항상 debug 모드를 켜놨었기 때문에 놓쳤던 부분이었는데, 배포를 위해 uwsgi를 통해 애플리케이션을 실행함으로써 만나게 된 문제였다. 설명이 장황했지만 `PROPAGATE_EXCEPTIONS` 옵션을 켜놓으면 문제가 해결된다.  `HTTPException`은 flask에서 사용하는 werkzeug에서 정의된 클래스인데, 처음부터 커스텀 예외 클래스를 정의할 때 `HTTPException` 클래스를 상속받아 정의하였으면 만나지 않았을 문제임을 생각하면 너무 슬프다...


댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday