All Articles

클린 코드 스터디 (7): 오류 처리

7. 오류 처리

프로그래머라면 예기치 않은 상황에 대비하기 위해 대비해야할 책임이 있습니다. 이는 깨끗한 코드와 오류 처리가 연관있다는 말입니다. 따라서 오류를 체계적으로 잘 정리할 필요가 있겠습니다.

오류 코드보단 예외를 사용하자

옛날 프로그래밍 언어에는 예외를 제공하지 않아서 에러코드로 처리했다고 합니다. 문제가 발생하면 에러코드도 함께 확인했어야 했죠. 하지만, 이제는 다릅니다. 로직과 오류처리 코드를 분리할 수 있습니다.

try-catch-finally 구문부터 쓰자

이 구문을 사용하면 예외를 통해 로직이 실행되는 문제의 범위를 지정할 수 있습니다. try 구문에서 문제가 생기면 실행중단 후 catch 블록으로 로직을 옮길 수 있습니다.

트랜잭션과 비슷하다고 할 수 있겠습니다. 이 구문을 통해 프로그램의 상태를 일관성있게 유지하는 것이 목표이기 때문입니다. 그러므로 예외가 발생할 수 있는 로직은 try-catch-finally 구문 안에 배치하는 편이 좋습니다.

이 경우에는 assertRaises() 를 사용해서 TDD를 구현하면 되겠지요.

미확인(unchecked) 예외를 사용하자 (Java 이야기입니다)

옛날 자바는 메소드 선언 시 메소드가 반환할 예외를 모두 열거했습니다. 메소드가 반환하는 예외는 메소드 유형의 일부였습니다. 코드가 메소드를 사용하는 방식이 메소드 선언과 맞지 않으면 컴파일도 못했다고 합니다.

그렇지만 확인된 예외를 사용하지 않게된 이유는 아래와 같습니다.

  1. OCP 를 위반합니다. 확인된 예외를 던졌는데 catch 블록이 세 단계 위에 있다면 그 사이 메소드 모두가 선언부에 해당 예외를 정의해야 합니다. →하위 단계에서 코드를 고치면 상위 단계 메소드를 모두 고치고 새로 빌드한 후 배포해야한다는 말입니다.

이 글들을 먼저 읽어보시는 것을 추천드립니다. Checked Exceptions v. Unchecked Exceptions 기본 설명 , Checked Exception이 꺼려지는 이유에 대한 국내 아티클 을 소개합니다.

정리해보자면 자바의 설계상 안전한 소프트웨어를 구현하기 위한 도구였으나 그렇게 되지 못했다 라고 이해했습니다.

예외에 의미를 제공하라

예외를 던질때는 호출스택 뿐 아니라, 오류 메시지에 적절한 정보를 담을 필요가 있습니다. 가능하다면 앱이 로그를 찍어서 디버깅하기 편하기 만들어줍니다.

호출자를 고려해 예외 클래스를 정의하라

애플리케이션 단에서는 오류를 잘 잡을 궁리를 해야합니다.

외부 라이브러리에서 던지는 모든 에러를 메인 로직에서 모두 캐치하려고 할 것이 아니라, 호출하는 라이브러리를 감싸서 거기서 발생하는 모든 에러를 wrapping 하는 편이 훨씬 깔끔합니다. 이 기법의 장점은 아래와 같습니다:

  1. 외부 라이브러리 API의 예외를 wrapper 클래스에서 처리할 수 있습니다.
  2. 라이브러리를 갈아타도 코드 변경에 대한 비용이 적습니다.
  3. 특정 라이브러리에 발목잡히지 않습니다.

정상 흐름을 정의하라

상기 이야기대로 코드를 짜더라도, 로직 중단이 적합하지 않을 때가 있습니다. 이럴 땐 리팩토링에 나오는 SPECIAL CASE PATTERN이 적합합니다1.

  1. 일단 원하는 로직을 풀어둡시다.
  2. 특수사례를 클래스 혹은 객체 안에서 해소하게 묶고 결과를 리턴하게 만듭니다.

null을 리턴하지 마라

악명높은 NullPointerException이 어디서 터질지 캐치하기 힘든 로직을 짜지않기를 주문하는 글입니다. 대체 어디서 온 null 인지 알 수 없도록 코드를 짜지 않고, 이에 대한 해결책으로 앞서말한 SPECIAL CASE PATTERN을 다시 소개합니다.

null을 전달하지 마라

마찬가지로, null을 메소드로 전달하는 것은 더 악질이라고 합니다. 진짜 어디서 문제가 발생할지 잡기 더 힘들 것으로 보입니다… 저자는 해결책으로 아래 내용을 소개합니다.

  1. 안좋은 케이스에 대해 새 Exception 을 만들고, 안좋은 케이스를 맞닥뜨렸을 때 이를 사용하는 것을 권합니다.
  2. assert 구문을 사용해서 아예 Exception을 발생시킵니다.

아예 null을 못받도록 하는 것이 합리적이라고 합니다.

결론

읽기 좋은 코드 뿐 아니라 안정성 있는 코드 또한 고려해야합니다. 오류처리를 프로그램 논리와 분리하여 관심사를 각각 분리하고 이를 통해 로직(에러든 본디 로직이든)에 집중할 수 있도록 합시다.


  1. 상세한 내용은 리팩토링 개정 2판의 ‘10.5 - 특이 케이스 추가하기’ 를 참고하십시오.

Published Feb 3, 2023

Non scholæ sed vitæ discimus.

his/him