코틀린에서의 Checked Exception 그리고 나의 의문

모지리 개발자·2022년 11월 17일
1

Kotlin

목록 보기
3/5

Intro

이 글은 자바와 코틀린을 비교하면서 코틀린을 공부하던 중 Checked Exception에 대하여 자바와 코틀린의 처리 방식이 다르다는 것을 알게되었고 코틀린의 Checked Exception의 처리 방식에 대한 의문이 생겨 이 글을 작성하게 되었습니다.

자바에서의 예외

우선 저의 의문을 작성하기에 앞서 예외에 관련된 선수지식이 필요하기 때문에 간단히 작성하고 넘어가겠습니다.

각 계층에 대해 간단하게 작성해보겠습니다.

Throwable : 예외 계층의 최상위 클래스
Error : 시스템에 비정상적인 상황 발생, 예측이 어렵고 기본적으로 복구 불가능
Exception : 시스템에서 포착 가능하여(try-catch)복구 가능, 예외 처리 강제
RuntimeException: 런타임시에 발생하는 예외, 예외처리 강제하지 않음

Checked Exception vs Unchecked Exception

Checked Exception

  • 반드시 예외처리해야함
  • 컴파일 단계에서 확인 가능
  • 예외발생시 트랜잭션 roll-back 하지 않음
  • Exception의 상속받는 하위 클래스 중 Runtime Exception을 제외한 모든 예외
    - IOException
    - SQLException

Unchecked Exception

  • 명시적인 처리를 강제하지 않음
  • 실행단계(Runtime) 때 확인 가능
  • 예외 발생 시 트랜잭션 roll-back 함
  • RuntimeException 하위 예외
    - NullPointerException
    - IllegalArgumentException
    - IndexOutOfBoundException
    - SystemException

나의 의문

제 의문은 자바와 코틀린에서의 Checked Exception의 처리에 대해 공부하면서 발생하였습니다. 글로 작성하는 것보다 코드로 이해하는 것이 편할 것 같아서 코드로 보여드리겠습니다. 코드는 패스트캠퍼스의 Kotlin & Spring: 리팩토링부터 서비스구현까지의 강의를 참고하였습니다.

java

위에서 보이는 것과 같이 자바에서의 Checked Exception은 컴파일 에러가 발생하기 때문에 무조건 try-catch로 감싸거나 throws로 예외를 전파해야합니다.

위에서 작성한 코드는 아래와 같이 해결할 수 있을 것입니다.

하지만 대부분의 개발자들은 자바에서 Checked Exception을 처리할 때 의미 없는 처리를 반복하게되고 Checked Exception이 발생할 경우 catch안에서 에러를 해결하는 일은 생각보다 흔하지 않고 오히려 생산성을 감소시킨다고 합니다.

예시는 아래와 같습니다.

java

try {
  log.append(message)
} catch(IOException e) {
  // Do nothing
  }
  
try {
  File file = FileUtils.get(filename);
  // ...
} catch (FileNotFoundException e) {
 // 파일이 없는데 어떤 처리를 하지?
}

try {
  return objectMapper.readValue(json, clazz);
} catch (IOException e) {
  // 단순 에러 로그 출력
  logger.error(e.getMessage(), e);
}

하지만 아래와 같이 코틀린은 Checked Exception을 강제하지 않습니다.

또 원하는 경우에만 try-catch를 쓸 수 있습니다.

아마 제가 추측하기로는 코틀린은 위에서 언급했던 자바의 단점을 보완하기 위해 Checked Exception을 강제하지 않고, 원하는 경우에만 try-catch를 쓸 수 있게 만들었다고 느꼈습니다.

제 의문은 여기서 발생했습니다.

이 코드에서 무슨 에러가 발생할 수 있다라는 것을 컴파일 시점에 알 수 있는 건 굉장히 좋은 기능 아닌가..? 이게 없어진거잖아?

의문의 구체적 예시

위에서 작성한 코드를 바탕으로 예시를 작성해보겠습니다.

Java 에서의 상황

  1. Thread.sleep(1) 이라는 코드를 작성할 상황이 생겼습니다.
  2. 아래와 같이 코드를 작성합니다.
  3. 컴파일 에러가 나는 것을 확인하였고 에러의 원인을 살펴보니 Thread.sleep(1)은 예외가 발생할 수 있다는 것을 알게 되었습니다.
  4. 저는 아래와 같이 코드를 변경하여 예외를 처리합니다.

Kotlin 에서의 상황

  1. Thread.sleep(1) 이라는 코드를 작성할 상황이 생겼습니다.
  2. 아래와 같이 코드를 작성합니다.
  3. 문제가 발생할 수 있다는 것을 인지하지 못한체 넘어갑니다.

해결방안

우선 당장 생각나는 것은 테스트가 필수적이라는 생각입니다.

원하는 것만 예외처리 할 수 있다는 것은 생산성에 도움이 되고 의미없는 처리를 줄여준다는 부분에서 굉장히 큰 장점이라고 생각하지만 위에서 언급했던 자바와 같이

"이 코드에서는 이런이런 에러가 발생할 수도 있어"

라는 인지가 되어있는 상태로 코드를 작성했던 것도 큰 장점이라고 생각했었습니다.
하지만 코틀린을 통해 코드를 작성하게 되면 컴파일 단계에서 에러의 발생가능성을 미리 알 수 없기 때문에 다양한 상황을 바탕으로 테스트를 해봐야한다고 느껴졌습니다.

결론

다른 분들에게도 이와 같은 문제에 대해서 어떻게 생각하는지 여쭤보기도 하고 제가 잘못생각했던 부분이 있다면 추가로 작성하도록 하겠습니다.

2022.11.30 일 내용추가

토스 개발자님의 답변

요약하자면
1. checked exception을 활용하여 올바른 에러 핸들링하는 경우가 드뭅니다.
2. 제 생각에도 공감을 해주셨습니다.
3. 모든 것엔 trade off가 있고 checked exception이 큰 역할을 하지 못해서 unchecked exception방향으로 가고 있다고 이해하는게 좋습니다.

제가 잘못이해하고 있거나 잘못 작성한 부분이 있다면 지적, 비판, 피드백 뭐든 해주시면 감사하겠습니다!

profile
항상 부족하다 생각하며 발전하겠습니다.

0개의 댓글