Java의 Exception

허진혁·2023년 7월 7일
1

기본기를 다지자

목록 보기
3/10

자바의 Exception

🤔 궁금한 점

다음과 같은 질문 두개를 던져 보았어요.

예외 및 예외처리가 무엇일까?

왜 필요할까?

자바의 Exception을 이해하기 위해 위의 두 질문부터 시작해 볼게요.

🙇 개념정리

예외는 프로그램 실행 중 비정상적인 상황을 말해요. 이는 프로그램이 예상하지 못한 입력이나 작업을 만났을 때 발생해요.

예외는 일어나서는 안되는 일이지만, 일어나기 때문에 그것들을 처리하는 것을 예외처리 라고 해요.

문제가 발생하면, 예외가 발생하고, 아무것도 하지 않는다면 애플리케이션이 충돌하게 돼요.

즉, 예외처리를 하지 않을 경우 애플리케이션 충돌로 인해 프로그램이 종료될 수가 있는 거에요.

🔑 자바의 예외 계층

모든 예외의 부모 클래스는 java.lang.Exception 클래스 에요.

모든 예외의 할아버지는 java.lang.Throwable 클래스 에요.

예외는 크게 세 가지 범주 내에 있습니다.

  • CheckException
  • UncheckedExcetpion(RuntimeExcetpion)
  • Error

Error

에러는 시스템적인 문제로 자바 프로그램 밖에서의 예외로, 오류라고 불러요. 오류는 시스템 문제이기에 개발자가 직접 처리하기 힘들어요. OutOfMemoryError, StackOverflowError 가 대표적인 예에요.

CheckedException

이 예외는 자바 컴파일러가 처리하는 예외에요. 그래서 개발자에게 컴파일러가 처리하도록 요구하는 방식이어서 예외처리를 강제한다고 말하기도 해요. 보통 자바와 외부 리소스가 상호작용할 때 발생해요. 대표적인 예외로 I/O Exception, SQL Exception, ServletException 이 있어요.

UncheckedExcetpion(RuntimeException)

이 예외는 자바 컴파일러가 예외처리를 요구하지 않아 개발자가 예상하여 직접 처리해야 합니다. RuntimeException 을 확장한 예외들이라 런타임 예외 라고도 합니다. 런타임 시에 발생하는 예외들이라 예측 불가능하다고 표현해요. 대표적인 예로 NullPointerException, IllegalArgumentException가 있습니다.

Error와 Exception의 차이

  • 프로그램 밖에서 발생하면 error, 안에서 발생하면 exception
  • 프로그램이 멈추면 error, 계속 실행할 수 있으면 excetpion
  • 프로세스에 영항을 주면 error, 쓰레드에 영향을 주면 exception

자바가 지원하는 예외처리

try-catch

예외가 발생하지 않는 경우 try 내에 모든 문장이 실행되고 try-cacth 이후 내용을 실행해요.

예외가 발생하는 경우 try내에서 예외가 발생한 지점 이후부터 실행이 안되고, cacth 내용이 실행된 후에 try-cacth 이후 내용을 실행해요.

public class ExTest {
    public void printBeforeEx() {
        System.out.println("예외 터지기 전에 출력합니다.");
    }

    public void printAfterEx() {
        System.out.println("예외 터진 후에 출력합니다.");
    }

    public void printAfterTryCatch() {
        System.out.println("try-catch 구문 후를 출력합니다.");
    }
    public static void main(String[] args) {
        ExTest ex = new ExTest();
        int zeroNum = 0;
        try {
            ex.printBeforeEx();
            System.out.println(10 / zeroNum);
            ex.printAfterEx();
        } catch (ArithmeticException e) {
            e.printStackTrace();
        }
        ex.printAfterTryCatch();

    }
}

/*
예외 터지기 전에 출력합니다.
try-catch 구문 후를 출력합니다.
java.lang.ArithmeticException: / by zero
	at practice.ExTest.main(ExTest.java:20)
*/

❗️주의 사항

try 내부에서 변수를 선언 하였는데, 에러가 터질 경우 → ‘cannot found symbol’ 에러 호출

catch는 여러개 사용 가능합니다. 다만, 자세한 예외부터 포괄적인 예외 순으로 선언해야 합니다.

즉, catch(Exception e) 이 부분을 넣는다면, 여러 개의 catch 구문 중 마지막에 있어야 합니다.

또한, catch의 파라미터에 여러 예외를 잡을 수 잇도록 할 수 있습니다.

try {
  // logic...
} catch (IOException | NumberFormatException e) {
   logger.warn("Failed to load score!", e);
}

throws

메서드를 선언할 때 throws 예약어를 붙여 확인된 예외들을 컴파일러가 예외 처리하도록 합니다.

public int getParticipants(String participantFile) throws FileNotFoundException {
    Scanner contents = new Scanner(new File(participantFile));
    return Integer.parseInt(contents.nextLine());
}

❗️ 이 방법에 주의해야 할 점이 있습니다. 매우 간단하게 사용할 수 있지만, 이 메서드를 호출하는 (상위) 메서드 모두에게 throws를 사용해야 합니다.

이처럼 컴파일러각 throws를 사용한 메서드를 호출하는 메서드에도 예외처리를 강제합니다.

👀 고민해보아야 할 것

checkedException을 처리하는 방법 중 throws를 사용하는 경우, 해당 예외를 상위 메서드로 던지는 게 좋은 습관이지만 항상 그렇지는 않을 거에요.

그래서 다음과 같은 질문을 던져보았어요.

1️⃣ 어떤 상황에서 다른 방식을 사용하는 것이 더 효율적일 수 있을까요?

비지니스 로직과 밀접한 관련이 있다면 로직 내에서 처리하는 것이 좋을 수 있습니다.

2️⃣ 또한, uncheckedException을 처리할 때에는 어떤 예외 처리 방식을 주로 사용하나요?

uncheckException을 처리할 때는 예외 처리 전략에 맞게 처리하거나, 로깅을 활용하여 문제 파악 후 원인과 상황을 기록하는 방법이 있습니다.

🤔 위의 두 질문을 답하고 나니 또 질문이 생겼어요. 그래서 에러를 다루는 전략을 다음 편부터 공부해 볼게요.

3️⃣ 비지니스 로직를 내부에서 처리할 때 어떻게 예외를 처리하고 로직에 반영하는 것이 적합할까요?

3번의 답을 하기 위해 Exception handling strategies을 보아야 해요.

Exception handling strategies

1️⃣ Clean Up Resources in a Finally Block or Use a Try-With-Resource Statement

빨간 부분을 try 내에서 처리하지 말아야 해요. 위에 file을 읽어올 때 에러가 난다면 그 순간 제어 흐름이 catch로 넘어가기에 에러가 난 코드 밑에는 자바 컴파일러가 읽지 않아요.

그렇게 되면 inputStream을 닫지 않으니 리소스를 낭비하게 돼요. 그래서 다음과 같이 finally 구문을 활용해요.

2️⃣ Prefer Specific Exceptions

던져지는 예외는 구체적일 수록 좋아요. 모든 자바의 예외는 Exception을 상속받았으니 Exception으로 처리하게 되면 구체적인 예외를 파악할 수 없어요.

예외를 구체적으로 하면 다음과 같은 이점이 있어요

  • 예외의 이름과 메시지를 통해 어떤 상황에서 발생했는지 유추가 쉬워져요.
  • 다른 예외와 혼동되지 않아 특정 예외에 대한 특정 로직을 구현할 수 있어요.
  • 디버깅 과정이 수월해지고, 코드 위치를 파악하기 용이해요.

3️⃣ Document the Exceptions You Specify

호출자가 예외를 피하거나 처리할 수 있도록 가능한 많은 정보를 기록하여 제공해야 해요.

4️⃣ Throw Exceptions With Descriptive Messages

가능한 한 정확하게 문제를 설명하고 예외적인 이벤트를 이해하는 데 가장 관련 있는 정보를 제공하여 예외가 발생했을 때, 예외 메시지를 통해 무슨일이 일어났는지 이해하기 쉽게 해야 해요.

그렇지만 예외 메시지는 몇 줄의 문장이 아닌 가독성 좋은 1~2줄로 예외 원인을 설명해야 해요.

5️⃣ Catch the Most Specific Exception First

덜 구체적인 예외를 먼저 잡으려고 하면 도달할 수 없는 코드 블록을 보고할 수도 있어요.

catch 구문은 코드 순서대로 실행이 돼요. 만약 catch(Excetpion e)가 맨 위에 있다면, 아래가 구체적이더라도 이 부분에서 에러를 잡게 됩니다.

즉, 문제는 예외와 일치하는 catch 구문에서 먼저 처리 돼요.

6️⃣ Don’t Catch Throwable

Throwable은 Exception 클래스의 부모 클래스에요. catch절에서 사용할 수는 있지만, 그렇게 해서는 안돼요.

catch 절에서 Throwable을 사용하면 모든 예외를 잡을 뿐만 아니라 또한 모든 오류를 포착하게 돼요.

7️⃣ Don’t Ignore Exceptions

예외를 무시하게 되면 나중에 겉잡을 수 없을 화를 가져올 수도 있어요. 발견하게 될 때 바로 예외 처리를 해야 해요.

8️⃣ Don’t Log and Throw

적절하게 처리할 수 있도록 예외가 발생했을 때 예외를 기록한 다음 다시 발생시키는 것이 직관적으로 느껴질 수 있어요. 하지만 동일한 예외에 여러 메시지가 발생하게 되고, 추가 메시지도 제공하지 않아요.

차라리 사용자 지정 예외를 활용하는 것이 좋아요. 하지만 이보다 9번을 활용하는 것이 더 좋아요.

9️⃣ Wrap the Exception Without Consuming It

때로는 표준 예외를 포착하더라도, 사용자 지정 예외를 호출하는 것이 더 좋을 수 있어요. 이를 통해 추가 정보도 넣을 수 있고, 특수한 처리도 가능해져요.

Exception handling strategies 정리

이러한 전략은 예외 처리를 더 효과적으로 관리하고 코드의 가독성과 유지보수성을 높일 수 있도록 도와줘요.

참고자료

자바의 신
Java의 예외 처리
9 Best Practices to Handle Exceptions in Java

profile
Don't ever say it's over if I'm breathing

0개의 댓글