Clean Code 07장. 오류 처리
TIL(Today I Learn)
2024.07.03
오늘 읽은 범위
7장. 오류 처리
책에서 기억하고 싶은 내용을 써보세요.
오류 처리
- 오류 처리는 프로그램에 반드시 필요한 요소 중 하나이다.
- 프로그램이 잘못되면 프로그래머가 책임지고 해결해야 한다.
- 오류 처리 코드로 인해 프로그램 논리를 이해할 수 없다면 클린코드라 할 수 없다.
오류 코드보다 예외를 사용하라
- 오류 플래그를 사용하거나 호출자에게 오류 코드를 반환하는 것은 비효율적인 방식이다.
- 함수를 호출한 즉시 오류 확인이 필요하므로, 호출한 코드가 복잡해질 수 있다.
- 오류가 발생하면 예외를 던지는게 낫다.
- 오류 코드를 확인할 필요가 없으므로, 호출한 코드가 깔끔해진다.
- 프로그램 논리가 오류 처리와 뒤섞이지 않는다는 장점이 있다.
try-catch-finally 문부터 작성하라
- try 블록에서 코드가 실행되다가 예외가 발생하면 그 즉시, catch 블록으로 실행 흐름이 넘어 간다.
try-catch 문은 일종의 트랜잭션과 비슷하다고 할 수 있다. - try 블록에서 무슨 일이 생기든지 catch 블록은 프로그램 상태를 일관성 있게 유지해야 한다.
- 예외가 발생할 코드를 작성할 때에는, try-catch-finally 문으로 시작하는 편이 낫다.
- 먼저 강제로 예외를 일으키는 테스트 케이스를 작성한 후 테스트를 통과하게 코드를 작성하는 방법을 권장한다.
자연스럽게 try 블록의 트랜잭션 범위부터 구현하게 되므로, 범위 내에서 트랜잭션 본질을 유지하기 쉬워진다.
언체크 예외를 사용하라
- 체크 예외는 여러 가지 비용을 치러야 한다.
- 체크 예외는 OCP(Open Closed Principle) 원칙을 위배한다.
- 메서드에서 체크 예외를 던지면, 상위 메서드의 선언부에 해당 예외를 정의해야 한다.
- 즉, 하위 단계에서 코드를 변경하면 상위 단계 메서드 선언부를 전부 고쳐야 한다.
- 결국 모듈을 다시 빌드한 다음 배포해야 한다.
- 최하위 함수에서 체크 예외를 던질 때 발생하는 시나리오
- 최하위 함수에서 선언부에 throws 절을 추가해야 한다.
- 해당 함수를 호출하는 상위 함수는 catch 처리하거나 선언부에 throws 절을 추가해야 한다.
- 결과적으로 최하위 단계에서 최상위 단계까지 모두 연쇄적인 수정이 발생한다.
- 경로에 포함된 모든 함수가 최하위 함수에서 던지는 예외를 알아야 하므로 캡슐화가 깨진다.
예외에 의미를 제공하라
- 오류 메시지에 실패한 연산 이름과 실패 유형을 포함하여 예외와 함께 던진다.
- 즉, catch 블록에서 오류를 기록하도록 충분한 정보를 넘겨준다.
호출자를 고려해 예외 클래스를 정의하라
- (잘못된 예외 처리 사례) 외부 라이브러리를 호출하는 try-catch-finally 문을 포함하는 코드로,
외부 라이브러리가 던질 예외를 모두 잡아내는 좋지 않다. - LocalPort 클래스처럼 ACMEPort를 감싸는 클래스는 매우 유용하다.
- 실제로 외부 API를 사용할 때는 감싸기 기법이 최선이다.
- 외부 API를 감싸면 외부 라이브러리와 프로그램 사이의 의존성이 크게 줄어든다.
- 테스트 코드를 작성하는 것도 쉬워진다.
정상 흐름을 정의하라
- 비즈니스 로직 관점의 예외적인 상황을 처리하기 위해 try-catch 블록을 사용하는 것은 좋지 않다.
- 예외가 프로그램 논리를 따라가기 어렵게 만든다. 특수 상황을 처리할 필요가 없게 만드는 것이 베스트다.
- MealExpenses 와 같이 특수 상황을 처리할 수 있도록 별도 클래스를 정의할 수 있다.
- 이를 특수 사례 패턴(SPECIAL CASE PATTERN)이라 부른다.
- 클래스를 만들어나 객체를 조작해 특수 사례를 처리하는 방식이다.
- 그러면 클라이언트 코드가 예외적인 상황을 처리할 필요가 없어진다.
null을 반환하지 마라
- 메소드에서 null을 반환하는 것은 좋지 않다.
- 여러 메소드에서 null을 반환한다면, 매번 null을 체크하는 로직이 별도로 추가되어야 한다.
- 메서드에서 null을 반환하고픈 유혹이 든다면 그 대신 예외를 던지거나 특수 사례 객체를 반환한다.
- 사용하려는 외부 API가 null을 반환한다면 감싸기 메서드를 구현해 예외를 던지거나 특수 사례 객체를 반환한다.
null을 전달하지 마라
- 메서드에서 null을 반환하는 방식도 나쁘지만 메소드로 null을 전달하는 방식은 더 나쁘다.
- 대다수 프로그래밍 언어는 호출자가 실수로 넘기는 null을 적절히 처리할 수 없다.
- 따라서 애초에 null을 넘기지 못하도록 금지하는 정책이 합리적이다.
결론
- 깨끗한 코드는 읽기도 좋아야 하지만 안정성도 높아야 한다.
- 오류 처리 로직을 프로그램 논리와 분리하면 클린 코드를 작성할 수 있다.
- 오류 처리를 프로그램 논리와 분리하면 독립적인 추론이 가능해지며 코드의 유지보수성이 높아진다.
오늘 읽은 소감은? 떠오르는 생각을 가볍게 적어보세요.
핵심 로직과 예외 처리 로직이 섞인 코드를 작성하다보니 보기에 깔끔하지 못한 코드를 작성해왔다.
예외 로직을 핵심 로직과 분리하는 것을 고민하고 리팩토링할 수 있는 개발자가 되어야 한다.무분별한 체크 예외 사용으로 발생하는 나비효과를 전혀 신경쓰지 못하고 있었다.
안타깝게도 무작정 throws 문으로 예외를 던지거나 생각 없이 try-catch 문으로 처리해왔다.
이런 방식은 프로그램 구조적으로 좋지 않으며 OCP 원칙을 위반하는 행위이다.핵심 기능을 작성하는 일에만 신경쓰고 싶었지만 항상 null과 관련된 문제가 발목을 잡았다.
if 문을 통해서 null 여부를 체크하는 부분이 프로그램에서 차지하는 비중이 작지 않았다.
null에게 끌려다니는 개발자가 되지 말아야 한다.
궁금한 내용이 있거나, 잘 이해되지 않는 내용이 있다면 적어보세요.
This post is licensed under CC BY 4.0 by the author.