티스토리 뷰

 

프로젝트를 진행하면서 가끔 내가 사용하고 있는 예외(Exception)에 대하여 제대로 사용하고 있는가 라는 의문이 들 때가 있을 것이다. 또는 정리가 필요하는 생각을 가졌을 것으로 생각된다. 이 글을 통해 이해하고 생각하며 적용할 수 있기를 바란다.

 

1. 예외? (Error vs Exception)

먼저 오류(Error)와 예외(Exception)의 개념을 정리해보자.

오류(Error)는 시스템에 비정상적인 상황이 생겼을 때 발생한다. 시스템 레벨에서 발생하여 심각한 수준의 오류를 말한다. 이런 오류는 개발자가 미리 예측할 수 없기 때문에, 오류에 대한 처리를 신경 쓰지 않아도 된다. 프로그램에 오류가 있을 때 발생되도록 의도된 오류이다.

예외(Exception)는 개발자가 구현한 로직에서 발생한다. 즉, 예외는 발생할 상황을 미리 예측하여 처리할 수 있기 때문에 예외를 구분하고 그에 따른 처리 방법을 명확히 알고 적용하는 것이 중요하다.

 

2. 예외 클래스

모든 예외 클래스는 Throwable 클래스를 상속받고 있다. Trowable은 상속받은 클래스는 Error와 Exception이 있다. 위에 언급한 오류(Error)와 예외(Exception)의 개념을 다시 생각해 보자.

Exception은 수많은 자식 클래스를 가지고 있다. 그중 Runtime Exception을 주목해야 한다. Runtime Exception은 Checked Exception과 Unchecked Exception을 구분하는 기준이다. Exception의 자식 클래스 중 Runtime Exception을 제외한 모든 클래스는 Checked Exception이며, Runtime Exception과 그의 자식 클래스들을 Unchecked Exception이라 부른다.

 

3. Checked Exception과 Unchecked Exception

두 Exception의 가장 명확한 구분은 "반드시 처리해야 하느냐"이다.

  • Checked Exception - 컴파일 시 검사하는 예외로서 예외가 발생할 가능성이 있는 메서드이면 반드시 로직을 try/catch로 감싸거나 throws로 호출한 메서드에게 예외를 넘겨준다. Java에만 존재하는 특별한 예외 처리 방식이다.
  • Unchecked Exception - 런타임 시 검사하는 예외로서, 컴파일하는데 문제는 없지만 실행 중에 예외가 발생할 수 있어 Runtime Exception이라고도 한다.

추가적으로 예외 발생 시 트랜잭션의 Rollback 여부에 따라 구분이 가능하다. 개발자가 이를 인지하여 트랜잭션의 전파 방식과 롤백 규칙 등을 적절히 사용해야 한다.

 

4. 예외 처리 방법

예외를 처리하는 일반적인 방법은 3가지가 있다.

4-1. 예외 복구(예외가 발생하면 다른 작업 흐름으로 유도)
핵심은 예외가 발생하여도 어플리케이션은 정상적인 흐름으로 진행된다는 것이다. 예외로 인해 기본 작업 흐름이 불가능하다면 다른 작업 흐름으로 자연스럽게 유도해주는 것이다.

int maxretry = MAX_RETRY;
while(maxretry -- > 0) {
    try {
        // 예외가 발생할 가능성이 있는 시도
        return; // 작업성공시 리턴
    }
    catch (SomeException e) {
        // 로그 출력. 정해진 시간만큼 대기
    }
    finally {
        // 리소스 반납 및 정리 작업
    }
}
throw new RetryFailedException(); // 최대 재시도 횟수를 넘기면 직접 예외 발생

4-2. 예외처리 회피(예외 복구를 하지 않고 호출한 쪽으로 throws)
예외가 발생하면 throws를 통해 호출 한쪽으로 예외를 던지고 그 처리를 회피하는 것이다. 하지만 무책임한 회피는 위험하다. 호출한 쪽에서 다시 예외를 받아 처리하도록 하거나, 해당 메서드에서 이 예외를 던지는 것이 최선의 방법이라는 확신이 있을 때만 사용해야 한다.

public void add() throws SQLException {
    ... // 구현 로직
}

4-3. 예외 전환(호출한 쪽으로 던질 때 명확한 의미를 전달하기 위해 다른 예외로 전환하여 throw)
발생한 예외를 적절한 예외로 전환해서 던진다. 호출한 쪽에서 예외를 받아서 명확하게 처리를 돕기 위한 방법이다. 어떤 예외인지 분명해야 처리가 수월하기 때문이다.

catch(SQLException e) {
   ...
   throw DuplicateUserIdException();
}

 

결론

예외 복구 전략이 명확하고 그것이 가능하면 Checked Exception을 try catch로 잡고 해당 복구를 하는 것이 좋다. 하지만 생각보다 그런 경우는 많지 않으며 Checked Exception이 발생하면 더 구체적인 Unchecked Exception을 발생시키고 예외에 대한 메시지를 명확하게 전달하는 것이 효과적이다.

무책임하게 상위 메서드로 예외 던지는 행위, 또한 예외를 잡고 아무런 처리도 하지 않는 것은 정망 위험한 코드이다. 그 원인 조차 파악하기 어렵기 때문이다. 예외 처리를 할 때는 신중해야 한다.

고객의 요구사항에 맞춰 결과적인 동작만을 위한 코드뿐만 아니라 보안 또는 예외적 케이스에 대해  신뢰를 줄 수 있는 개발자가 되기 위해서는 예외처리를 이해하고 적용할 수 있는 능력을 갖춰야 한다. 항상 기본을 충실히 더욱 견고히 해야 함을 잊지 말자.

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
링크
«   2024/10   »
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 31
글 보관함