※ 일본의 한 블로그 글을 번역한 포스트입니다. 오역 및 의역, 직역이 존재할 수 있으며 틀린 내용은 지적해주시면 감사하겠습니다.
예외의 사고방식 근원
예외또란 무엇인가? 보통 이것을 에러이며 무언가 제대로 되지 않았을 때에 알려주는 것(에러가 아닌 예외도 있지만, 현 시점에서는 이 부분은 신경쓰지 않도록 하자) 역할을 한다.
일부의 프로그래밍 언어는 에러를 반환값으로 반환하는 것을 장려하고 있으며, 당신은 반환 값을 확인한다. Python 경우 예외의 사용을 장려하고 있는 언어이며, 이러한 처리에 대해 고려할 필요가 있다.
예외는 반드시 프로그램의 충돌을 일으키는 것은 아니다. 예외는 처리할 수 있다. 예외의 원인인이 완전히 코드 버그(존재하지 않는 변수에 접근하는 등)일 수도 있지만 예외의 발생을 예측할 수 있는 경우도 있다. 예를 들어 파일을 열려고 했을 때, 그 파일이 존재하지 않을 수도 있다. 또한 모듈을 가져오려고 했을 때, 그 모듈이 설치되어 있지 않을 수도 있다. 그리고 데이터 베이스에 접속하려 했을 때, 그 데이터베이스가 이용할 수 없는 상태일 수도 있다.
이러한 상황에서 예외가 발생한다는 것을 알고 있다면 try ... except 블록을 사용하여 예외를 처리해야한다. 그러나 예외는 반드시 발생한 함수 안에서 처리될 필요는 없다.
만약 그 함수가 예외를 처리하지 않고 있다면 그 예외는 그 함수를 호출하고 있는 함수에 전달되고, 다시 해당 함수를 호출하고 있는 함수에 전달 되며 스택을 거슬러 올라가게 된다. 이 상황에서 어떠한 함수에서도 예외에 대한 처리가 이루어지지 않은다면 프로그램은 충돌하게 되고 Python의 경우 Traceback이라는 표준 에러를 출력하여 처리가 도중에 끝는다.
거듭말하지만 이러한 결과를 원할 수도 있다. 그것은 만들고자하는 프로그램이 무엇을 하느냐에 달려 있다. 즉 예외는 그렇게 거창하고 힘든 것이 아니라 기대한 동작을 제외하고는 예외이고 그렇다고 모든 예외에 대응할 필요는 없으며 필요한 것만 캐치하면 된다.
구체적인 이야기
방침 : 예외 케이스마다 독립 예외를 만들어, 그것을 송출하도록 한다.
예를 들어 신청 API를 만들 때, 사전조건으로 미신청 상태이어야한다는 것으로 설정한다(신청 상태에서는 이 API를 호출하는 것을 기대하지 않는다고 한다).
이 때의 예외 대응으로 ApplicationException 클래스를 만든다. 혹시 신청되어 있다면 ApplicationException을 송출하도록 한다.
// 사전 조건 체크
if(신청 상태인지 아닌지){
throw new ApplicationException('already submitted');
}
신청처리()
ApplicationException이 송출됐다는 것은 사전에 상정했던 예외가 송출됐다는 표현이 된다.
그리고 운용에 따라 사용될 타이밍에, 예를 들어 "어떤 항목이 모두 입력되어 있을 것"이라는 사전 조건(한 개라도 입력되지 않은 항목이 있는 경우에는 신청도지 않는 조건)이 필요해졌다고 하자.
혹시 사전조건을 만족하지 않은 상태에서 신청했을 경우 "입력 항목이 불충분합니다"라는 메시지를 보내고 싶다고 됐다고 치자. 이러한 경우, AlreadySubmittedException 클래스 와 IncompleteInputException 클래스를 만들어 그렇을 송출하도록 한다.
// 사전 조건 체크
if(입력 항목이 불충분한지 아닌지){
throw new IncompleteInputException('incomplete input');
}
if(신청 상태인지 아닌지){
throw new ApplicationException('already submitted');
}
신청처리()
그리고 프론트 엔드측에서 예외에 따라 메시지가 다르게 표시되도록 할 수 있다.
보충 : 맨처음부터 구체적인 예외 클래스를 만들지 않는다. 운용에 따라 불편함이 생겼을 타이밍에 구체적인 예외 클래스를 만든다.
위에서 설명했는 Application Exception을 만든 후에 필요가 생겨 Already Submitted Exception를 만들었다. 여러 개의 사전 조건이 생기고, 그 사전 조건이 충복되지 않은 케이스에 따라 프론트 엔드에서의 대응을 바꾸고 싶다는 구체적인 요건이 발생했을 타이밍에 예외 클래스를 만드는 것이 좋다.
보충 : 예외를 발생시킬 때에 전달한 메시지에 대해서
예시 코드에서는 예외 메시지로 "already sumitted"이나 "incomplete input"이라는 메시지를 작성했다. 이 메시지와 관련해서는 서비스 이용자, 악의가 있는 서비스 이용가, 개발자의 시점으로 메시지를 생각해야한다.
메시지가 너무 정중하고 상세하면 악의가 있는 서비스 이용자에게 공격의 힌트를 주게 되고, 혹시 서비스 시용자에게 이 메시지가 보여지면 이 문장은 뭐지라고 생각되면 좋지 않다. 따라서 적당히 균형 잡힌 메시지를 생각해야 할 필요가 있다.
어느 타이밍에서 예외에 대해 고려해야 하는가
처음에는 예외 처리에 대해 생각하지 않고 우선 정상적일 때의 처리에 대해 작성해나간다. 그 이후에 테스트 등을 통해 여러 조작을 해보고 예외가 생기면 그때 예외 처리를 추가해야한다 (그렇다고는 하지만 경우에 따라 예외부터 생각하는 경우도 있다).
마무리
예외를 의미 있게 다룸으로써 사용자가 사용하기 쉬운, 엔지니어가 개선하기 쉬운 제품 개발로 이어진다. 예외에 관련해서는 프로덕트나 멤버에 따라 정답이 달라지기 때문에 반드시 이 포스트의 내용이 절대적이라고 할 수는 없지만, 예외 처리에 대해서 부담을 갖지 않았으면 하는 마음으로 이 포스트를 공유한다.
참고자료
'IT > 기초 지식' 카테고리의 다른 글
JWT(JSON Web Token)란? (0) | 2024.03.10 |
---|---|
2024년 2월부터 바뀌는 Gmail의 이메일 발신자 가이드 라인과 필요한 대응 정리 (0) | 2023.12.11 |
Vue2와 Vue3의 차이 (0) | 2023.07.12 |
그림으로 이해하는 SOLID 원칙 (0) | 2023.07.09 |
[DDD] Entity, VO, DTO 등 관련 용어 정리 (0) | 2023.07.03 |