최근 exception핸들링에 대해서 고민이 있다.
애플리케이션을 만들때 어떻게 예외를 처리해야할지에 대해 근본적인 해답이 없다는 생각이 든다.
외부 api호출같은 IO작업에서 주로 try catch같은 exception 처리를 하는게 좋은데, 이미 요청이 실패하거나 크리티컬한 이유로 실패해서 exception이 터졌다면 애초에 처리하고자하는 정상적인 상태에서 깨진 상황인데 이상황에서 뭘 더 핸들링할까라는 생각이 든다. 바꿔 말하자면 이미 깨진 상태인데 뭘 더 하겠냐라는 뜻이다.
크리티컬한 이슈가 아닌 경우를 생각해보자면, 뭔가를 등록하려는데 중복 등록이라던지 혹은 워닝으로만 남겨도 될만한 작업이라면 쉽다. 그런데 인증이 잘못되었거나 뜬금없는 timeout 혹은 전혀 대비되지 않았던 예외케이스처럼 크리티컬한 상황에는 과연 여기서 깨진 상태를 어거지로 exception으로 캐치해서 뭔가 장황한 핸들을 해야할까? 그냥 넘어가야할까? 혹은 최소한의 처리?
그나마 넣어줘도 수 회 retry나 exponential backoff 정도가 대안이다. 혹은 내 슬랙이나 이메일로 알림이 오게 한다던지 말이다. 그런데 결국 이것도 한계가 있다.
결국 근본적인 고민은 크리티컬한 이슈에 대해 어플리케이션을 죽을 가능성을 그대로 둬야할지, 어거지로 현재 상태를 커버하기 위해 코드로 땜질을 해놓던지에 대한 고민이다.
물론 어플리케이션의 성격에 따라 다르긴 할 것 같다. 백엔드라면 죽게 놔둬선 안된다는 생각이 들고 간단한 툴 정도면 그냥 죽게 놔두고 내가 exception 로그를 보고 고치게 하면 될 것 같다는 생각도 든다.
이렇게 터지게 두는 것의 장점은 원인을 정확히 따고 고칠 수 있다는 점이지만 내가 직접 운전하는 자동차가 길가에서 퍼지면 그때 고쳐서 다시 운전하겠다는 마인드이기도 하다.
그런데 만약 exception을 핸들링하겠다고 뭔가 로직을 추가한다면 대개의 경우 정확한 exception 원인을 찾기 어렵게 몇 개의 로직으로 감싸져서 처음 보고 잠시 헷갈릴 여지가 있다. 또한 차가 온갖 범퍼로 덩치가 커지게 된다. 앞에만 달아놓아야할 범퍼가 문에도 달려있는 셈이다.
Airflow같은 워크플로우 관리도구의 경우 exception 터지면 Fail 알림받게 만들고 다시 돌리면 되겠지만, 백엔드의 경우 죽으면 안된다. 서비스 중인데 그냥 죽게 두는 건 치명적이라고 생각한다. 간단하게 생각하자면 프론트단에서 예외 케이스를 던져주지 못하게 validation로직을 잘 줘야하겠지만 그렇지 않은 경우가 있다.
간단하게 에러 응답을 던져주면 되지 않냐고 생각할 수 있겠지만 이런 고민을 하게 된데는 이유가 있다. 클라이언트단으로 에러 응답을 줄 수 없는 상황이면서 예외 케이스가 발생할 가능성이 꽤 높으며 잦은 상황이라서 그렇다.
다른 외부 소스에서 데이터를 읽어오는 경우다. 그 데이터 처리를 위한 애플리케이션이고, 자주 바뀌고, 바뀌었다고 말도 안해주는 그런 데이터다. 매번 바뀌었는지 확인할 수도 없다. 데이터 포맷에 의존이 크기 때문에 살짝 바뀌는 것 자체가 exception 요소다. 그런데 해당 데이터를 등록하고 수정하는 제3의 api 또한 간헐적 500 혹은 상태 변경의 지연이 수 초 단위로 걸린다.
이런 경우 어떻게 할까?
뭐 사실 그냥 하던대로 처리해줄만한 곳은 해주고 성공과 실패 히스토리를 깔끔하게 남겨주고 retry + Ex backoff로 스파이크성 timeout을 막아주고 실패 처리에 대한 건수를 체크해서 일정 retry 초과하면 알림이 오게도 할 수 있다.
이렇듯 적절한 밸런스를 찾아야하겠지만 완벽하게 막아보고 싶은 욕심이 생겨서 과하게 추가하는 것 같다는 생각이 들곤한다. 그냥 순전한 욕심이다...
고민을 좀 해봐야할 문제인 것 같다.
고민을 하던 중 포프님의 영상을 하나 찾아봤다. exception에 대한 좋은 내용이 있다.
exception이 터졌다는건 이미 상태가 깨진 것이기 때문에 이후에 더이상 진행이 되지 않아야 한다는데, 이에 동의한다.
깨진 상태로 뭘 더 진행해봤자 연달은 exception만 얻게 될 거다.
흥미로운 점은 exception이 났을때 그걸 그냥 throw해버려서 상위의 어딘가에서 catch해서 처리하게 하지 말고 exception난 곳에서 우선 처리를 하고 특정 에러 code를 리턴시키는 방식도 있다고 말씀하신다.
이게 꽤 좋은 방법이 될 수도 있다고 생각한다. 전부터 에러 코드를 여러 케이스 만들어두면 괜찮다고 생각했는데 이 또한 깔끔한 방식이 될 수 있겠다.
물론 자세한 exception에 대한 log정보는 남겨야한다. exception에 대한 로그를 제대로 찍지 않아서 막상 발생했을때 당황했던 적이 몇번있다. exception 자체에 대한 정보도 좋지만 당시 주변환경, 시도한 데이터 정도는 찍어주면 나중에 디버깅에 도움이 된다. 물론 로그 범벅되어 알아보기 힘든 양을 어마무시하게 찍진 말고.
exception을 핸들링하겠다는 개발자의 의도가 자체가 무의미할 수 있다고도 말씀하시는데 어느정도 맞는 말 같다. 이미 터졌는데 뭘 더해봐야 소용없다.
현재 받은 요청은 크래시가 나서 처리할 순 없지만, 다음 요청 정도는 처리할 수 있다는 정도가 적당한 타협인 것 같다.
백엔드 애플리케이션이 죽게 할 순 없으니 말이다.
암튼 고민이다.
https://www.youtube.com/watch?v=YGOE5CEkX0o
'개발 이야기' 카테고리의 다른 글
[Dev] 큐를 사용한 아키텍처에 대한 고민 (메세지 단위) (0) | 2024.05.14 |
---|---|
[개발 이야기] 신입에서 주니어로 (0) | 2024.04.27 |
[개발 이야기] 개인 공부 플랜 (0) | 2023.11.03 |