Java 미니 기술세미나 Exception

김승연·2023년 6월 14일

Error, Exception 차이 💡

Error (오류)

  • 프로그램에서 복구할 수 없는 시스템 수준의 심각한 문제를 의미 (주로 자바 가상머신에서 발생시키는 것)
  • 개발자가 미리 예측하여 방지할 수 없음 (로직 수정으로 처리할 수 없음)
ex) OutOfMemoryError, ThreadDeath, StackOverflowError 등

Exception (예외)

  • 개발자가 구현한 로직에서 실수가 발생하거나, 사용자 영향에 의해 발생
  • 개발자가 미리 예측하여 방지할 수 있음 (상황에 맞는 예외처리 필요)

Java 예외의 종류

상속 관계가 이렇게 되어 있는 이유는 Exception이나 Error의 성격은 다르지만, 모두 동일한 이름의 메소드를 사용하여 처리할 수 있도록 하기 위함

1. checked exception (일반 예외)

컴파일러가 해당 예외를 확인하고 처리 여부를 강제하는 예외

  • 처리를 해주지 않으면 런타임 전에 에러가 발생한다. (실행 불가) ⬇️
Checked Exception 종류
IOException입출력 작업 중에 발생하는 예외 (파일을 읽거나 쓸 때 발생)
SQLException데이터베이스 작업 중에 발생하는 예외 (데이터베이스 연결, 쿼리 실행, 트랜잭션 등)

2. unchecked exception (실행 예외)

컴파일러가 해당 예외를 확인하지 않고 예외 처리를 강제하지 않는 예외
RuntimeException을 상속한 클래스

  • 처리 안해줘도 실행 되지만, 런타임에서 에러 발생
Unchecked Exception 종류
NullPointerExceptionnull 값을 갖는 참조 변수로 객체 접근 연산자인 도트(.)를 사용했을 때 발생
ArrayIndexOutOfBoundsException배열에서 인덱스 범위를 초과하여 사용할 때 발생
NumberFormatException숫자로 변환할 수 없는 문자열을 숫자로 변경할 때 발생
ClassCastException허용되지 않는데 억지로 타입 변환을 시도할 경우 발생 (ex. 부모 클래스를 자식 클래스로 타입 변환 시도)
IllegalArgumentException적합하지 않거나(illegal) 적절하지 못한(inappropriate) 인자를 메소드에 넘겨주었을 때 발생
ArithmeticException예외적인 산술 조건이 발생했을 때 (ex. 0으로 나누기)

Throwable 클래스에 선언, Exception 클래스에서 오버로딩한 메소드

1. getMessage()

예외 메시지를 String 형태로 제공받는다. 예외가 출력되었을 때 어떤 예외가 발생되었는지를 확인할 때 매우 유용하다. 즉, 그 메시지를 활용하여 별도의 예외 메시지를 사용자에게 보여주려고 할 때 좋다.

2. toString()

예외 메시지를 String 형태로 제공받는다. 그런데, getMessage() 메소드보다는 약간 더 자세하게, 예외 클래스 이름도 같이 제공한다.

3. printStackTrace()

가장 첫 줄에는 예외 메시지를 출력하고, 두 번째 줄부터는 예외가 발생하게 된 메소드들의 호출 관계(스택 트레이스)를 출력해준다.


Exception 처리 방법

try : 예외 발생 가능성이 있는 로직이 포함된 블럭
catch : 예외가 발생했을 때 처리되는 로직이 포함된 블럭
finally : 예외가 발생하던 안하던 최종적으로 처리되는 로직이 포함된 블럭

[예외에 대한 책임 전가]

📍 throw

  • 예외를 강제로 발생시킨 후, 상위 블럭이나 catch문으로 예외를 던진다.
  • 즉, 예외를 발생시키는데 사용

public void divide(int num1, int num2) {
    if (num2 == 0) {
        throw new ArithmeticException("Divide by zero error");
    }
    int result = num1 / num2;
    System.out.println("Result: " + result);
}

📍 throws

  • 예외가 발생할 수 있는 메서드에서 해당 예외를 호출자에게 전달하는 데 사용
  • 예외를 직접 처리하지 않고, 호출자에게 예외 처리 책임을 넘기는 방식
  • Throws를 던지면 해당 코드를 사용하는 쪽에서 try catch 요구를 받게 된다.

import java.io.IOException;

public class FileReader {

    public void readFile() throws IOException {
        // 파일을 읽는 작업
        // IOException이 발생할 수 있음
    }

    public static void main(String[] args) {
        FileReader reader = new FileReader();
        try {
            reader.readFile();
        } catch (IOException e) {
            System.out.println("파일 읽기 오류: " + e.getMessage());
        }
    }
}

1. 예외 복구

  • 예외 상황을 파악하고 문제를 해결해 정상 상태로 돌려놓는 방법
  • 예외를 잡아서 일정 시간, 조건만큼 대기하고 다시 재시도를 반복
  • 최대 재시도 횟수를 넘기게 되는 경우 예외 발생시킴

final int MAX_RETRY = 100;
public Object someMethod() {
    int maxRetry = MAX_RETRY;
    while(maxRetry > 0) {
        try {
            ...
        } catch(SomeException e) {
            // 로그 출력. 정해진 시간만큼 대기한다.
        } finally {
            // 리소스 반납 및 정리 작업
        }
    }
    // 최대 재시도 횟수를 넘기면 직접 예외를 발생시킨다.
    throw new RetryFailedException();
}


2. 예외처리 회피

  • 예외 처리를 직접 담당하지 않고 호출한 쪽으로 던져 회피하는 방법
  • 긴밀하게 역할을 분담하고 있는 관계가 아니라면 예외를 그냥 던지는 것은 무책임하다.

public void method1(){
 try {
   method2(); // throws가 붙은 method2는 반드시 이렇게 try문 안에서 호출되어야 함. 
   // method2가 떠넘긴 예외를 아래 catch문을 통해 처리
}
 catch (ClassNotFoundException e1) {
  System.out.println("클래스가 존재하지 않습니다.");
}

public void method2() throws ClassNotFoundException {
 Class class = Class.forName("java.lang.String22");
}


3. 예외 전환

  • 회피와 비슷하게 메서드 밖으로 예외 던지지만, 그냥 던지지 않고 적절한 예외로 전환해서 넘기는 방법
  • 더 명확한 의미로 전달하기 위해 적합한 의미를 가진 예외로 변경
  • 예외 처리를 단순하게 만들기 위해 포장(wrap)할 수도 있음

// 조금 더 명확한 예외로 던진다.
public void add(User user) throws DuplicateUserIdException, SQLException {
    try {
        // ...생략
    } catch(SQLException e) {
        if(e.getErrorCode() == MysqlErrorNumbers.ER_DUP_ENTRY) {
            throw DuplicateUserIdException();
        }
        else throw e;
    }
}

// 예외를 단순하게 포장한다.
public void someMethod() {
    try {
        // ...생략
    }
    catch(NamingException ne) {
        throw new EJBException(ne);
        }
    catch(SQLException se) {
        throw new EJBException(se);
        }
    catch(RemoteException re) {
        throw new EJBException(re);
        }
}

서버쪽 시스템 오류를 메세지로 사용자에게 보여주게 되면, 심각한 보안적인 사고를 초래할 수 있다. 따라서 에러메세지를 사용자에게 보내주면 안될 때 오류를 파일 형식으로 저장할 수 있는 로깅 시스템을 사용한다.

0개의 댓글