[토프링 4장] 예외

jomminii·2022년 10월 16일
1

toby-spring-study

목록 보기
5/5

예외의 종류와 특징

자바에서 throw를 통해 발생시킬 수 있는 예외는 크게 세 가지가 있다.

😵 Error

java.lang.Error 를 상속한 서브클래스들로 시스템 상에서 OutOfMemoryErrorThreadDeath 같은 비정상적인 상황들이 발생했을 때 사용된다. 시스템 상의 문제이기 때문에 시스템 레벨에서 특별한 처리 로직을 작성하는게 아니라면 catch 로 처리를 할 수 있는게 없으므로 처리에 특별히 신경쓰지 않아도 된다.

❎ Exception 과 checked Exception

Exceptionchecked Exceptionunchecked Exception 둘로 나눌 수 있다. java.lang.Exception 클래스를 상속한 서브클래스이면서 RuntimeException의 서브클래스가 아닌 것들을 checked Exception(체크 예외) 라고 부른다.

체크 예외는 일단 throw 로 선언을 하면 무조건 처리를 해줘야한다. 무조건 catch 로 잡든지 throws 로 밖으로 던지든지. 그냥 넘어가게 되면 컴파일 오류를 발생시킨다.

자바 언어와 JDK 초기 설계자들은 발생 가능한 예외에 체크 예외를 모두 적용하려고 했던 것 같은데 IOException 이나 SQLException 같은 것들이 대부분 체크 예외로 만들어져 있다.

커스템 Exception 을 만들 때 예외 대응을 강제하고 싶다면 체크 예외를 사용하면 된다.

하지만 강제한다고 해도 꼼수로 회피할 수는 있다.

// 아무것도 안할거지만 무조건 잡아야하니 무의미한 코드 작성으로 catch
catch (SQLException e) {
	아무것도 안하고 패스
}

or

// 습관적으로 무의미하게 책임을 상위로 전가
public void method() throws Exception {
	SQLException 발생
}

❌ RuntimeException 과 unchecked/runtime Exception

앞에서 살펴본 체크 예외와 반대로 언체크 예외는 예외처리를 할 수 있긴하지만 강제하지는 않는다.

좀 더 자율성이 있기에 커스텀 Exception을 만들 때 더 자주 사용하게 된다.(사실 Exception 을 상속받아서 만든게 아직까지 없다...)

이런 식으로 언체크 예외를 만들어 볼 수 있다. - [Spring] ServiceException 기능 개발

주로 개발자가 의도한 방식대로 예외를 처리하고 싶을 때 상속 받아서 예외를 만들곤 한다.

예외처리 방법

예외 복구

예외 복구는 예외가 발생했을 때 다시 정상 프로세스로 처리되도록 해주는 방법이다.
예를 들어 네트워크 등의 문제로 최초 API 요청이 실패했을 때, 일정 횟수동안 재시도를 하여 정상처리 되도록 유도하는 방식이 있을 수 있고, 파일의 문제 등으로 IOException 이 발생했을 때 다른 파일로 요청하라는 등으로 정상 처리될 수 있게 유도를 하는 방식이 있을 수 있다.

결과적으로 예외가 발생하는 것으로 끝나지 않고 정상궤도로 복구할 수 있게 하는 방식이다.

예외처리 회피

예외가 발생한 곳에서 처리하지 않고 throws 로 해당 메서드를 호출한 곳으로 예외처리의 책임을 넘겨버리는 방식을 회피라고 한다.

회피는 예외를 복구하는 것처럼 회피를 하는 명백한 의도가 있어야 좋은 처리라고 할 수 있다. 그냥 처리하기 귀찮다고 습관적으로 예외처리 책임을 밖으로 넘겨버리면 안된다.

예외가 발생한 곳은 예외를 처리할만한 컨텍스트가 아니고, 메서드를 호출한 곳이 예외 처리를 하기 적합한 컨텍스트라고 판단되면 예외처리 회피로 진행하면 된다.

예외 전환

예외 전환은 예외 회피와 비슷하게 예외 복구를 하지 못하는 경우 예외를 바깥으로 던지는데, 그냥 던지는게 아니라 의도적으로 다른 예외로 바꿔서 넘기는 걸 말한다.

예를 들어 SQLException이라는 퉁쳐진 DB 예외가 발생하게 되면 이 예외를 받는 입장에서는 어떤게 원인이 되서 이 예외가 발생했는지 알기 어렵다.

때문에 이 예외가 발생한 의미를 담은 DuplicatedIdException 등의 다른 예외를 만들고 이로 전환시켜서 넘겨주면 받는 입장에서 보다 명확하게 이해하고 처리할 수 있게 된다.

또 다른 사용 예로는 시스템에서 체크 예외로 던지고 있는 예외가 비즈니스 로직 상에서 의미가 있다거나 복구를 할 수 없는 예외라면 의도적으로 언체크 예외로 전환해서 예외를 전달해도 된다.

이렇게 하면 쓸데없이 catch 를 만들어서 처리하는 의미 없는 코드를 줄일 수 있게 된다.

의도적 이라는게 중요하다.

예외 처리는 어떤 전략을 가지고 해야하나

일단 예외는 모두 복구를 필요로 하는 상황이라고 볼 수 있다.
이 복구를 하도록 어떻게 강제하느냐가 예외 처리 전략이라고 할 수 있는데, 체크 예외는 이 복구를 강제 시키는 거고 언체크 예외는 복구를 강제 시키지 않는 것으로 보인다.

그런데 이 강제라는게 정말로 예외를 복구 시키는 작업을 강제하는건 아니고 일단 처리하는 시늉이라도 해! 라는 느낌이라서 위에서 봤듯 catch 만 하고 아무처리를 안할 수도 있고, 의미 없이 밖으로 에러를 전가시켜버려서 의미를 알 수 없는 코드를 만들어버릴 수도 있다.

그렇기에 시스템 적으로 무조건 처리해야하는 예외가 아니라면 새로 예외를 만들 때는 RuntimeException 을 상속받은 언체크 예외를 사용하는게 좋아보인다.

이는 이 예외를 만들고 사용하는 개발자가 충분히 의도를 갖고 언체크 예외를 사용한다는 가정을 둔다.

언체크 예외를 사용하여 전달하고자 하는 의미를 클라이언트에 전달하고(예외 전환) 클라이언트가 적절한 조치를 취할 수 있게 해준다.

여기서 클라이언트는 해당 메서드나 작업을 호출한 상위 메서드일수도 있고, 검색 버튼을 클릭한 실제 사용자 일 수도 있다.

상위 메서드가 재시도를 통해 예외를 복구하게 할 수도 있고, 실제 사용자가 특정 입력 값을 다시 작성해서 호출하라는 안내를 제시해서 잘못된 입력에 대한 예외를 복구할 수도 있다.

결과적으로 체크 예외를 사용하든 언체크 예외를 사용하든 개발자가 충분한 의도를 갖고 예외를 처리하고자 하는 로직을 작성하면 된다.

말은 쉽지만 많은 코드들을 작성해가며 체득해가는걸로...

profile
고민은 격렬하게, 행동은 단순하게

0개의 댓글