예외(Exception)
Java 는 예외가 너무나 많다 ..
if 문으로 해결할 수 있는 코드도, 예외를 던진다.
사람의 사고방식은 선형적이기 때문에, 예외가 너무 많을 시 코드를 이해하기가 어렵다.
C++ 의 예외는 어디에서 올까?
언어 자체에서 오는 예외는 없다. -> c++ 의 예외는 프로그래머가 만든다.
C++ 표준 라이브러리가 많은 예외를 던지기도 한다. -> std::out_of_range, std::exception
하지만, Java 나 C# 에서 있는 언어 자체의 당연한 예외가 C++ 에는 없다.
예외 발생 사례
대부분의 경우에는 상당히 불필요하다. -> 프로그래머가 논리적으로 충분히 예측이 가능하다면, 분기 처리를 통해서 해결할 수 있다.
예외 처리에는 분명한 비용이 발생한다. -> 예외 처리를 하는 어셈블리어는 분기 처리를 하는 어셈블리어의 크기에 비해 매우 크다! (그만큼 비용이 커진다는 이야기..)
생성자 부분에서는 동적 할당을 할 때 메모리가 부족하다면 이론적으로만!! 필요하다!! -> 생성자 내부에서 예외 상황이 생긴다면 알릴 방법이 없다.. (RETURN 값이 없잖아!) -> 때문에, 생성자에서는 예외 처리가 필요하다.
하지만, c++ 은 성능을 중요시하는 업계! 예외처리는 기본적으로 느리기 때문에, 예외처리 기능을 끈다.. -> 기본적으로 생성자에서 메모리 부족으로 발생하는 문제를 예외처리 한다고 해서 해결할 수가 없다..
범위(range) 이탈
다음과 같이 try catch 를 사용해 해당 예외에 대한 처리를 적절하게 해줄 수 있다.
하지만 예외처리 한 코드에 대한 어셈블리어를 보게 되면, 그렇지 않은 어셈블리어에 비해서 코드량이 많다. -> 예외처리는 공짜가 아니다.. 비용이 든다.
또한 다음의 경우에서 볼 수 있듯이, 예측이 가능한 부분이라면, 예외처리를 하지 않고 분기문으로 처리가 가능하다. -> 엄격하게 말할 때, 예외는 프로그래머가 예측할 수 없어야 한다. (해당 사례는 충분히 예측이 가능하기에 예외라고 부르기에 애매하다;;)
0으로 나누기
정수를 0으로 나누었을 때 예외가 발생하는데, 이 예외는 c++ 의 예외가 아닌 OS 단의 예외이다. -> OS 단의 예외는 어플리케이션 단에서 발생하는 것이 아닌, OS 단에서 발생하는 것이기 때문에, 속도가 느릴 수 밖에 없다.
다음과 같이 예측이 가능한 부분이라면, 예외처리를 하지 않고 분기문으로 처리가 가능하다.
NULL 개체 사용
null 개체를 사용할 때 발생하는 오류도 c++ 의 오류가 아닌, OS 단의 예외이다. -> 없는 메모리를 참조한다고 하니 OS 에서 예외를 발생시킨다.
다음과 같이 예측이 가능한 부분이라면, 예외처리를 하지 않고 분기문으로 처리가 가능하다.
생성자
OS 예외 vs C++ 예외
에러 코드에 대한 오해를 풀어보자!
에러 코드는 가독성이 떨어진다?
함수를 잘 만들어 에러 코드를 반환 한다면 오히려 예외 처리의 코드량이 더 많다.. -> 잘 만들어진 에러 코드를 반환하는 메서드는 예외 처리와 가독성의 차이는 없다..
에러 코드의 가독성이 떨어진다면, 그 코드를 가독성이 좋게 만들면 될 일이지 에러코드 자체의 문제는 아니다!
예외처리를 하는 프로그램이 유지보수성이 높다?
예외 처리를 하면 예외가 발생하는 경우에도, 프로그램이 계속 돌아간다. -> 그래서 서비스가 다운되지 않고 계속 돌아간다.
뒷받침할 증거나 데이터가 없고, 실제로 잘 만들어진 운영체제 프로그램은 에러 코드를 사용하고 있다. (C사용) -> 웹도 에러코드 기반으로 돌아가고 있다. (http status code)
대부분의 프로그래머는 예외를 제대로 처리하지 못함.. -> 100% 예외 안정성을 가지는 프로그램을 만드는 일은 정말로 쉬운일이 아니다;; -> 상식적으로 사람은 사고방식이 선형적인데 메서드 내에서 10개의 예외가 발생한다고 파악할 수 있을까? 메서드의 결과를 통해서 이후 로직을 짜는 것이 유지 보수성이 높지 않을까? ( C++ 같이 2개의 리턴값을 전달할 수 있다면 예외처리가 필요 없을 것 같다! -> Java 에서 exception 을 적극적으로 사용하는 이유로 생각된다 )
최근 Java 에서는 exception 별로 처리하는 것도 아닌, 최상위 Exception 으로 잡아서 처리한다. -> 어디서 어떤 exception 이 발생하는지 파악하는 것은 상당히 어렵다 ;;
적절한 예외처리
적절한 예외처리 전략
유효성 검사 / 예외는 오직 경계에서만!
밖에서 오는 데이터를 제어할 수 없기 때문이다. -> 반대로 내부에서 우리가 관여하는 코드는 예측이 가능하기에 에러 코드로 처리가 가능하다.
ex, 외부에서 들어오는 웹 요청, 파일 읽기/쓰기, 외부 라이브러리
일단 시스템에 들어온 데이터는 모두 올바르다고 간주할 것!
assert 를 사용해서 개발 중 모두 고쳐 놓을 것! -> assert 는 프로그램 실행 중 조건이 거짓이면 프로그램을 종료하고 에러 메시지를 보여준다.
예외 상황이 발생할 때는 NULL 을 능동적으로 사용할 것!
사실 NULL 을 사용하는 것은 좋은 표준은 아니다..
사용할거라면, 함수의 이름을 잘 지어서 바로 판단이 가능하게 하자
결론
예외는 만병통치약이 아니다!
동일한 프로그래머가 로직과 예외를 모두 작성한다. -> 로직이 잘못되어 있으면, 예외도 잘못될 가능성이 높다. -> 동일한 프로그래머가 작성한 유닛테스트가 한계를 갖는 이유.
양질의 소프트웨어는 예외가 아니라 철저한 테스트 계획에서 만들어진다.
Last updated