프로젝트 경험기/기타 경험기

[기타] 에러 핸들링 (Error Handling) 처리 경험기

yubi5050 2023. 10. 1. 17:32

에러 핸들링 구현기

프로젝트를 진행하며 초기 셋팅시, 에러에 대한 핸들링 처리를 통해 서버에 대한 신뢰도와 안정성을 높이는 작업을 진행하였고, 해당 과정에서 최종 정의한 에러 범위에 대한 정의, 정의 간 고려사항, 처리 방법 등에 대해 정리 해보았다. 

 

에러 핸들링 처리는 목적은 다 똑같지만.. 범위나 방법 등에 대해서는 기업마다 팀마다 다를 것이라고 생각한다.

 

에러 핸들링 이란?

런타임 과정, 혹은 특정 예외 상황에서 발생하는 에러/비정상적인 상황들 (ValidationError, AuthError, ServiceLogicError 등)에 대해 직접 의도된 동작으로 처리 하는 기술

 

에러 핸들링의 주요 목표는 다음과 같다.

- SW의 비정상적인 종료들로 인한 사용자 경험 저해 방지 

- 프로그램에 대한 안정성 및 신뢰도 향상

- 오류에 대한 정보를 전달하여 디버깅 및 유지보수 간 문제 해결에 도움 목적

 

에러 핸들링 간 고려 사항

에러 핸들링 정의를 위해 고려 되는 건, 상태 코드 (status)언어나 프레임워크, 패키지 레벨에서 발생하는 일반적인 에러 들 (VadliationError, AuthError) 이 고려 된다. 추가로 직접 예외 처리를 하고자 하는 경우 MyCustomError 등을 통해 발생하는 에러에 대해(=예외에 대해) 의도하는 방향으로 처리 할 수 있다.

 

각 역할을 간단히 설명하면

 

📘 응답 상태 코드 

- 200, 201, 204 : 성공, 생성 성공, 삭제 성공 등 (전반적인 성공에 대한 의미)

- 400, 401, 404, 422 : 잘못된 요청, 인증 에러, NotFound 에러, 엔티티 에러

- 500 : 서버에서 발생하는 에러 

 

상태 코드는 서버로 접근 하여 얻을 수 있는 응답에 대한 대분류로, 어떤 응답 상태 코드 까지 의도한 상황으로 보고 에러 핸들링을 할지에 대한 의사 결정 과정이 필요하다.

 

📘 일반적인 에러 

- 일반적인 에러는 언어 (ex. Python)나 프레임워크(Django), 패키지(Package) 레벨에서 발생하는 에러를 의미한다.

- 해당 에러 클래스들은 Exception 이란 클래스를 상속 받아 만들어지고, 각 언어/프레임워크/패키지 에 종속되어 있다.

- ex) VadliationError, AuthError, ParameterError, JWTTokenError 등

- Python Error [링크]

- Django Error [링크]

 

📘 커스텀 에러 (MyCustomError)

- 요청에 대한 서비스 로직 수행 중 발생하는 에러에 대한 정의

- 일반 에러와 같이 Exception를 상속받아 정의되며, 서비스에 따라 일정 정의 규칙에 따라 정말 커스텀하게 정의된다. 

- 이름은 에러지만 성공에 대한 컨트롤을 맡기도 하며, 특정 규칙에 따라 UI 페이지 이동 등의 액션이 연결 될 수도 있다.

- 정확하게 구분하면 예외 핸들링  (Exception Handling) 이라고도 볼 수 있다.

 

최종 진행 한 에러 핸들링 

팀 내에서는 서버로 들어오는 모든 요청에 대해 처리를 하고자 했고, 정의한 에러 핸들링 범위는 다음과 같았다.

 

 

📘 응답 상태 코드

- 200 번대, 400번대, 500번대에 대해 상관없이 서버로 들어왔다가 나가는 모든 요청은 응답 상태코드를 200으로 반환

- 단 404, 405 등 endpoint 주소, 스펙이 맞지 않아서 팅겨 나가지거나, 502, 503 처럼 어플리케이션 레벨에서 알 수 없는 서버 상태 등의 코드 들은 제외된다.

- 모든 상태 코드를 200으로 반환한 것은, 

장단이 있겠지만, 서버로 보낸 API 요청에 대해 응답 상태 코드를 받고 처리할 때, 상태 코드별로 분기 처리를 하길 원치 않았기 때문이였다.

 

 

📔 세부 에러 코드의 사용

응답 상태 코드가 200으로 통일 되는 상황에서, 발생하는 에러에 대해 성공/실패 여부를 알기 위해

응답에 대한 세부 결과 코드가 필요하였고, 세부 에러 코드 규칙 (=0~9999 의 임의의 숫자)을 내부적으로 정의하고

해당 규칙에 맞게 응답을 내려주었다. 

 

 

📘 일반적인 에러

- 일반적인 에러는 사실 언어/프레임워크/패키지 레벨에서 정의 되어 있는 모든 에러에 대해 핸들링을 정의 하는 것은 불필요 하다.

- 따라서 큰 갈래에 따라 에러 처리를 하고 나머지는 묶어 처리를 하였다.

 

ex) ValidationError -> 상태코드 200 / 세부 에러코드 3

ex) AuthError -> 상태코드 200 / 세부 에러코드 4

ex) 나머지 (정의되지 않은 에러) -> 상태코드 200 / 세부 에러 코드 9999

 

여기서의 나머지에 대한 정의된 적 없는 에러는 추후 디버깅을 통해 이슈를 파악한 후, 개발자가 추가적으로 보완한다. (개발자가 예측하지 못했던 에러로 궁극적으로는 999 발생하지 않는 경우를 지향)

 

 

📘 커스텀 에러

- 일반적인 에러를 제외한, 서비스 로직 수행 중 발생하는 에러에 대해, 1000~9999 사이의 숫자로 자유롭게 정의 하였다.

 

ex) 상태코드 200 / 세부 에러코드 1001 / 메시지 : "회원가입 간 유저의 패스워드가 일치 하지 않음"  -> "비밀 번호를 다시 확인해 주세요" 등의 추가 팝업 처리 가능

 

ex) 상태코드 200 / 세부 에러코드 1002 / 메시지 : "회원가입 성공!" -> 가입 성공 후 페이지 redirection 등의 처리 가능

 

즉 커스텀 에러는 성공/실패에 대해 세부 에러코드를 기준으로 처리하고 각 상황에 맞는 Action을 연결하게 된다.

 

 

📘 추가 예시

404로 처리 될 수 있는 API를 요청한다고 가정

 

ex) GET /user/3/mypages (요청 url endpoint를 mypages로 잘못 입력 한 경우)

-> 자원을 요청하는 입력한 주소 자체가 잘못됨 (경로가 존재하지 않는 경우)

-> 상태코드 404

-> 클라이언트 : 해당 에러 코드에 대해 404 처리 (페이지 없음)

 

ex) GET /user/{user_id}/mypage (user_id를 잘못 입력했다고 가정)

-> 입력한 주소에 해당되는 자원이 존재 하지 않거나, 유효하지 않음 (or 접근권한이 없음)

-> 서버에서는 user_id가 유효하지 않음 확인

-> 상태코드 200 / 세부 에러코드 1005 / 메시지 : "유저 id가 유효하지 않습니다!" 응답

-> 클라이언트 : 특정 팝업 처리 or 해당 에러 코드에 대해 404 처럼 처리(페이지 없음)

 

등의 방식으로 처리 가 가능하다.

 

물론 여기서의 404는 특정 세부 코드로 판별하는 것이 아닌 Http404 에러를 발생시켜 404 상태 코드로도 처리가 가능하다.