자바프로그래밍 예외처리

최주영·2023년 3월 22일
0

자바

목록 보기
14/30

✅ 프로그램 오류

  • 종류 : 컴파일에러, 런타임에러, 시스템에러
  • 컴파일 에러 : 빨간줄 뜨는것! (개발자의 오타나 로직실수로) 실행하지못하는것

  • 런타임 에러 : 프로그램이 정상적으로 실행되지만 실행도중에 중간에 에러나는거 EX) 널포인터오류, 배열크기벗어난오류

  • 시스템 에러 : 램의 공간이 부족하거나, 전원이 차단되는 에러 (개발자가 처리할 수 없음)

이러한 에러를 해결하기 위해서는 소스 수정으로 해결 가능한 에러를 예외 라고 한다


✅ 예외 클래스 계층 구조

부모 계층의 클래스들이 자식 계층의 클래스들의 오류를 다 처리해준다
ex) catch문에 RuntimeException 넣으면 ArithmeticExcption, NullPointerException 등 자식 exception들을 다 처리할 수 있음


✅ Checked Exception

  • 예외 클래스를 만드려면 예외를 상속받는다 -> Exception
  • 새로운 예외를 발생시킬 때 throw 키워드 사용 -> 예외도 객체이므로 new로 생성 후 예외 발생
  • 예외를 메서드 밖으로 던질 때 throws 키워드 사용
public class MyCheckedException extends Exception {
   public MyCheckedException(String message) {
     super(message);
   }
}
public class Client { 
    public void call() throws MyCheckedException { // 해결못해서 밖으로 던지기 
    	throw new MyCheckedException("ex");	 // 예외 발생시키기 (ex = 메시지입력)
    }
}
public class Service {
	Client client = new Client(); 
    
    // 예외를 잡아서 처리하는 코드
    public void callCatch(){
    	try{
        	client.call(); // call 메소드에서 예외발생시 catch에서 예외처리
        }catch(MyCheckedException e){
        	// 예외처리로직
        }
    }
    
    // 체크 예외를 밖으로 던지는 코드
    public void catchThrow() throws MyCheckedException { // 예외를 잡지 않고 밖으로 던지기
    	client.call();
    }
    
}
//ex) 밑에 예외들은 무조건 try ~ catch로 예외처리 해야함!
// try ~ catch로 하지 않을시 프로그램 시작 자체가 안됨!
throw new FileNotFoundException();   //오류
throw new EOFException(); // 오류
throw new SQLException(); // 오류

예외처리 2가지 규칙

  • 예외는 잡아서 처리하거나 밖으로 던져야함
    -> 처리할 경우 -> try catch
    -> 예외를 밖으로 던질경우 (폭탄돌리기) -> 메소드 뒤에 throws 예외이름

  • 예외를 잡거나 던질 때, 지정한 예외뿐 아니라 그 예외의 자식들도 함께 처리할 수 있다
    -> Exception 을 지정하면, 자식 예외들 모두 처리 가능

Checked Exception 장단점

  • 장점 : 컴파일러를 통해 개발자가 실수로 예외를 누락하지 않을 수 있음 (Build전에 표시됨)
  • 단점 : 모든 체크예외를 잡거나 던져야하기 때문에, 불필요한 예외들도 모두 처리해야함

✅ Unchecked Exception

  • 예외를 잡거나, 던지지 않아도됨 -> 예외를 잡지않으면 자동으로 밖으로 던짐
    -> Checked Exception과 다르게 컴파일러가 이런 부분을 체크하지 않음

		// 발생하는 예외에 대해서 처리하지 않아도 되는 예외 (Unchecked Exception)
		 ArithmeticException : 수학적으로 계산이 불가능할 때 발생하는 Exception
		 int su = 10;
		 int su2 = 0;
		 System.out.println(su/su2);

		// ArrayIndexOutOfBounsException
		 배열의 인덱스범위를 초과해서 접근할 때 발생하는 예외
		 +int[] intArr = new int[5];
		 System.out.println(intArr[5]);

		// ClassCastException
		 클래스형변환을 잘못했을 때
		 Object o = new String("안녕");
		 Integer num = (Integer)o; // 문자열을 정수형으로 바꿀 수 없음

		// NullPointException
		 참조형변수에 null값이 있을 때 접근연산자를 사용하면 발생하는 예외
		 String name = null;
		 name.length(); // name에는 널값이 있는데 .연산자로 접근하고 있어서 발생

		// NumberFormatException
		 문자열을 숫자형으로 변환할 때 변환 불가능한 문자가 있는 경우
		 String name = null;
		 su = Integer.parseInt(name); //문자열을 정수로 바꿀 수 없음

		// InputMissMatchException
		 입력값의 타입이 일치하지 않을 때 발생
		 Scanner sc = new Scanner(System.in);
		 int su3 = sc.nextInt();

UnChecked Exception 장단점

  • 장점 : 신경쓰고 싶지 않는 언체크 예외를 무시할 수 있다
  • 단점 : 개발자가 실수로 예외를 누락할 수 있다

✅ 체크 예외 vs 언체크 예외

  • 체크예외 : 예외를 잡아서 처리하지 않으면 항상 명시적으로 throws 키워드로 예외를 던져야함
    -> Exception을 상속받은 예외는 체크 예외가 됨

  • 언체크예외 : 예외를 잡아서 처리하지 않아도 throws 키워드를 생략 가능
    -> RuntimeException을 상속받은 예외는 언체크 예외가 됨


✅ finally

try catch문을 이용해서 예외를 처리할 경우
정상적으로 연결이 되든 안되든 필수적으로 실행해야하는 기능이 있다 (ex : 자원 반환)
그럴 경우 catch 뒤 finally 키워드를 사용한다

try{
	// 정상흐름
}catch{
	// 예외흐름
}finally{
	// 반드시 호출해야하는 마무리 흐름
}

try 문이 시작되면, catch안에서 잡을 수 없는 예외가 발생해도 finally 코드는 어떤 경우라도 호출됨

try finally 만 사용할 수도 있음


✅ try-with-resources

try 에서 외부 자원을 사용하고, try 가 끝나면 외부 자원을 반납하는 패턴이 반복됨
-> 자바7에서부터 try-with-resources 라는 편의 기능을 제공함

이 기능을 사용하려면 먼저 AutoCloseable 인터페이스를 구현해야함
이 인터페이스를 구현하면 try가 끝나는 시점에 close() 가 자동으로 호출됨
-> try에서만 자원을 사용하기 때문에, catch문 시작전에 try 가 끝나는 순간 close() 호출됨


try-with-resources 장점

  • 모든 자원이 제대로 닫히도록 보장 (실수로 닫지 않는 것을 대비)
  • 명시적인 close() 호출이 필요 없으므로 코드가 간결해짐
  • 기존에는 try-> catch-> finallycatch 이후에 자원을 반납했지만
    try 블럭이 끝나면 즉시 close()를 호출한다 (더 빠른 자원해제가 가능)

✅ 예외 계층

  • 디테일한 예외들을 먼저 적어야함
try{
  // 정상흐름
}catch(ConnectException e){
  // 연결오류
}catch(NetworkClientException e){
	// 네트워크 오류
}catch(Exception e){
	// 알수없는 오류 -> 위 2개 예외로 처리못한 나머지 모든 예외들은 여기서 처리됨
}finally{
	// 무조건실행되는 로직
}

✅ 실무 예외처리

실무에서는 수많은 예외처리들이 있다

하지만 실제로 개발자가 catch 문을 사용해서 처리할 수 있는 것이 많지 않다
그래서 모든 예외들을 잡는 것은 좋지 않다 -> catch 로 처리해도 똑같은 문제가 발생하기 때문에
ex) 데이터베이스 서버 문제

그러면 모든 예외들을 throws를 사용해서 던지면 되지 않을까?
하지만 요즘 라이브러리도 많이 사용하면서, 수 많은 예외들이 존재한다
모든 예외들을 해결하지 않고 밖으로 던지면 모든 클래스에서 지저분한 코드만 추가해지는 것 뿐이다
-> 해결하지 못한 예외를 폭탄처럼 돌리기 때문에

그러면 최상위 계층인 Exception 을 던져서 처리하면 되지 않을까?
코드는 깔끔해지지만 치명적인 문제가 발생한다
-> 최상위 타입인 Exception을 던지게되면 다른 체크 예외를 체크할 수 있는 기능이 무효화됨
-> 중요한 체크 예외를 다 놓치게 됨
-> 중간에 중요한 체크 예외가 발생해도 컴파일러는 문법에 맞다 판단해서 컴파일 오류를 발생 X
즉 꼭 필요한 경우가 아니라면, Exception 자체를 밖으로 던지는 것은 좋지 않다


요즘은 본인이 해결할 수 있는 예외만 잡아서 처리하고
해결할 수 없는 예외는 던지는것이 더 나은 선택일 수도 있다

그리고 처리할 수 없는 예외들은 공통 으로 처리할 수 있는 곳을 만들어서 한곳에서 해결한다
ex) 처리할 수 없는 모든 예외들 -> 현재 시스템에 문제가 있습니다 오류 메시지 출력

// 공통 예외처리
private static void exceptionHandler(Exception e) {
     //공통 처리
     System.out.println("사용자 메시지: 죄송합니다. 알 수 없는 문제가 발생했습니다.");
     System.out.println("==개발자용 디버깅 메시지==");
     e.printStackTrace(System.out); // 스택 트레이스 출력
     //e.printStackTrace(); // System.err에 스택 트레이스 출력
     //필요하면 예외 별로 별도의 추가 처리 가능
     if (e instanceof SendExceptionV4 sendEx) {
       System.out.println("[전송 오류] 전송 데이터: " + sendEx.getSendData());
       }
 }
 
try { 
   networkService.sendMessage(input);
} catch (Exception e) { // 모든 예외를 잡아서 처리
   exceptionHandler(e);
}

초기 자바에서 제공하는 컴파일에서 잡아주는 체크 예외가 더 많았고, 많이 사용하였다.
하지만 시간이 지나면서 복구할 수 없는 예외가 많아지고 라이브러리에서 제공하는 모든 예외들을
처리할 수 없을 때마다 throws 예외를 덕지덕지 붙였었다.
이러한 문제점 때문에 최근 만들어진 라이브러리는 대부분 UnChecked Runtime 에러를 사용함
t런타임 예외도 필요한 부분은 잡을 수 있고, 해결할 수 없는 부분은 공통으로 처리하는 부분에 던져줘서 처리 또는 그 부분은 무시하면 효율적이다.


profile
우측 상단 햇님모양 클릭하셔서 무조건 야간모드로 봐주세요!!

0개의 댓글