오류 처리는 다양한 방식이 있습니다.

JavaScript와 Python은 예외를 던지지만, Rust 언어는 암시적으로 예외를 던집니다.
C 언어와 Go 언어는 오류 값을 반환하며, 이 값이 -1이나 빈 값인지 판단해야 합니다.
나는 항상 어떤 방법이 더 좋을지 궁금했어요.
지난 며칠에, 나는 오랜 시간 전에 쓰인 글을 읽었습니다.기사명확히 제시하다예외를 던지는 것이 상태 코드를 반환하는 것보다 좋습니다그의 이유는 매우 설득력이 있어서, 글이 아직 중국어로 번역되지 않았어 보여서 번역해 냈습니다.
예외 및 반환 상태 코드
저작자: 너드 바첼더 (Ned Batchelder)
원문 웹사이트:nedbatchelder.com

소프트웨어에서 오류 처리는 두 가지 방식이 있습니다: 예외를 던지는 것(throwing exceptions)과 상태 코드를 반환하는 것(returning status codes).
대부분의 사람들이 예외가 더 나은 처리 방식이라고 생각하지만, 여전히 상태 코드를 선호하는 사람들도 있습니다. 이 글에서는 예외가 왜 더 나은 선택인지 설명합니다.
1. 코드가 깨끗합니다.
예외는 대부분의 코드에서 오류 처리 단계를 생략할 수 있게 합니다. 예외를 잡지 않는 레이어를 통해 자동으로 상위로 전달됩니다. 그래서 오류 처리 논리가 전혀 없는 코드를 작성할 수 있어, 코드가 간결하고 읽기 쉬워집니다.
두 가지 방식으로 동일한 간단한 함수를 작성하는 것을 비교해 보겠습니다.
먼저 상태 코드를 반환하는 방식입니다.
STATUS DoSomething(int a, int b) { STATUS st; st = DoThing1(a); if (st != SGOOD) return st; st = DoThing2(b); if (st != SGOOD) return st; return SGOOD; }
위 예제에서는DoThing1(a)와DoThing2(b)의 반환 값이 정상인지 확인해야 다음 단계로 진행할 수 있습니다.
만약 예외가 발생하면, 중간의 오류 판단이 필요 없습니다.
void DoSomething(int a, int b) { DoThing1(a); DoThing2(b); }
이것은 가장 간단한 경우입니다. 복잡한 시나리오에 만나면, 상태 코드로 인한 소음이 더 심해질 수 있으며, 예외는 코드의 깔끔함을 유지할 수 있습니다.
이제, 의미 있는 반환 값
상태 코드는 귀중한 반환 값을 차지하므로, 반환 값이 올바른지 확인하기 위해 코드를 추가해야 합니다.
일부 함수는 원래 정상 값 하나만 반환해야 했지만, 이제 오류 상황을 반환해야 합니다. 시간이 지남에 따라 코드량이 점점 증가하고, 함수는 점점 커지며, 반환 값은 점점 복잡해집니다.
예를 들어, 많은 함수의 반환 값은 오버로드되어 있습니다: "실패하면 NULL을 반환" 또는 실패 시 -1을 반환합니다. 결과적으로 이 메서드를 호출할 때마다 반환 값이 NULL이나 -1인지 확인해야 합니다. 만약 함수가 나중에 새로운 오류 반환 값을 추가하면, 모든 호출 지점을 업데이트해야 합니다.
예외가 발생하면, 함수는 항상 성공한 경우에만 반환되고, 모든 오류 처리도 한 곳에 간소화할 수 있습니다.
3. 더 풍부한 오류 정보
상태 코드는 일반적으로 정수이며 전달할 수 있는 정보가 상당히 제한적입니다. 오류가 파일을 찾을 수 없는 경우, 어떤 파일인지 알 수 없습니다. 상태 코드는 그렇게 많은 정보를 전달할 수 없습니다.
상태 코드를 반환할 때, 특별한 오류 로그에 오류 메시지를 기록하는 것이 좋습니다. 호출자는 그 안에서 상세 정보를 얻을 수 있습니다.
예외는 완전히 다릅니다. 클래스의 인스턴스이므로 많은 정보를 가질 수 있습니다. 예외는 서브클래싱될 수 있으므로, 다른 예외는 다른 데이터를 가질 수 있어 매우 풍부한 오류 메시지 체계를 형성합니다.
4. 숨겨진 코드를 처리할 수 있습니다.
일부 함수는 상태 코드를 반환할 수 없습니다. 예를 들어, 생성자는 명시적인 반환 값을 없으므로 상태 코드를 반환할 수 없습니다. 또한, 소멸자와 같은 일부 함수는 직접 호출할 수조차 없으므로, 더不用说 반환 값입니다.
반환값이 없는 함수들은 예외 처리를 사용하지 않으면 오류 메시지를 전달하는 다른 방법을 찾거나, 이 함수들이 실패하지 않을 것처럼 가장해야 합니다. 간단한 함수는 오류 없이 작동할 수 있지만, 코드 양은 점점 증가하고, 오류 발생 가능성도 증가합니다. 실패를 표현할 방법이 없다면 시스템은 더 쉽게 오류를 일으키고 이해하기 어려워질 것입니다.
5. 오류의 가시성
개발자가 실수로 오류 처리 코드를 작성하지 않은 경우 어떻게 될지 생각해보세요.
반환된 상태 코드를 확인하지 않으면 오류가 발견되지 않고, 코드는 성공적으로 실행된 것처럼 계속 실행됩니다. 나중에 코드가 실패할 수 있지만, 이는 많은 단계의 작업 이후에 발생할 수 있습니다. 그때 어떻게 원래 오류가 발생한 위치를 추적할 수 있을까요?
반대로, 예외가 즉시 포착되지 않으면 호출 스택에서 위로 전파되어 더 높은 catch 블록으로 도달하거나 최상위로 이동하여 운영체제에 처리를 맡깁니다. 운영체제는 일반적으로 오류를 사용자에게 표시합니다. 이는 프로그램에 좋지 않지만, 오류는 최소한 눈에 보이는 것입니다. 예외를 볼 수 있으며, 그것이 던져진 위치와 처리되어야 할 위치를 판단할 수 있으므로 코드를 수정할 수 있습니다.
여기서는 오류가 발생하지 않은 경우를 논의하지 않습니다. 이 경우 반환 상태 코드나 예외를 던져도 무용합니다.
따라서 발생한 오류가 처리되지 않은 경우는 두 가지 상황으로 요약될 수 있습니다: 하나는 반환 상태 코드가 문제를 숨기고, 다른 하나는 예외를 던지면 오류가 눈에 보입니다. 어떤 것을 선택하시겠습니까?
6. 반박
유명 프로그래머 Joel Spolsky는 반환 상태 코드가 더 나은다고 생각합니다. 그는 예외가 훨씬 더 나쁜 goto 문장이라고 생각합니다.
"예외는 소스 코드에서 보이지 않습니다. 코드 블록을 읽을 때 어떤 예외가 발생할 수 있는지, 그리고 어디서 발생할 수 있는지 알 수 없습니다. 이는 세심하게 코드를 검토하더라도 잠재적 오류를 발견할 수 없음을 의미합니다."
"예외는 함수에 너무 많은 가능한 출구를 만듭니다. 올바른 코드를 작성하려면 모든 가능한 코드 경로를 고려해야 합니다. 가능한 예외를 던질 수 있는 함수를 호출할 때 즉시 예외를 포착하지 않으면 함수가 갑자기 종료될 수 있거나 예상치 못한 다른 코드 경로가 발생할 수 있습니다."
"이 말들은 매우 합리적으로 들리지만, 반환 상태 코드로 변경하면 함수의 모든 가능한 반환 지점을 명시적으로 확인해야 합니다. 따라서, 당신은 명시적인 복잡성을换取了隐式的复杂性. 이것도 단점이 있습니다. 명시적인 복잡성은 당신이 숲을 보지 못하고 나무만 보게 하며, 코드가 혼란스러워질 수 있습니다.
이러한 명시적인 복잡성에 직면할 때, 프로그래머는 지치도록 작성하게 되고, 결국 사용자 정의 방법으로 오류 처리를 숨기거나 오류 처리를 생략할 수 있습니다.
앞서 언급한 것은 오류 처리를 숨기고, 명시적으로 처리된 것을 다시 숨겨진 처리로 변경할 뿐이며, 원래의 Try 방법보다 편리하지 않고 기능도 부족합니다.
뒤쪽은 오류 처리를 생략하는 것이 더 나빠서, 프로그래머가 특정 오류가 발생하지 않을 것이라고 가정하여 위험을 숨기게 됩니다.
일곱, 요약
반환 상태 코드는 사용하기 어렵고, 일부 경우에는 전혀 사용할 수 없습니다. 이는 반환 값을 강탈합니다. 프로그래머는 쉽게 오류 처리 코드를 작성하지 않을 수 있어 시스템 내에서 조용한 장애를 유발할 수 있습니다.
예외는 상태 코드보다 우수합니다. 만약 당신의 프로그래밍 언어가 예외 처리 도구를 제공한다면, 그것들을 사용하세요. __JHSNS_SEG_a1dd5f4b_54__ (끝)


























