본문 바로가기

Backend/Java

[JAVA] 예외(Exception) 처리

 


예외(Exception) 처리 알아보기


 

 

1. 예외 처리


예외처리란? 예외가 발생한 경우 프로그램이 강제 종료되지 않도록 방지하는 것이다.

 

Q. 아니, 코드가 잘못됐는데 왜 프로그램을 종료시키면 안되나요?

A. 우리는 에러와 예외를 혼동해서는 안 된다. 에러(Error)가 존재하면 프로그램이 실행될 수 없지만, 예외(Exception)는 프로그램이 돌아가다 특정 상황 또는 조건이 만족되는 경우 프로그램이 종료되는 현상으로 문법적 오류, 논리적 오류, 물리적 오류를 모두 포함한다. 우리는 예외처리를 통해 예외가 발생한 경우 프로그램이 종료되지 않고 저장과 같은 기능을 제공함으로서 사용자에게 처리기회(선택권)를 줄 수 있다.

 

우리가 코드를 짜다보면 겪는 흔한 에러인 '런타임 에러' 도 예외(Exception)의 한 종류이다.

 

 

 

2. 예외처리 방법 ( try-catch )


예외처리 방법은

  1. 예외 상황이 발생할 가능성이 있는 곳을 try{ }catch(){ } 로 감싼다.
  2. try 구문예외발생 예상 명령과 예외 발생 시 함께 영향을 받는 명령어들을 넣는다.
  3. catch 구문예외 발생 시 처리하고 지나갈 명령을 넣는다.

 

0~9의 난수 발생 코드를 통해 자세히 알아보자.

for (int i = 0; i < 10; i++) {
    int k = (int) (Math.random() * 10); // 0~9 의 랜덤 숫자 발생

    double result = 0.0;

    result = 100 / k;
    System.out.println(100 + "÷" + k + "=" + result);
}

 

k 에 0부터 9까지의 숫자를 랜덤하게 받고, 100 / k 를 진행하여 결과를 낼 것이다.

우리가 나누기의 몫으로 1-9까지의 양수는 문제가 없지만 만약 0으로 나누게 된다면?

 

 

java.lang.ArithmeticException: / by zero 오류 즉, 0으로 나누려는 시도 탓에 수학적 오류가 뜬다.

이를 오류 없이 해결하기 위해서 사용하는 구문이 try-catch 구문이며, 0으로 나누는 계산을 하지 않고 프로그램이 진행되도록 한다.

 

 

for (int i = 0; i < 10; i++) {
    int k = (int) (Math.random() * 10); // 0~9 의 랜덤 숫자 발생

    double result = 0.0;

    try {
        // k == 0 Exception in thread "main" java.lang.ArithmeticException: / by zero
        result = 100 / k;
        System.out.println(100 + "÷" + k + "=" + result);
    } catch(ArithmeticException e){ // ArithmeticException : 수학적인 예외
        System.out.println("0으로 나눴습니다.");
        //e.printStackTrace(); // 시스템 메시지로 예외 내용을 표시하는 메소드 - 지금은 콘솔창에 표시
    }
}

 

문제가 발생할 수 있는 result = 100 / k; 코드와 출력문을 함께 try{ } 에 넣고, catch(괄호) 에 발생하는 오류의 종류를 작성해주었다. try-catch 를 이클립스에서 자동으로 작성하게 하면, 예외의 종류를 변수 e로 받아 콘솔창에 표시하도록 하는 e.printStackTarce(); 코드가 기본으로 작성된다. 위 예제는 해당 부분을 주석처리하고 "0으로 나눴습니다." 라는 문구를 띄우도록 설정했다.

 

 

랜덤한 변수 k에 0 값이 들어오게 되면 계산 대신 0으로 나눴습니다. 라는 문구가 뜬다.

 

이렇듯 catch() 안에 지정된 예외가 발생하면 System.out.println("0으로 나눴습니다."); 가 실행되지만

만약 다른 종류의 예외가 발생하면 해당 문구는 실행되지 않는다.

그래서 보통 예외처리에서는 모든 경우를 포함한 예외의 최상위 부모 클래스인 Exception 객체를 추가해서 표시한다.

// 최상위 부모 클래스 Exception
catch(Exception e){
    e.printStackTrace();
}

 

위와 같은 모든 예외와 일치할 수 있는 최상위 부모클래스를 가장 마지막에 작성해 이도저도 해당하지 않는 예외가 발생할 때 실행되도록 한다.

 

try {
    result = 100 / k;
    System.out.println(100 + "÷" + k + "=" + result);
}catch(ArithmeticException e){ // ArithmeticException : 수학적인 예외
    System.out.println("0으로 나눴습니다.");
}catch(Exception e){ // 최상위 부모 클래스
    e.printStackTrace();
}

 

 

 

3. 예외처리 자동 완성


예외가 발생할 수 있는 상황에 이클립스는 에러표시(빨간 밑줄)을 띄우고, 밑줄에 마우스를 가져다 대면 해결책을 제시한다.

 

 

상황에 따라 Add throws declaration Surround with try/catch 도움말이 뜬다.

 

둘 중 하나를 선택하는 즉시 에러표시는 사라진다.

 

 

1. Add throws declaration

 

에러가 발생한 메소드 옆에 throws Exception이 붙는다. 예외처리가 던져졌으니 처리해줘! 의 의미를 가진다.

 

 

2. Surround with try/catch

 

우리가 앞서 다룬 try-catch 구문을 자동으로 작성해준다.

 

 

 

4. 예외처리 과정


try{} catch(){} 구문에서 catch()에 해당하는 예외가 발생하면 try 안에 있는 명령 중 예외가 발생한 위치 이후의 명령은 실행되지 않는다.

 

System.out.println(1);
System.out.println(2);
try {
    System.out.println(3);
    System.out.println(4);
    System.out.println(0 / 0);
    System.out.println(6); // 실행안됨
    System.out.println(7); // 실행안됨
} catch (Exception e) {
    System.out.println(8);
    System.out.println(9);
    System.out.println("예외메시지 : " + e.getMessage()); // / by zero -> e.getMessage()
}
System.out.println(10);

 

System.out.println(0 / 0); 에서 에러가 발생한 이후에 작성된 System.out.println(6); 과 System.out.println(7); 은 출력되지 않았다.

 

 

 

5. 강제 예외 발생


Exception a = new Exception("예외 발생");
throw a;

 

예외를 객체로 선언하여 throw (예외객체); 를 하게 되면 해당 에러를 고의로 발생시킬 수 있다.

 

 

try {
    ArithmeticException a = new ArithmeticException("ArithmethicException 고의 발생");
    throw a;
}catch(ArithmeticException e) {
    System.out.printf("ArithmeticException - ");
    System.out.println("에러 메시지 : " + e.getMessage());
}catch(RuntimeException e) {
    System.out.printf("RuntimeException - ");
    System.out.println("에러 메시지 : " + e.getMessage());
}catch(Exception e) {
    System.out.printf("Exception - ");
    System.out.println("에러 메시지 : " + e.getMessage());
}

 

 catch 구문안에서 e.getMessage() 를 통해 설정한 텍스트로 반환할 수도 있다.

 

 

 

6. 예외처리를 포함한 예제


사용자로부터 형식에 맞춰 날짜를 입력하도록 한다. 글자수가 10글자가 아니거나, 형식이 다를 경우 그에 맞는 오류를 띄운다.

 

public static void main(String[] args) {

    Date inDate = null;
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    Scanner sc = new Scanner(System.in);

    System.out.print("날짜를 (YYYY-MM-DD)형식으로 입력하세요 : ");

    // 사용자가 정확한 양식(yyyy-MM-dd)에 맞춰서 날짜를 입력할 때까지 재입력 받기
    // 정확한 양식의 입력을 하면 날짜로 변환해서 2024년 10월 8일의 양식으로 출력

    // 반복실행문과 예외처리를 이용
    String strDate;

    while (true) {
        try {
            strDate = sc.nextLine();

            if(strDate.length() != 10)
                throw new Exception("글자 수 오류");
            // if문이 실행되면 아래 명령등이 실행되지 않게하기 위해 강제 예외를 발생하고 catch에서 예외처리

            inDate = sdf.parse(strDate);
            break; // while문 탈출
            // break : 자신을 감싸고 있는 반복실행문이나 switch를 탈출
            // 해당 코드에서의 if나 try{}catch(){}는 해당사항이 없음
        } catch (ParseException e) {
            System.out.print("양식이 다릅니다 (YYYY-MM-DD) 형식으로 다시 입력하세요 : ");
        } catch (Exception e) {
            System.out.print("글자수가 맞지 않습니다. 다시 입력하세요 : ");
        }
    }

    SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy년 MM월 dd일");
    System.out.print("입력한 날짜는 " + sdf2.format(inDate));

}