TIL 클린코드 - 7장. 오류처리
TIL (Today I Learned)
2022.05.05
오늘 읽은 범위
7장. 오류처리
책에서 기억하고 싶은 내용을 써보세요.
- 뭔가 잘못될 가능성은 늘 존재한다. 뭔가 잘못되면 바로 잡을 책임은 바로 우리 프로그래머에게 있다.
- 오류 처리는 중요하다. 하지만 오류 처리 코드로 인해 프로그램 논리를 이해하기 어려워 진다면 깨끗한 코드라 부르기 어렵다.
- 오류 코드보다 예외를 사용하라
- 논리가 오류 처리 코드와 뒤섞이지 않으니까 호출자 코드가 더 깔끔해진다.
- Try-Catch-Finally 문부터 작성하라
- try 블록에서 무슨 일이 생기든지 catch 블록은 프로그램 상태를 일관성 있게 유지해야 한다.
- 먼저 강제로 예외를 일으키는 테스트 케이스를 작성한 후 테스트를 통과하게 코드를 작성하는 방법을 권장한다.
- 미확인 예외를 사용하라
- 안정적인 소프트웨어를 제작하는 요소로 확인된 예외가 반드시 필요하지는 않다는 사실이 분명해졌다.
- 확인된 예외는 하위 단계에서 코드를 번경하면 상위 단계 메서드 선언부를 전부 고쳐야 하는 단점이 있기 때문이다.
- 예외에 의미를 제공하라
- 오류 메세지에 정보를 담아 예외와 함께 던진다. 실패한 연산 이름과 실패 유형도 언급한다.
- 호출자를 고려해 예외 클래스를 정의하라
- 애플리케이션에서 오류를 정의할 때 프로그래머에게 가장 중요한 관심사는 오류를 잡아내는 방법이 되어야 한다.
- 외부 API를 사용할 때는 감싸기 기법이 최선이다. 외부 API를 감싸면 외부 라이브러리와 프로그램 사이에서 의존성이 크게 줄어든다.
- 실패를 표현하는 예외 유형 하나만 정의해도 프로그램이 훨씬 깨끗해진다.
정상 흐름을 정의하라
- 독자적인 예외를 던지고 코드 위에 처리기를 정의해 중단된 계산을 처리한다. 대게는 멋진 처리 방식이지만, 때로는 중단이 적합하지 않은 때도 있다.
나쁜 예
// 비용 청구 애플리케이션에서 총계를 계산하는 허술한 코드 try{ MealExpenses expenses = expenseReportDAO.getMeals(employee.getID()); m_total += expenses.getTotal(); } catch(MealExpensesNoFound e){ m_total += expenses.getMealPerDiem(); } // 예외가 논리를 따라가기 어렵게 만든다좋은 예
// 특수 상황을 처리할 필요가 없게 만드는 것이 더 좋다 (SPECIAL CASE PATTERN) MealExpenses expenses = expenseReportDAO.getMeals(employee.getID()); m_total += expenses.getTotal(); public class PerDiemMealExpenses implements MealExpenses { public int getTotal() { // 기본값으로 일일 기본 식비를 반환한다. } }- null을 반환하지 마라
- 메서드에서 null을 반환하고픈 유혹이 든다면 그 대신 예외를 던지거나 특수 사례 객체를 반환한다(ex. 빈 리스트)
- null을 전달하지 마라
- 정상적인 인수로 null을 기대하는 API가 아니라면 메서드로 null을 전달하는 코드는 최대한 피한다.
- 깨끗한 코드는 읽기도 좋아야 하지만 안정성도 높아야 한다. 오류 처리를 프로그램 논리와 분리해 독자적인 시안으로 고려하면 튼튼하고 깨끗한 코드를 작성할 수 있다.
오늘 읽은 소감은? 떠오르는 생각을 가볍게 적어보세요
- 실무를 하다보면 오류처리는 정말 중요하다고 생각한다. 정말 철저하게 검증해봤다고 생각했는데 이게 오류가 난다고? 하는 일이 생각보다 많이 일어나기 때문이다.
- 최근에 한 프로젝트에서 백엔드 API가 오류처리가 제대로 되지 않아 모든 에러코드가 500에러로 넘어와서 고생한 적이 있다. 이런 상황이 되면 프론트가 데이터를 잘못 전달해서 나는 에런지 백쪽 문제인지 알 수가 없다. 오류 처리는 프론트와 백엔드가 뚫리지 않는 방패처럼 철저하게 처리 해야한다.
현재 회사에서 프론트 측에서 서버 데이터를 관리할 때 react-query를 사용하는데 react-query는 비동기 과정 에러 핸들링을 정말 편하게 할 수 있다. 다음과 같이 사용할 수 있다.
import { useQueryErrorResetBoundary } from 'react-query'; import { ErrorBoundary } from 'react-error-boundary'; const App: React.FC = () => { const { reset } = useQueryErrorResetBoundary(); return ( <ErrorBoundary onReset={reset} fallbackRender={({ resetErrorBoundary }) => ( <div> There was an error! <Button onClick={() => resetErrorBoundary()}>Try again</Button> </div> )} > <Page /> </ErrorBoundary> ); };- 깨끗한 코드는 읽기도 좋아야 하지만 안정성도 높아야한다는 말에 핵공감이다. 코드가 아무리 깨끗해도 버그가 많이 발생하는 코드라면 깨끗한 코드도 의미가 없어진다.
궁금한 내용이 있거나, 잘 이해되지 않는 내용이 있다면 적어보세요.
- javascript 오류처리에 대한 좋은 글들이 많아 아래 링크로 걸어둔다.
- Clean Code concepts adapted for JavaScript - 한글 번역판의 에러처리 부분을 가져온 내용이다.
- try/catch로 어떤 코드를 감쌌다면 그에 대한 계획이 있거나 어떠한 장치를 해야한다.
안 좋은 예)
try {
functionThatMightThrow();
} catch (error) {
console.log(error);
}
좋은 예)
try {
functionThatMightThrow();
} catch (error) {
// 첫번째 방법은 console.error를 이용하는 것입니다. 이건 console.log보다 조금 더 알아채기 쉽습니다.
console.error(error);
// 다른 방법은 유저에게 알리는 방법입니다.
notifyUserOfError(error);
// 또 다른 방법은 서비스 자체에 에러를 기록하는 방법입니다.
reportErrorToService(error);
// 혹은 그 어떤 방법이 될 수 있습니다.
}