작성
·
45
·
수정됨
0
토비 스프링 3.1 책과 강의를 병행하면서 궁금한 점이 있어서 질문 남깁니다.
책 [4.1.4 예외처리 전략 - 애플리케이션 예외] 에서는 '외부의 예외상황이 원인이 아니라 애플리케이션 자체의 로직에 의해 의도적으로 발생시키는 예외도 있다' 라고 하는데요.
예를들어 잔고 부족과 같은 상황에서 예외상황에 대한 리턴값을 코드화 하는것 보다는 InsufficientBalanceException
과 같은 체크 예외를 던지는것도 좋은 방법(코드가 이뻐짐) 으로 소개가 됩니다.
그러나 강의에서 예외는 '정상적인 값을 리턴으로 수단으로 사용' 하는 것은 지양하고 '예외적인 상황에서만 사용' 해야 한다고 해주셔서 조금 헷갈리네요.
제가 이해를 잘못했을 수도 있는데요. 같은 상황이 맞다면 어떤게 더 맞는 방향인지 궁금합니다.
답변 1
0
날카로운 질문을 해주셨네요.
지금 예로 드신 InsufficientBalanceException을 복구가능한 정상적인 작업흐름, 그러니까 비즈니스적으로 의미있는 설계된 플로우의 하나로 볼 것인가, 또 그게 어느 정도의 비율로 나타날 것인가 등에 따라서 사실 접근 방법은 조금씩 다른 듯합니다.
InsufficientBalanceException을 정상적인 비즈니스 로직의 케이스의 하나로 보지만 극히 드물게 일어난다고 가정을 해보죠. 예를 들어 프론트에서 이미 잔고를 확인한 상태에서만 이체 요청을 한다거나 결제를 하도록 만들었는데, 하지만 그 순간 다른 결제가 돌아가면서 잔액이 부족해지는 경우가 있습니다. 아주 드물지만 없지 않죠. 비율이 얼마나 될까요. 전체 이체 요청 만 건당 하나라고 보면, 이건 예외적인 상황이 맞습니다. 그래서 이런 건 시스템 장애나 버그에 의한 예외는 아니지만 극히 예외적인 상황이라서 Exception을 사용하고 싶은 거죠. 그러면서 이런 시도를 했다는 기록을 DB에 남기고 싶다면, 체크예외를 쓸 수 있습니다. 그러면 이체 자체를 제외한 나머지 DB 작업은 트랜잭션이 커밋되면서 남게 되고, 이 예외를 앞의 Controller가 받아서 이 경우에 사용자에게 보내줄 알림을 전해줄 수 있겠죠.
그런데 이런 0.01% 수준의 예외적인 케이스를 위해서 리턴 값에 의미를 부여해서 잔액 부족은 -1. 이런식으로 정해서 이걸 if로 잡는 코드를 넣으면, 이를 처리하는 코드가 뭔가 메인이 되는 느낌이죠. 게다가 응답 값에 의미를 부여하는 것은 자칫 버그가 발생하기 쉽죠. 어떤 개발자는 이 서비스 코드를 호출하면서 0보다 작으면 에러인가 이럴 수도 있고, 어떤 개발자는 이거 상수로 정의해야 하는 거 아닌가 해서 INSUFFICIENT_BALANCE = -1 이렇게 정의해서 쓸 수도 있고, 나중에 예외 값은 -100으로 하자고 고치게 되면 로직이 틀어지겠죠. enum으로 정의할 수도 있지만, 다른 응답 값이 있는 경우에 예외 경우를 체크하기 위한 enum을 같이 리턴하려면 또 클래스로 만들어야 하죠. 그래서 이때는 체크 예외를 만들어서 이런 경우가 있을 수도 있다는 걸 사용하는 컨트롤러 등의 코드에 명확하게 알려주고, 이때 대응하는 코드를 만들도록 강제할 수 있습니다.
아마 책에서는 이걸 강조해서 설명했던 것 같습니다. 강의에서 이 케이스를 다루지는 않았지만, 핵심은 복구 불가능한 예외는 예외로 처리하고 굳이 핸들링하지 않는게 맞다는 것을 이야기했습니다. 이 잔액 부족은 복구 가능한 것일까요? 여기서 예외를 프론트엔드까지 다루는 시나리오에 따라서 여러 판단이 있을 수 있을 듯합니다. 이걸 의미있게 처리하기 위해 정상 플로우의 특별한 상황으로 정의하고 API에서 특정 응답을 주도로 만들고, 프론트는 이런 잔액부족 상황을 알게 되면 다시 최신 잔액을 확인하는 API를 호출하게 만들고, 안내를 주도록 할 수 있죠. 또는 이걸 응답에서 그래서 부족한 이유와 잔액을 이 케이스에서는 API 응답으로 만들 수도 있습니다.
또는 이건 어쨌든 처리할 수 없는 예외상황이니 메시지만 딱 보여주고 말겠다면, 서비스에서부터 런타임 에러를 던지고, @ControllerAdvice에서 그 Exception에 담긴 에러 메시지를 담아서 리턴하고, 프론트는 에러 메시지를 띄워주고, 사용자는 다시 잔액 확인하러 뒤로 가고.. 등등의, 정말 예외적인 케이스니까 굳이 이걸 처리하는 코드를 복잡하게 만들지 않도록 할 수 있습니다.
어쨌든 예외 상황을 리턴 값으로 다루지는 마시고, 복구 가능한 예외인지 아니면 일반적인 정말 예외 상황인지에 따라서 예외 타입을 결정해서 사용하시면 될 듯합니다.
결국 그 상황이 비즈니스적으로 얼마나 의미있는 흐름인지가 중요한거네요.
빠른 답변 감사드립니다~