모르지 않다는 것은 아는것과 다르다.

Java

Exception

채마스 2022. 2. 26. 00:11

Error vs Exception

  • Error 란?
    • Error(오류)는 시스템에 비정상적인 상황이 생겼을 때 발생한다.
    • 시스템 레벨에서 발생하기 때문에 심각한 수준의 오류이고, 개발자가 미리 예측하여 처리하기 힘들다.
    • 그렇기 때문에 애플리케이션에서 오류에 대한 처리를 거의 신경 쓰지 않아도 된다.
  • Exception 이란?
    • 개발자가 구현한 로직에서 발생한다.
    • 예외는 발생할 상황을 미리 예측해서 처리할 수 있다.
    • 그렇기 때문에 예외를 구분하고 그에 때른 처리 방법을 명확히 알고 적용하는 것이 중요하다.
    • 크게 RuntimeException 과 그 밖에 여러 Exception으로 구분된다.
    • RuntimeException 의 종류는 아래와 같다.
      • ArithmeticException : 정수를 0으로 나누었을 경우
      • ArrayStoreException : 배열 유형이 허락하지 않는 객체를 배열에 저장하려는 경우
      • ArrayIndexOutOfBoundsException : 배열을 참조하는 인덱스가 잘못된 경우
      • ClassCastException : 적절치 못하게 Class를 형 변환하는 경우
      • NullPointerException : 널 객체를 참조했을 경우
      • NegativeArraySizeException : 배열의 크기가 음수인 경우
      • NoClassDefFoundException : 클래스를 찾을 수 없는 경우
      • OutOfMemoryException : 사용 가능한 메모리가 없는 경우
      • IndexOutOfBoundsException : 객체의 범위를 벗어난 index를 사용하는 경우
      • IllegalArgumentException : 메소드 유형이 일치하지 않는 매개변수를 전달하는 경우
      • IllegalMonitorStateException : 스레드가 스레드에 속하지 않는 객체를 모니터 하려고 기다리는 경우
      • IllegalStateException : 적절하지 않는 때에 메소드를 호출하는 경우

 

예외클래스

  • 위의 그림은 예외클래스의 구조이다.
  • 모든 예외클래스는 Throwable 클래스를 상속받고 있고, Throwable은 Object의 자식클래스다.
  • Error 와 Exception은 Throwable을 상속받고 있다.
  • 여기서 RuntimeException을 주목할 필요가 있는데, RuntimeException는 UnCheckedException이다.
  • Error 와 RuntimeExcption은 UnCheckedException 에 속한다.
  • RuntimeExcption 를 제외한 Exception은 CheckedException 에 속한다.

 

 

Checked Exception VS Unchecked Exception

  • Checked Exception과 Uncheckd Exception을 구분하는 기준은 '꼭 처리를 해야 하느냐" 이다.
  • Checked Exception은 컴파일단계에서 확인이 가능하고, Unchecked Exception은 실행단계에서 확인이 가능하다.
  • Checked Exception이 발생할 수 있는 메소드는 반드시 try/catch로 감싸거나 Throw를 던져야 한다.
  • Unchecked Exception 는 명시적으로 예외를 처리하지 않아도 된다.
  • 그렇기 때문에 개발자가 부주의해서 발생하는 경우가 많다.

 

Exception 과 roll-back 관계

  • 개발자는 예외발생시 트랜잭션의 roll-back 여부를 인지하고 있어야한다.
  • 그 이유는 roll-back 이 되는 범위가 달라지기 때문에 개발자가 이를 인지하지 못하면 예상하지 못한 예외가 발생할 수 있기 때문이다.
  • Checked Exception 는 예외가 발생하면 트랜잭션을 roll-back하지 않고 예외를 던진다.
  • Unchecked Exception 은 예외 발생 시 트랜잭션을 roll-back한다는 점에서 차이가 있다.
  • 그렇기 때문에 트랜잭션의 전파방식(propagation behavior)과 트랜잭션을 어떻게 묶어놓느냐에 따라서 Exception의 영향도가 커진다.
  • 그러므로 전파방식(propagation behavior)과 roll-back 규칙을 적절하게 설정하면 효율적으로 애플리케이션을 구현할 수 있다.
  • 아래와 같이 @Transactional의 rollbackFor 옵션을 이용하면 Rollback이 되는 클래스를 지정할 수 있다.
  // Exception예외로 롤백을 하려면 다음과 같이 지정
  @Transactional(rollbackFor = Exception.class) 
  // 여러개의 예외를 지정
  @Transactional(rollbackFro = {RuntimeException.class, Exception.class})
  //예외가 발생하면 롤백이 되지 않도록 지정
  @Transactional(noRollbackFor={IgnoreRollbackException.class})

 

예외 처리 방식

try catch finally - 직접 처리 방식

  try{
    // 예외 발생 가능성이 있는 코드 
    // 예외가 발생하면 try 블록의 나머지 문장들은 수행되지 않습니다.
  } catch{
    // 최소 하나의 catch 블록 
    // Exception은 java.lang.Throwable 클래스의 하위 클래스 타입으로 선언되어야 합니다. 
    // try 블록 안에서 발생한 예외와 동일한 예외 타입 catch 블록을 수행합니다. 
    // 보통 catch 문 안에는 로직을 넣지 않습니다. log만 찍고 끝냅니다. 
    // catch 블록이 여러 개로 구성될 때, 계층구조가 높을수록 아래로 내려갑니다. 즉, 자식 Exception부터 아래로 구성합니다. 
    // 부모 Exception부터 Catch 블록이 구성되면 UnReachableException이 발생합니다.
  } finally{
    // finally 블록은 필수 블록이 아닙니다 
    // 무조건 실행됩니다. 그래서 항상 수행해야 할 필요가 있는 코드를 넣습니다
  }

throws - 간접 처리 방식

  • 방법 1
  void innerMethod() throws UnCheckedException { 
    UnCheckedException uce = new UnCheckedException(); 
    uce.unCheckedException(); // Exception 발생 
  } 
  public static void main(String[] args) { 
    try{ 
      innerMethod(); // 메서드 호출 
    } catch(UnCheckedException ex){ 
      System.err.println(e); 
    } 
  }
  • 위와 같이 코드가 있는 메서드를 호출하는 곳으로 예외 처리의 책임을 넘깁니다.
  • 방법 2
  public class Main { 
    public static void throwExample() throws Exception { 
      throw new Exception(); 
    } 
    public static void main(String[] args) { 
      try { 
        throw new Exception(); 
        } catch (Exception e){ 
          System.out.println("예외가 발생했습니다..."); 
        } 
    }
  }
- 로직상에 Exception 처리와는 관련 없지만, 예외를 발생시키는 방법: throw

 

예외 처리 방법

예외 복구

  public static void main(String[] args) throws IOException {
      byte[] list = {'a', 'b', 'c'};
      try {
          System.out.write(list);
      } catch (IOException e) {
         log.info(e);
      }
  }
  • 예외 복구 방법은 대부분의 상황에서 예외를 복구할 수 없기에 실무에서는 보통 잘 사용할 일이 없다.

예외 처리 회피

  public static void main(String[] args) throws IOException {
     byte[] list = {'a', 'b', 'c'};

     System.out.write(list[4]);
  }
  • 예외 처리를 자신이 담당하지 않고 자신을 호출한 쪽으로 던져버리는 방법 ( throws )으로 위와 같은 코드로 예외를 던져버린다.
  • 적당한 상황에서 사용하면 퀄리티 좋은 코드가 나오지만 무분별하게 사용하게 된다면 코드의 퀄리티가 떨어지기에 추천하는 방법은 아니다.

예외 전환 (추천)

  public static void main(String[] args)  {
        byte[] list = {'a', 'b', 'c'};

        try {
            System.out.write(list);
        } catch (IOException e) {
            throw new RuntimeException("list를 읽지 못했습니다.");
        }
    }
  • 예외를 적절한 예외로 전환해서 자신을 호출할 쪽으로 던져버리는 방법으로 아래와 같은 방법으로 예외를 전환시킨다.
  public class CustomException extends RuntimeException{
    public CustomException(String message) {
        super(message);
    }
}
  • 보통 실무에서는 예외 처리를 하기 위해서 위와 같은 방법으로 RuntimeException을 상속받은 클래스를 만들어서 예외 전환 방법으로 예외를 처리한다.

 

 

REFERENCES

'Java' 카테고리의 다른 글

Stream  (0) 2022.02.26
Static 변수와 Static 메서드  (0) 2022.02.26
Optional  (0) 2022.02.26
Object 클래스  (0) 2022.02.26
Class 클래스  (0) 2022.02.26