9주 차 시작!

 

자바에서 예외 처리 방법 (try, catch, throw, throws, finally)

try Statements

try {
        // Do SomeThing..
}

예외가 발생할 수 있는 로직을 실행할 때 예외가 발생하는 것을 잡아내기 위해 사용하는 Statements이다. 주로 JDBC Connection이나 File Reader, API에 대한 URL Connection 등을 처리하는 로직을 감싸는 데 사용한다.

 

Ex) JDBC

try {
        connection = dataSource.getConnection(); 

        //SQLException 이 발생 가능하다.
    ResultSet resultSet = connection.prepareStatement("SELECT * FROM USER")
                        .executeQuery();
} // catch Statements

 

catch Statements

try {
        // Do SomeThing
} catch (Exception exception) {
        // Stack Trace, Ignore 등
}

Try Statements의 내부에서 발생한 Exception을 잡아 사전 정의된 동작을 하는 Statements 이다.

 

Ex) JDBC

try {
        connection = dataSource.getConnection(); 

    ResultSet resultSet = connection.prepareStatement("SELECT * FROM USER")
                        .executeQuery();
} catch (SQLException exception){

        // 해당 예외가 발생한 시점까지의의 메서드 정보를 호출한다.
        exception.stackTrace();
}

 

Multiple catch

Try Statements에서 발생한 2개 이상의 Exception에 대한 Catch Statements를 처리하는 것을 말한다.

 

JDK 7 이전

try {
        // Do Something..
} catch (IOException exception) {
        exception.stackTrace();
} catch (FileNotFoundException exception) {
        exception.stackTrace();
}

발생하는 Exception들에 대해 동일하게 stackTrace()를 호출하는 것을 볼 수 있다. 모든 Exception에 대해 같은 처리를 한다고 생각하면 같은 로직이 중복되는 것을 알 수 있다.

 

JDK 7 이후 버전부터는 해당 부분에 대해 개선이 이루어짐으로써

try {
        // Do Something..
} catch (IOException exception | FileNotFoundException exception) {
        exception.stackTrace();
} 

이런 식으로 처리할 수 있다.

 

throw

메서드 내에서 사용 가능하며, 로직을 진행하면서 조건에 따라 명시적으로 예외를 던질 때 사용하는 키워드이다. Check Exception은 해당 키워드를 통해 전파시킬 수 없으며 인스턴스만 전달 가능하고 한 번에 여러 예외를 전달할 수 있게끔 작성할 수 없다.

 

Spring에서는 ExceptionHanler를 이용하여 원하는 처리를 진행할 수 있다.

public void someMethod() {
        // Do Something..
        if(a < 1) {
                throw new RuntimeException();
        }
        // Do Something
}

// 당연히 Try-catch 문도 이용 가능하다.
try {
            throw new RuntimeException("hello");
} catch (RuntimeException exception){
            exception.printStackTrace();
}

 

throws

메서드 시그니쳐에 사용 가능하며, 해당 메서드를 호출하는 클라이언트에게 예외를 던진다.

해당 키워드는 Check Exception도 전달할 수 있으며, 메서드 뒤에 예외 클래스 형태로 정의된다.

쉼표를 이용하여 여러 Exception을 던질 수 있음을 선언할 수 있다.

void userDaoTest_selectOnes() throws SQLException {
        ResultSet resultSet = connection.prepareStatement("SELECT * FROM USER WHERE ID = 3")
                        .executeQuery();
}

void userDaoTest_selectOnes() throws IOException, FileNotFoundException{
        // Do Someting..
}

 

finally

try문, try-catch 문의 과정, 결과와 상관없이 마지막에 꼭 실행하여야 하는 로직을 정의하는 문법, 주로 사용된 Resource에 대해 반납하는 코드를 작성한다.

} finally {            
        // 리소스 반납 코드
        try {
                if (ps != null) {
                        ps.close();
                }
                if (con != null) {
                        con.close();
                }
        } catch (SQLException exception) {
                exception.printStackTrace();
        }
}

 

try-catch-finally

위에서 언급했던 try, catch, finally 문법들을 같이 사용하는 것을 의미한다.

Connection con = null;
PreparedStatement ps = null;

try {
        con = dataSource.getConnection();
        ps = con.prepareStatement("SQL");
        ps.executeUpdate();

} catch (SQLException exception) {
        exception.printStackTrace();

} finally {            
        // 리소스 반납 코드
        try {
                if (ps != null) {
                        ps.close();
                }
                if (con != null) {
                    con.close();
                }
        } catch (SQLException exception) {
                exception.printStackTrace();
        }
}

해당 로직의 경우 하는 일이 많이 없기에 가독성의 대한 큰 문제를 느끼지 못하지만, 복잡한 흐름을 가지기에 로직이 복잡해질수록 가독성이 떨어지게 된다. 추가적으로 반환되어야 할 자원이 많을수록 개발자가 어떠한 자원에 대하여 정리하는 로직을 작성하지 못해 문제를 일으킬 수 있다.

 

이를 해결하기 위해 JDK 7부터는 AutoCloseable 인터페이스를 구현한 클래스들에 대해 자동적으로 자원을 회수할 수 있는 문법을 제공한다. 이를 try-with-resources 문법이라고 한다.

 

try-with-resources

해당 방식은 여러 로직을 세미콜론으로 구분하여 선언하게 된다. 해당 방식은 로직이 진행되고, 내부적으로 close() 호출하여 모든 리소스를 반납하게 된다.

try (Connection con = dataSource.getConnection();
         PreparedStatement ps = con.prepareStatement("SELECT * FROM USER")){

        ResultSet resultSet = ps1.executeQuery();

} catch (SQLException exception) {
        exception.printStackTrace();
}

 

자바가 제공하는 예외 계층 구조

 

출처 : https://coderanch.com/t/627585/certification/Error-unchecked-exception

 

Throwable

Java에 존재하는 모든 예외와 오류에 대한 상위 클래스이다. 해당 클래스를 상속 받음으로써 thorw 문이나 catch 문 등에 사용할 수 있는 파라미터가 될 수 있으며, 추가적으로 JVM에 의해 throw 될 수 있다.

 

주로 사용되는 getLocalizedMessage, printStackTrace, initCause, toString 등의 메서드들을 구현한다.

 

Exception

프로그램 실행 중 개발자가 구현한 로직에서 던져진 문제에 대한 정보를 담는 클래스를 말하며, 자바에서의 Exception은 컴파일 시점에서 컴파일러에게 발견되는 확인된 예외이다.

 

Exception와 해당 클래스를 상속받는 Sub Class들(Runtime Exception 제외)은 try, catch Statements로 감싸거나 throw를 작성하는 등 명시적인 예외 처리를 하여야 한다.

 

Error

프로그램 실행 중 시스템에서 발생한 문제에 대한 정보를 담는 클래스를 말하며, JVM에 의해 던져지는 것이다. Application 로직에서 잡을 수 없는 대처 불가능한 것이기에 프로그램 로직을 구현할 때 상정하지 않아도 된다.

(심각한 수준의 상황을 의미한다.)

 

Exception과 Error의 차이는 JVM, WAS와 같은 System Level의 문제와 Application Level의 문제의 차이이다.

 

RuntimeException

Exception을 상속받는 클래스이며, 컴파일 시점에서 식별되지 않는 (Unchecked) Exception들의 집합이다. 주로 개발자의 실수로 인해 발생하는 예외이다. (Array Index를 넘어간다던지, 0으로 나누는 등)

 

RuntimeException과 RE가 아닌 것의 차이는?

 

Exception과 (Runtime Exception을 제외한) Sub Class들 (Checked Exceptions)

  • 컴파일 시점에서 compiler에서 확인할 수 있는 예외이다.
  • 프로그램 실행, 구현의 흐름상 예외의 발생 가능성이 있다면 명시적인 예외처리를 작성하게 강제한다.
  • IOException, ServletException, SQLException 등이 있다.

 

RuntimeException과 Sub Class들 그리고 Error (UnChecked Exceptions)

  • 런타임 시점에서 발생하는 Exception, Error들을 말한다. Checked Exception보다 좀 더 구체적인 뜻을 가지고 있다.
  • 개발자 부주의로 인한 문제에서 발생하게끔 의도된 것들이거나(null 체크, 형 변환, 메서드 호출) 시스템에서 발생하는 것들을 말한다.

NullPointerException, IllegalArgumentException, SecurityException. IndexOutOfBoundsException

ClassCastException 등이 있다.

 

커스텀한 예외 만드는 방법

Custom Exception을 만들 때에는 해당 예외의 특징을 고려하여 extend를 통해 Exception이나 RuntimeException을 상속받아 작성한다.

 

개인적으로 해당 Exception에 대해 좀 더 명확한 정보 전달을 위해 유사한 표준 에러를 상속받아 좀 더 구체적인 Custom Exception 들을 작성하고 있다.

// Custom Runtime Exception
public class NoSuchPostException extends NoSuchElementException {

    // Do SomeThing..

    public NoSuchPostException(String errorMessage) {
        super(errorMessage);
    }

}

 

커스텀 예외를 사용한다는 것은 표준 예외에 비해 좀더 명확한 정보를 전달할 수 있음을 의미하지만, 오용하게 될 경우 지나치게 많은 클래스가 만들어짐으로써 메모리의 문제( Metaspace는 상관없으며, Perm gen을 사용하는 경우)와 클래스 로딩 문제가 발생할 수 있다.

 

그렇기에 그저 이름을 바꿔 구현하는 것보다는 범용 에러를 활용하면서 메시지를 잘 작성하고 꼭 필요한 시점에만 명확한 정보를 추가적으로 제공함으로써 디버그를 진행함에 있어서 도움이 되게끔 잘 활용하여야 한다.

'Live Study' 카테고리의 다른 글

Live Study_Week 12. Annotation  (0) 2021.02.02
Live Study_Week 11. Enum  (0) 2021.01.28
Live Study_Week 08. 인터페이스  (0) 2021.01.05
Live Study_Week 07. 패키지  (0) 2020.12.28
Live Study_Week 06. 상속  (0) 2020.12.21

+ Recent posts