Close
Close full mode
logo만렙 개발자 키우기

7장. 오류 처리

Git RepositoryEdit on Github
Last update: a month ago by now.waterReading time: 4 min

프로그램에서 뭔가가 잘못 될 가능성은 늘 존재한다. 뭔가가 잘못되면 바로 잡을 책임은 바로 우리 프로그래머에게 있다.

깨끗한 코드와 오류 처리는 확실히 연관성이 있다.

오류 처리 코드로 인해 프로그램 논리를 이해하기 어려워진다면 깨끗한 코드라 부르기 어렵다.

오류 코드보다 예외를 사용하라

오류가 발생하면 예외를 던지는 편이 낫다.

오류를 직접 처리하려면 함수를 호출한 즉시 오류를 확인해야 한다. (ex. if-else)

반면 예외를 사용하면 호출자 코드가 더 깔끔해지고, 논리가 오류 처리 코드와 뒤섞이지 않게 되어 각 개념을 독립적으로 살펴보고 이해할 수 있다.


Try-Catch-Finally 문부터 작성하라

try 블록은 트랜잭션과 비슷하다.

강제로 예외를 일으키는 테스트 케이스를 작성한 후 테스트를 통과하게 코드를 작성하는 방법을 권장한다.

그러면 전체 로직을 확인해서 예외 케이스를 확인하게 되는데, 자연스럽게 try 블록의 트랜잭션 범위부터 구현하게 된다. 따라서 범위 내 트랜잭션 본질을 유지하기 쉬워진다.


Unchecked 예외를 사용하라

Checked 예외는 OCP(Open Closed Principle) 를 위반한다.

예를 들어, Checked 예외를 던졌는데 catch 블록이 세 단계 위에 있다면, 그 사이 모든 메서드가 선언부에 해당 예외를 정의해줘야 한다.(throws)

throws 경로에 위치하는 모든 함수가 최하위 함수에서 던지는 예외를 알아야 하므로 캡슐화가 깨진다.

또한 모듈과 관련된 코드가 변경되지 않았더라도 선언부가 바뀌었기 때문에 모듈을 새로 빌드하고 배포해야 한다.

때로는 Checked 예외가 유용한 경우도 있지만, 일반적인 어플리케이션에서는 그로 인해 얻는 이익보다 의존성이라는 비용이 더 크다.


예외에 의미를 제공하라

예외를 던질 때는 전후 상황을 충분히 덧붙여라

오류가 발생한 원인과 위치를 찾기가 더 쉬워진다.


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

애플리케이션에서 오류를 정의할 때 프로그래머에게 가장 중요한 관심사는 오류를 잡아내는 방법이 되어야 한다.

대다수 상황에서 오류를 처리하는 방식은 오류를 일으킨 원인과 무관하게 비교적 일정하다.

-> 오류를 기억하여, 프로그램을 계속 수행해도 좋은지 확인

따라서 예외 클래스를 정의하여 호출하는 라이브러리 API 를 감싸면서 예외 유형을 하나만 반환하도록 작성한다.

장점

  • 외부 라이브러리와 프로그램 사이에서 의존성이 크게 줄어든다.
  • 다른 라이브러리로 갈아타도 비용이 적다.
  • API 를 설계한 방식에 발목 잡히지 않는다.
  • 외부 API 를 호출하지 않고도 테스트 코드로 테스트하기 쉬워진다.

정상 흐름을 정의하라

위의 방법들로도 비즈니스 논리와 오류 처리를 잘 분리한 코드를 만들 수 있다.

하지만 예외가 논리를 따라가기 어려워지는 상황도 존재할 수 있다.

애초에 예외적인 상황을 처리할 필요가 없도록 만들면 그러한 문제를 해결할 수 있다.

이때 특수 사례 패턴 이라 불리는 패턴을 사용할 수 있는데, 클래스를 새로 만들거나 객체를 조작해 특수 사례를 처리하는 패턴이다.

그러면 클래스나 객체가 예외적인 상황을 캡슐화해서 처리할 수 있게 된다.


null 을 반환하지 마라

null 을 반환하는 코드는 일거리를 늘릴 뿐만 아니라, 호출자에게 문제를 떠넘긴다. null 확인을 빼먹는다면 애플리케이션이 통제 불능에 빠지게 된다!

만약 메서드에서 null 을 반환하고픈 유혹이 든다면, 그 대신 예외를 던지거나 특수 사례 객체를 반환하자.

예시

val employees: List<Employee> = getEmployees()
if (employees != null) { ... }

이러한 경우엔 getEmployees 메서드를 수정해 빈 리스트를 반환한다면 if 문이 필요없어 코드가 훨씬 깔끔해진다.


null 을 전달하지 마라

메서드에서 null 을 반환하는 방식보다, 메서드에 null 을 전달하는 방식이 더 나쁘다.

따라서 애초에 null 을 넘기지 못하도록 금지하는 정책이 합리적이다. 그로 인해 부주의한 실수를 저지를 확률을 낮출 수 있다!


결론

깨끗한 코드는 읽기도 좋아야 하지만 안정성도 높아야 한다.

오류 처리를 프로그램 논리와 분리해 독자적인 사안으로 고려하면 튼튼하고 깨끗한 코드를 작성할 수 있다.

또한 독립적인 추론이 가능해지며, 코드 유지보수성도 크게 높아진다.

Previous
6장. 객체와 자료 구조
Next
8장. 경계