• 구현된 프로그램 로직에서 발생하는 오류를 의미
  • 발생하는 Exception을 적절하게 처리해야 프로그램이 종료되지 않고 원활히 돌아가게 할 수 있다

  • Exception VS Error
    • Exception(예외) : 구현한 로직에서 발생. 처리가능
    • Error(에러) : 시스템레벨에서 발생. 처리불가능
  • Error는 catch로 잡으려고 해서는 안되기 때문에 Throwable을 catch로 잡으면 안된다
    • Error가 하위에 속해 있다

Exception 종류

image

  • CheckedException는 복구가 가능할 떄 RuntimeException, Error는 복구가 불가능할 때 사용

1. Checked Exception

  • 컴파일시 체크되는 exception -> Exception 상속
    • 반드시 예외처리가 되어야한다
    • 예외를 던질 시 반드시 throws로 던져야 함
  • 예외발생시 트랜잭션을 commit
    • CheckedException은 예외처리가 강제되기 때문에 이 과정에서 복구가 가능하다고 생각하는 메커니즘이다
    • 복구가 되었기 때문에 commit을 하는 것으로 추측한다 ex) ClassNotFoundException

문제점

  1. 복구 불가능한 예외
    • 반드시 처리해야 하는 Exception이지만 비즈니스 로직에서 처리할 방법이 없을 수 있다
    • SQLException의 경우 서비스/컨트롤러에서 처리할 방법이 없다. 계속해서 던져진 예외는 DispatcherServlet에서 500코드로 응답하게 된다
    • 이렇게 해결 불가능한 예외는 오류 로그를 남기고, 개발자가 빠르게 인지할 수 있도록 알람을 받아 빠르게 해결해야 한다
  2. 의존관계 문제
    • 상위로 던져진 예외에 의존하게 된다.
    • SQLException의 경우 서비스/컨트롤러에서 java.sql.SQLException에 의존하게 된다

그렇기 때문에 CheckedException을 RuntimeException으로 전환시켜야 한다 (예외 전환)

2. Unchecked Exception

  • 컴파일시 체크되지 않고 런타임시 발생하는 exception -> RuntimeException 상속
    • 예외처리를 하지 않아도 프로그램은 돌아간다
    • 예외로 던질 시 throws 생략 가능 -> 처리되지 않은 예외는 자동으로 던져준다
  • 예외발생시 트랜잭션을 rollback
    ex) NullPointerException

  • 런타임 예외는 복구 불가능한 예외를 서비스/컨트롤러에서 신경쓰지 않아도 되며 의존관계 문제도 사라진다

Exception 처리

1. try-catch-finally (복구)

  • 현재 로직에서 예외를 처리하는 방법
  • try-catch 문에서 여러개를 catch할 경우, 하위에 해당하는 Exception부터 처리해야 한다
    • ArithmeticException -> RuntimeException -> Exception 순
public class ExceptionEx1 {

	public static void main(String[] args) {
		try {
		    System.out.println(1/0);
		} catch (ArithmeticException e) {		//ArithmeticException만 실행
		    System.out.println("0으로 나누지 마세요");
        	e.printStackTrace();
		} catch (RuntimeException e) {			//RuntimeException중 ArithmeticException이 아닌것만 실행
		    System.out.println("연락요망");
		    e.printStackTrace();	
		} catch (Exception e) {				 //RuntimeException 이외의 나머지 Exception이 실행		
       		 e.printStackTrace();
        }
    }

}
  • try문을 실행하다가 예외가 발생했을 때 여러개의 catch문이 존재한다면 위에서부터 instanceof의 결과가 true인 catch문을 찾아내려간다. 찾으면 그 catch문 하나만을 실행하고 try-catch문을 빠져나온다

  • finally 구문은 예외가 발생하던 발생하지 않던 무조건 마지막에 실행된다

    • 자원 해제 등을 위해 사용한다
public class ExceptionEx2 {

	public static void main(String[] args) {
		try {
			System.out.print(1);
			System.out.print(2);
			System.out.print(3/0);		// exception 발생
			System.out.print(4);
		} catch (Exception e) {
			System.out.print(5);
			return;				 //main method 종료
		} finally {
			System.out.print(6);
		}
		System.out.print(7);
	}	
}

> 1256

2. try-with-resource (복구)

  • 자바 7버전부터 추가된 문법
  • finally를 사용해서 자원을 수동으로 해제시켜줘야 했지만 이 문법을 사용하면 try구문이 끝나면 자동으로 자원을 해제시켜준다
  • try절 시작시 할당한 자원을 구문종료시 해제시킨다

3. throws (회피)

  • 현재 로직에서 exception이 발생해도 처리하지 않고 자신을 호출한 상위 메서드로 던지는 방법 (책임회피)
  • 상위 메서드에 던지지만 처리되지 않고 메인메서드까지 도달해 처리되지 않는다면 쓰레드가 종료된다
public void doSomething() throws Exception { 
	//something
}

throw 키워드

 - Exception을 발생시키는 키워드

public class ThrowEx {
	public static void main(String[] args) {
		try {
		    Exception e = new Exception("고의로 발생");
		    throw e;				// = throw new Exception("고의로 발생");
		} catch (Exception e) {
		    e.printStackTrace();
		    System.out.println(e.getMessage());
		}
	}
}
> java.lang.Exception: 고의로 발생
  	at ch08.ThrowEx.main(asdf.java:7)
  고의로 발생

4. 예외 전환 (전환)

  • 발생한 예외에 대해 또 다른 예외로 변경하는 것
  • 특정한 Exception이 발생하면 새로운 Exception으로 전환하여 구체적으로 예외를 다룰 수 있다
  • CheckedExceptionRuntimeException으로 전환하여 예외를 다룰 수 있도록 전환이 가능하다
  • 예외를 전환할 경우 Cause 예외를 반드시 담아서 던져야 한다
    • 발생한 예외를 전환시킬 예외의 생성자로 넘겨줘야 정보가 사라지지 않고 상위로 전달된다
try {
	...
} catch (xxxException e) {
	throw CustomxxxxxException(e);
}

예외처리하는 방법

  • 기본적으로 런타임 예외를 사용하자
  • 체크예외는 비즈니스 로직상 의도적으로 던지는 예외에만 사용하자 (계좌이체 실패, 포인트 부족 등)
  1. catch문에는 로깅과 복구로직을 추가해라
    • 로그를 찍을 때는 e.printStackTrace()를 사용하지 말고 로그 프레임워크를 사용
  2. Exception을 무시하거나 다시 던지지 마라
    • catch 블럭을 비워두지 마라
  3. 리소스 정리
    • 리소스를 사용하는 구문이라면 반드시 리소스를 해제해야 한다
  4. 더 자세한 예외
    • 메서드 정의시 발생하는 예외를 최대한 자세한 예외를 명세하는 것이 좋습니다
     // bad
     public void exceptableMethod() throw Exception;
    
     // good
     public void execptableMethod() throw NumberFormatException;
    
  5. 메시지를 자세하게 적는다
    • 예외가 발생하면 메시지를 보고 처리할 수 있도록 가능한 자세하게 적어야 한다