티스토리 뷰

예외

모든 예외는 적절하게 복구되든지 아니면 작업을 중단시키고 운영자 또는 개발자에게 분명하게 통보되는 것이 예외 처리의 핵심 원칙이다.

잘못된 예외처리

예외를 처리하는 가장 기본적인 방법은 try-catch 구문을 사용하는 것이다. 하지만 catch 구문에서 아무 작업도 하지 않는 코드들이 발견되는데 이러한 코드는 잘못된 코드이다.

try {
    ...
} catch (Exception e) {
    // 아무것도 하지 않음
}

예외를 제대로 처리하지 않아 메모리나 리소스가 소진되거나 결과적으로 오작동으로인한 시스템 오류가 발생할 수 있다. 아래 두 가지 경우도 마찬가지로 좋지 않은 예외 처리이다. 단순히 예외 메시지를 처리하는 것은 올바른 예외처리 방법이 아니다.

try {
    ...
} catch (Exception e) {
    System.out.println(e);
}


try {
    ...
} catch (Exception e) {
    e.printStackTrace();
}

예외를 잡아먹는 위 경우와 다르게 무책임하게 throws를 날리는 방법도 잘못된 예외 처리 방식이다.

public void method1() throws Exception {
    method2();
}

public void method2() throws Exception {
    method3();
}

public void method3() throws Exception {
    ...
}

예외의 종류와 특징

Error

java.lang.Error 클래스의 서브 클래스들이다. 에러는 시스템에 비정상적인 상황이 발생했을 경우에 사용된다. OutOfMemoryError와 같은 것들인데, 이 같은 에러는 개발자가 처리할 수 없기 때문에 신경쓰지 않아도 된다.

Exception

java.lang.Exception 클래스와 그 서브 클래스로 정의되는 예외들은 에러와 달리 개발자들이 만든 애플리케이션 코드의 작업 중에 예외 상황이 발생했을 경우에 사용된다.

예외는 다시 체크 예외와 언체크 예외로 나뉜다. 전자는 RuntimeException 클래스를 상속하지 않은 것들이고, 후자는 상속한 클래스들을 말한다.

체크 예외는 반드시 try-catchthrows를 사용하여 예외를 처리해야하는 반면에 RuntimeException이나 NullPointerException, IlligalArgumentException 들과 같이 예외처리를 강제하지 않는 것들을 언체크 예외라고한다.

언체크 예외는 개발자의 부주의로 발생하고 코드 작성에 따라서 충분이 피해갈 수 있는 예외이기 때문에 예외 처리를 강제하지 않는다.

예외 처리 방법

예외 복구

예외 상황을 파악하고 문제를 해결해서 정상 상태로 돌려 놓는 것을 말한다. 예를 들어, 어떠한 예외로 인하여 기본적인 작업 흐름이 불가능 해질 때 다른 작업 흐름으로 자연스럽게 유도해주는 것이다. 이런 경우 에외 상황은 다시 정상으로 돌아오고 예외를 복구했다고 할 수 있다.

체크 예외들은 예외 처리를 강제하기 때문에 예외를 어떤 식으로든 복구할 가능성이 있는 경우에 사용한다. 통제 불가능한 예외가 발생했을 경우 재시도할 수 있는 기회를 줄 수 있다. 단, 재시회 횟수를 제한해야할 것이다.

예외처리 회피

예외처리를 자신이 담당하지 않고 throws를 사용하여 자신을 호출한 쪽으로 던져버리는 방법이다. 예외를 회피하는 방법은 예외 복구 처럼 의도가 분명해야한다. 긴밀한 관계가 있는 오브젝트에 예외처리 책임을 위임하거나, 자신을 사용하는 쪽에서 예외를 다루는 게 최선의 방법이라는 것에 확신이 있어야한다.

예외 전환

예외 상태를 복구하여 정상적인 상태로 되돌릴 수 없어 예외를 메소드 밖으로 던지는 것이다. 하지만 회피와는 다르게 발생한 예외를 그대로 던지는 것이 아니라 적절한 예외로 전환하여 던진다는 특징이 있다.

내부에서 발생한 예외를 그대로 던지는 것이 그 예외 상황에 대한 적절한 의미를 부여하지 못하는 경우에 의미를 분명하게 해줄 수 있는 예외로 전환한다. 그리고 예외를 처리하기 쉽고 단순하게 만들기 위해 포장할 수 있다.

예외 처리 전략

자바 엔터프라이즈 서버환경에서는 수많은 사용자가 동시에 요청을 보내고 각 요청이 독립적인 작업으로 취급된다. 하나의 요청을 처리하는 중에 예외가 발생하면 해당 작업만 중단 시킬 수 있다.

런타임 예외

애플리케이션 차원에서 예외상황을 미리 파악하고, 예외가 발생하지 않도록 차단하는 것이 좋다. 또는 프로그램의 오류나 외부 환경으로 인해 예외가 발생하는 경우라면 빨리 해당 요청의 작업을 취소하고 서버 관리자나 개발자에게 통보해주는 편이 낫다. 서버 환경에서는 체크 예외의 활용도가 점점 떨어지기 때문에 차라리 런타임 예외로 전환하여 던지는 것이 좋다.

애플리케이션 예외

애플리케이션 예외는 애플리케이션 자체 로직에 의해 의도적으로 발생시키고, 반드시 catch 해서 무엇인가 조치를 취하도록 요구하는 예외이다. 런타임 예외는 복구할 수 있는 예외가 없다고 가정하는 것과 상반된다.

애플리케이션 예외를 처리하는 방법에는 0과 -1 처럼 리턴 값을 일종의 결과 상태로 사용하는 방법이있다. 이러한 방법은 예외 상황에 대한 리턴 값을 명확하게 코드화 하고 잘 관리하지 않으면 혼란이 생길 수 있다. 그리고 상황에 따라 다른 값을 리턴해야하기 때문에 분기문이 여러개 상길 수 있다.

또 다른 방법으로 정상적인 흐름을 따르는 코드는 그대로 두고, 잔고 부족과 같은 예외 상황에서는 비즈니스적인 의미를 띤 예외를 던지도록 하는 것이다. 이때 사용하는 예외는 의도적으로 체크 예외로 만들고 개발자에게 잊지 않고 자주 발생 가능한 예외 상황에 대한 로직을 구현하도록 강제한다.

DataAccessException

JdbcTemplate을 사용하면 체크 예외인 SQLException이 언체크 예외인 DataAccessException으로 전환된다. 이는 복구 불가능한 예외를 일단 런타임 예외로 전환하여 애플리케이션 레벨에서는 신경쓰지 않도록 하고, SQLException에 담긴 다루기 힘든 상세한 예외 정보를 의미있고 일관성 있는 예외로 전환하여 추상화 하기 위함이다.

DataAccessException는 JDBC의 SQLException을 전환하는 용도만 아니라, JPA 등 다른 표준의 데이터 엑세스에도 사용되어 의미가 같은 예외라면 기술의 종류에 상관없이 일관된 예외가 발생하도록 해준다.

DataAccessException는 자바의 주요 데이터 액세스 기술에서 발생할 수 있는 대부분의 예외를 추상화 하고 있다. JPA나 Hibernate와 같은 ORM에서 발생하는 예외가 JDBC에는 없는 경우가 있는데 스프링의 DataAccessException는 이러한 경우를 모두 포함하여 계층 구조로 분류해 놓았다.

유의 사항

DuplicateKeyException 은 JDBC에만 존재한다. JPA나 하이버네이트에서는 DB의 에러 코드와 달리 예외가 세분화 되어있지 않아 스프링이 최종적으로 DataAccessException으로 변환한다. 따라서 기술의 종류와 상관 없이 일관된 예외를 얻고 싶으면 DuplicatedUserIdException 처럼 직접 예외를 정의 해두고 상세한 예외 전환이 필요하다.

728x90
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함