SSAFY11th-book-study / book-study

SSAFY 11기 6반의 '토비의 스프링 스터디'
0 stars 0 forks source link

[4.1.3] 예외 처리 방법 - 예외 전환 #36

Open a-young-kim opened 7 months ago

a-young-kim commented 7 months ago

p. 291 예외 전환의 방법 중 두번째 전환 방법은 중첩 예외를 이용해 새로운 예외를 만들고 원인이 되는 예외를 내부에 담아서 던지는 방법입니다. 주로 예외 처리를 강제하는 체크 예외를 언체크 예외인 런타임 예외로 바꾸는 경우에 사용한다고 합니다. 체크 예외는 비지니스 로직을 볼 때 의미 있는 예외이거나 복구 가능한 예외 가 아니라서 런타인 예외로 포장해서 던지는 것이 낫다라고 하는데 이 부분이 잘 이해가 가지 않아 질문드립니다. 체크 예외를 런타임으로 바꾸면 자동으로 시스템에서 트랜젝션을 롤백해 주고 해당 예외로 인해 다른 문제가 발생하지 않으며 이 후 사용자 혹은 관리자에게 자세한 정보를 줄 수 있다는 장점이 있다고 합니다. 그렇다면 체크 예외로 throws를 할 경우 자동으로 시스템에서 트랜젝션을 롤백을 하지 않기 때문에 프로그램이 다운되는 것인지 궁금합니다. 또한, 일반적으로 체크 예외를 계속 throws 해서 넘기는 것은 무의미하다고 하는데, 체크예외는 런타임 예외와 달리 왜 계속 throws 해서 넘기는지 궁금합니다.

질문이 약간 횡설수설한 것 같아서 세줄요약 했습니다.

  1. 체크 예외를 런타임 예외로 바꿀 때 자동으로 트랜잭션이 롤백되고 다른 문제가 발생하지 않는다고 하였는데 이 문장이 잘 이해가 되지 않습니다.
  2. 체크 예외랑 달리 런타임 예외는 왜 계속 throws로 넘기지 않아도 문제가 발생하지 않는지 궁금합니다.
limjongheok commented 7 months ago

우선 checked 예외는 강제 , unChekced 예외는 강제x 라는 차이점이 있다고 알고 있습니다. 즉 강제라는 것은 checked 예외는 try catch 가 필요하다는 것입니다. 이를 책에서는 checked 는 복구 가능 unChecked 는 복구 불가능이라 설명 하엿습니다. 즉 checked 는 복구 가능 하기 때문에 예외가 터진 메서드를 호출한 가장 위칸에서 catch를 통해 복구를 해야하는거 아닌가 생각합니다. 그리고 unChecked 예외는 강제하지 않고 복구 불가능 하기 때문에 throw runtimeException 을 하여 에러난 부분에서 그대로 스레드를 종료해 주는것이 아닌가 생각합니다.

gmelon commented 7 months ago

맞는지는 모르겠지만 이해한 내용을 정리해보겠습니다.

checked vs unchecked

먼저 책 p.287에 보면, RuntimeException (unchecked) 는 주로 개발자의 실수로 인해 발생하는 프로그램 예외이며 개발 시 조건 등을 주의 깊게 살핀다면 피할 수 있지만 (부주의로) 피하지 않았을 때 발생시키는 예외라고 합니다. 그리고 따라서 이는 예상하지 못한 예외 상황에서 발생하는 예외가 아니기 때문에 (충분히 예상가능한 예외) catch나 throw를 통해 잡지 않도록 설계되었다고 합니다.

그렇다면, 반대로 checked 예외의 경우 개발자가 충분히 조건을 고려하고 조심해도 피할 수 없는 정말 예외적인 상황에서 발생하는 예외라는 소리가 됩니다. (사용자가 주소를 잘못 입력하거나, DB 커넥션에 문제가 생기거나, 등등) 그래서 정말 예외적인 상황이 생길 수 있으므로 개발자에게 catch, throw 등을 통한 예외 처리를 강제하는 것이라고 생각합니다.

복구 가능?

그렇다면, 여기서 잘 이해되지 않는 부분은 checked 예외가 '복구 가능' 하다는 것입니다. 이것이 의문인 이유는 아래와 같이 2가지 입니다.

  1. unchecked도 (index가 터지면 조정하는 등 방식으로) 코드 레벨에서 복구를 시도해볼 수 있지 않나?
  2. checked 예외는 예상하지 못한 예외 상황에서 발생하는데 그걸 복구할 수 있다고..?

먼저 2번에 대한 해답은 4.2절인가 에서 언급되었듯이 과거 swing 등을 통한 싱글 쓰레드 앱의 경우 사용자와 앱이 직접 상호작용하기 때문에 사용자가 주소를 잘못 입력했다고 해서 (예외 상황) 프로그램이 종료되어서는 안 되기 때문에 주소를 다시 입력하라고 catch 문등을 통해 다시 입력 시도하도록 예외를 복구해볼 수 있었다고 합니다. 하지만 이제는 멀티쓰레드의 웹 앱 환경이 주로 사용되므로 예외 상황이 발생했다고 해도 복구를 동일 쓰레드에서 수행할 필요가 없으며, 사실 그렇게 (쓰레드를 유지하며 복구) 하기가 오히려 더 어렵다고 생각합니다.

그래서 'checked 예외는 복구 가능하다' 라는 이야기는 과거에는 맞는 말이고 의미가 있었지만 최근에는 의미가 없다고 생각합니다. 최근 개발되는 API들은 모두 런타임 예외를 던진다고 하는 이야기도 이런 맥락에서 이해해보면 납득이 되는 것 같습니다.

1번 의문의 경우, 잘 모르겠습니다 같이 얘기해봐요오..

롤백과의 관계

스프링 @Transactional 의 기본 설정이 checked의 경우 롤백하지 않고, unchecked의 경우 롤백하도록 되어있다고 합니다. checked와 unchecked의 차이점을 고려해서 이유를 생각해보면, unchecked는 개발자에게 예외 처리를 강제하지 않기 때문에 개발자가 예상하지 못한 예외가 발생하여 쓰레드가 비정상 종료될 수 있습니다. 따라서, 스프링은 이를 개발자가 놓친 예외 처리라고 생각하여 롤백 처리를 해주는 것이라고 생각합니다. 반대로 checked는 개발자에게 예외 처리를 강제하므로 개발자가 이를 충분히 고려했을 것이라고 판단하고 예외가 루트까지 전달되어도 롤백을 기본으로 수행하지 않는게 아닐까? 하고 생각했습니다.

추가로, 이 설정들(checked, unchecked 여부에 따른 롤백 여부)은 모두 커스텀할 수 있다고 합니다.

throws 필요 여부

checked 여부에 관계없이 throws 명시가 가능하긴 하지만, checked인 경우에만 강제됩니다. 그 이유로는 위의 내용과 연관이 있다고 생각됩니다. unchecked는 에상하지 못한 예외가 아니지만, checked는 예상하지 못한 예외이기 때문에 개발자에게 예외 처리의 필요성을 인지시키기 위해 강제된다고 생각합니다.

a-young-kim commented 6 months ago

checked 예외는 예상치 못한 상황에서 발생하는 예외입니다. (사용자가 주소를 잘 못 입력하거나, DB에 문제 발생 등) unchecked 예외는 예상치 못한 상황에서 발생하는 예외가 아닙니다. (즉 개발자가 코드를 통해 수정할 수 있는 예외)

  1. checked 예외는 개발자 입장에서가 아닌 사용자 측면에서 혹은 다른 측면에서 복구할 수 있기 때문에 복구 가능한 예외입니다. 따라서 스레드를 종료해 주는 것이 아닌 다른 측면에서 발생한 예외를 복구 시키기를 기다립니다. 하지만 unchecked 예외는 개발자 측으로 인해 발생할 오류이므로 코드 자체를 수정해 주어야 합니다. 따라서 예외를 처리하는 것이 아니라 코드를 수정을 통해 복구를 해야하기 때문에 throws로 넘기지 않아도 오류가 발생하지 않습니다.