자바의 예외
자바에는 에러(Error)와 예외(Exception)가 있다. 에러는 하드웨어의 오동작 또는 고장으로 인해 응용프로그램 실행 오류가 발생하는 것이고 개발자는 에러에 대처할 방법이 전혀 없다. 반면에 예외는 사용자의 잘못된 조작 또는 잘못된 코딩으로 발생하는 프로그램 오류이다. 예외가 발생하여 적절히 대처하지 않으면 프로그램이 비정상적으로 종료된다는 점은 둘이 비슷하다. 그러나 예외는 개발자가 예외 처리(Exception Handling)를 통해 대처할 수 있다.
예외에는 크게 일반 예외(Exception)와 실행 예외(Runtime Exception)가 있다. 일반 예외는 컴파일 과정에서 예외 처리가 필요한지 확인하고 예외 처리 코드가 있는지 확인한다. 만약 예외 처리 코드가 없다면 컴파일 오류가 발생한다. 실행 예외는 컴파일하는 과정에서 확인할 수 없는 예외이다. 즉 개발자가 경험에 의존하여 미리 예상하고 처리해야 하는 예외를 말한다.
만약 서비스 중에 원인을 알 수 없는 실행 예외가 발생한다면, 심지어 본인이 담당자라면 식은땀이 많이 날 것이다. 서비스의 매출과도 직결되는 문제라면 책임을 피할 수 없을 것이다.
자바는 이런 예외들을 클래스로 관리한다. JVM은 프로그램을 실행하는 도중에 예외가 발생하면 해당 예외 클래스로 객체를 생성한다. 그러고 나서 예외 처리 코드에서 예외 객체를 이용할 수 있도록 해준다. 자바의 예외 클래스들의 상속 구조는 다음과 같다.
일반 예외와 실행 예외의 차이점은 일반 예외는 Exception을 상속받지만 실행 예외는 RuntimeException을 상속받는다. JVM은 RuntimeException을 상속했는지 여부를 보고 실행 예외를 판단한다. 대표적인 실행 예외들은
NullPointerException, ArrayIndexOutOfBoundsException, NumberFormatException, ClassCastException, ...
등이 있다. 원래라면 실행 예외이기 때문에 프로그램을 실행해야 알 수 있는 에러였지만 요즘은 IDE의 성능이 워낙 뛰어나기 때문에 미리 개발자에게 경고해주기도 한다.
예외 처리
자바에서 예외 처리 방법은 다음과 같이 여러 방법이 있다. 하나씩 살펴보자.
try - catch - finally
간단하게 TCP 연결을 맺는 코드를 작성해보았다. 그런데 TCP Socket을 연결할 때 발생할 수 있는 에러가 있다. 그리고 해당 에러를 아래처럼 처리해주지 않으면 IDE가 경고 메시지를 보여준다.
그래서 UnknownHostException을 catch 하는 구문을 추가해줬다.
그래도 경고 메시지가 발생한다. 왜냐하면 IOException도 처리해줘야 하기 때문이다. 그래서 IOException을 처리하는 구문도 추가해줬다. 그리고 아래와 같은 구조를 다중 catch라고 부른다. 근데 또 IDE가 계속 머라그런다.
왜냐하면 UnknownHostException은 IOException을 상속하기 때문에 UnknownHostException이 발생하더라도 catch (IOException e) 구문에서 모두 처리해버린다. 즉 예외를 세밀하게 살펴보고 싶다면 아래와 같이 catch 구문 순서에 신경을 써줘야 한다.
이 두 예외가 서로 관련 없는 클래스일 때 한 번에 catch 할 수 없을까? 아래와 같이 멀티 catch를 사용하면 가능하다.
빨간 줄은 UnknownHostException과 IOException이 서로 상속 관계에 있는 클래스이기 때문에 이상하게 코딩하지 말라고 경고하는 것뿐이다. 그럼 모든 Exception을 한 번에 처리하고 싶다면 어떻게 해야 할까? 아래와 같이 Exception을 catch 해버리면 된다. 왜냐하면 모든 예외는 Exception 클래스를 상속하고 있기 때문이다.
try - with - resources
자바 7에서 새로 추가된 try - with - resources는 기존 방식을 개선한 기법이다. 기존의 try - catch - finally의 finally 구문에서 개발자가 직접 자원을 해제해줬다면 try - with - resources는 자동으로 자원을 해제해준다. 자원을 해제한다는 것은 자원의 close() 메서드를 호출한다는 의미인데, 이 close() 메서드는 java.lang.AutoCloseable 인터페이스를 구현하면 된다. 이 내용은 예외 처리와 직접적인 관련은 없기 때문에 여기까지만 이야기하고 넘어가도록 하겠다.
예외 떠넘기기 혹은 예외 발생시키기
try - catch 블록으로 예외를 직접 처리하는 것과 달리 메서드를 호출한 곳으로 예외를 떠넘기거나 일부러 발생시켜서 넘기는 것이다. 즉 예외 처리 역할을 위임한다고 보면 된다. 이러한 방식은 꽤 유용할 수 있는데, 예를 들어 컨트롤러가 다양한 비즈니스 로직을 호출한다면, 비즈니스 로직에서 발생하는 예외들을 한 곳에서 예외 처리 로직으로 다룰 수 있다는 장점이 있다. 즉 예외 처리에서 중복되는 코드가 줄어들고 예외 처리에 대한 유지 보수성이 증가할 수 있다.
물론 상황에 따라서 try - catch - finally 또는 try - with - resources가 필요한 경우도 있다. 예를 들어 자원이 할당되고 예외가 발생했을 때 즉각 자원 해제가 이루어져야 한다면 다른 곳으로 예외를 떠넘기는 행위는 바람직하지 않을 수 있다. 반면에 사용자의 잘못된 조작으로 발생하는 크리티컬 하지 않은 실행 예외들은 한 곳에서 일괄 처리해도 괜찮기 때문에 예외 떠넘기기 방식을 이용하는 것도 좋은 방법이다.
'JAVA' 카테고리의 다른 글
[기본 시리즈] JAVA 특징 및 JVM에 대하여 (0) | 2022.08.17 |
---|---|
[컬렉션 프레임워크 끝내기] #List (0) | 2022.07.19 |
어노테이션에 대하여 (0) | 2022.07.16 |
프로젝트 JAVA 컨벤션 (0) | 2022.05.08 |
SOLID 원칙 (0) | 2022.03.13 |