[JAVA] Exception 정리하기

2022. 11. 10. 00:30몰랐던거/JAVA & SPRING

정리의 계기

자바를 공부한 지 얼마 되지 않아서 Exception을 잘 처리하는 개발자가 좋은 개발자라는 것을 들어왔지만 몸으로 체감할 수 없었다. 하지만 최근 교육을 들으면서 과제에서 Exception을 다루는 일이 많아졌는데 어떻게 예외처리하는 것이 좋은 것이고 맞는 걸까라는 생각이 들었다.

OOP에 관한 얘기를 들었을 때 Exception으로 인해 캡슐화가 깨지는 경우도 있다는 얘기를 들었고 이 시기에 하루에 하나씩 올라오는 면접 질문으로 checked, unchecked Exception에 대해서 설명하라는 질문이 나와 정리해보려고 한다.

  1. Error와 Exception의 차이
  1. checked, unchecked Exception
  1. Exception을 잘 처리하는 방법

이런 부분에 대해서 알아보겠다.

1️⃣Error vs Exception

[그림 1] Error vs Exception

먼저 Error와 Exception에 대해서 확실히 알아보고 다음을 알아보자.

Error : 부정확하거나 잘못된 행위, 시스템에 발생된 비정상적인 상황

Exception : 의도적인 누락 행위, Error와 다르게 시스템 레벨이 아닌 개발자가 구현한 로직에서 발생하는 상황

즉, Exception은 발생 상황을 미리 예측해서 처리가 가능함.

Error잡거나 처리할 수 없고 심각한 문제를 나타낸다. 런타임에 발생하고 주로 시스템 리소스 부족으로 발생한다. ex) OutOfMemoryError, AssertionError

Exception은 Exception Event의 약어이다. 이는 프로그램 실행 중 발생하여 프로그램 명령의 정상적인 흐름을 방해하는 event라는 의미이다. ( 컴파일, 런타임 중 ) 이는 try-catch, throw 키워드로 복구가 가능하다. ex) IOException, NullPointException

2️⃣Checked Exception vs Unchecked Exception

[그림 2] checked exception vs unchecked exception

[그림 2]에서 보이는 것처럼 Exception을 상속받는 Exception들 중 RuntimeException을 기준으로 RuntimeException을 상속한 Exception들을 Unchecked Exception, 이를 상속하지 않는 Exception을 Checked Exception이라고 한다.

Checked Exception

: 컴파일 시점에 예외처리 여부를 확인한다. 발생 가능성이 있으면 꼭 예외처리를 해야한다. (try-catch, throw) ex) FileNotFoundException, IOException

Unchecked Exception

: 런타임 시점에 예외처리 여부를 확인한다. 위와 달리 예외처리를 강제하지 않는다. ex) ArryaIndexOutOfBoundsException, NullPointerException

⇒ 강제하지 않는 이유는 대부분 개발자의 실수로 발생하는 예외이기에 에러를 강제하지 않는 것이다. ex) 배열 사용 중 배열 길이를 넘어가는 인덱스로 접근함.

이보다 더 중요한 차이점은 Rollback 여부이다.

Checked Exception은 Rollback이 되지 않고 Commit이 된다.

→ 이렇게 되면 큰 문제가 발생한다. 물건의 주문이 되지 않았는데 결제만 되는 그런 상황이 발생할 수 있다.

Unchecked Exception은 Rollback이 가능하다.

⇒ 일반적 실행 시 예외처리가 가능한 경우 RuntimeException 클래스를 확장해 Unchecked Exception을 사용하는 것이 좋다.

3️⃣Exception을 잘 처리하는 방법

  • 예외 처리 방법
    [그림 3] 예외 처리 방법

    (1). 예외 복구

    [그림 4] 예외 복구

    이것의 핵심은 예외가 발생해도 정상적인 흐름으로 진행된다는 것이다.

    [그림 4]는 재시도를 통해서 예외를 복구하는 코드이고 네트워크가 불안정해서 재접속을 시도하는 상황을 연상하면 좋을 것 같다. 이때 재시도를 통해 정상적 흐름을 타도록 하거나 예외 발생 시 다른 흐름으로 유도시키도록 구현하면 정상적으로 작업 종료가 가능하다.

    (2). 예외처리 회피

    이는 예외가 발생하면 throws 키워드를 통해 호출한 곳으로 예외를 던지면서 회피하는 방법이다. 무책임한 회피는 위험하고 호출한 곳에서 예외처리를 하거나, 해당 메서드로 예외를 던지는 것이 최선의 방법인 경우 사용해야한다.

    (3). 예외전환

    이는 예외를 잡아서 다른 예외를 던지는 것이다. Checked Exception이 잡혔다면 이를 Unchecked Exception으로 바꿔서 던짐으로써 다른 계층에서 일일이 예외를 처리할 필요가 없게 만드는 방법이다.

이제는 예외를 처리하는 추가적인 방법 및 팁들에 대해서 알아보겠다.

  1. 예외 전파, 예외 체이닝

    예외가 다른 계층으로 전달될 때, 이전 예외를 원인으로 가지는 새로운 예외를 던지는 것을 예외 전파라고 한다. 정말 간단하게 예외 전파를 생각하면 특정 메서드에서 예외를 던지고 이를 호출한 측에서 예외를 처리하는 것이다.

    이를 이용한 예외 체이닝이란 방법이 존재한다. 예외가 전달될 때마다 새로운 예외에 포함시켜 다시 던지는 과정이다. 이게 유용한 경우는 0으로 나누려는 시도로 ArithmethicException을 throw하는 메서드를 생각해보자. 사실은 이것의 실제 원인은 나누는 수를 0으로 설정함으로써 일어난 IOException이였던 것이다. 하지만 메서드에선 ArithmeticException을 던질 것이고 IOException에 대해선 모르게 되는 것이다. 이런 경우 예외 체이닝을 사용한다.

    [그림 5] Throwable 클래스에서 연결 예외를 지원하는 생성자 및 메서드

    [그림 5]는 Throwable 클래스에서 예외 체이닝을 지원하는 생성자와 메서드에 관한 정보이다. 이를 이용해서 예를 작성해보자.

    public class MyChainedException {
    
        public void main(String[] args) {
            try {
                throw new ArithmeticException("Top Level Exception.") // 현재 발생한 예외 설정
                  .initCause(new IOException("IO cause.")); // Exception의 근본적 원인 설정
            } catch(ArithmeticException ae) {
                System.out.println("Caught : " + ae); // 현재 원인이 출력됨.
                System.out.println("Actual cause: "+ ae.getCause()); // 근본적인 원인이 출력됨.
            }
        }    
    }
    
    // 결과
    Caught: java.lang.ArithmeticException: Top Level Exception.
    Actual cause: java.io.IOException: IO cause.

    예외 체이닝을 사용해야하는 이유

    로그를 찍어서 정보를 얻을 때 더 깨끗하고 버그를 찾기가 쉽다는 장점이 있음.

  1. 예외는 진짜 예외인 경우에만 사용해야 함.

    예외를 잡아서 이를 다른 로직을 수행하기 위함으로 사용하면 안된다. 예를 들어, if-else처럼 ArithmeticException이 발생하지 않으면 나눈 값을 반환하고 발생하면 더한 값을 반환하는 것처럼 분기를 나누기 위하여 사용하면 안된다.

  1. catch 블록에서 예외를 삼키면 안된다.

    즉, catch에서 예외를 처리 후 다시 던지는 것과 같은 예외처리가 아닌 return null; 와 같이 삼킴으로써 오류의 원인을 잃어버리게 된다.

  1. Stack Trace가 손실되지 않도록 custom exception에서 exception을 올바르게 래핑해야한다.
    [그림 6] 올바르게 래핑하는 방법

    위가 아닌 아래와 같은 방법으로 stack trace를 가져갈 수 있게 래핑해야한다.

이외의 많은 Java 예외 처리 모법 사례들이 아래의 링크에 존재한다. 한번 읽어보고 따라하는 것을 권장한다.

Top 20 Java Exception Handling Best Practices
This post is another addition in best practices series available in this blog. In this post, I am covering some well-known and some little known practices which you must consider while handling exceptions in your next java programming assignment. Follow this link to read more about exception handling in java.
https://howtodoinjava.com/best-practices/java-exception-handling-best-practices/

Reference

  • 예외 체이닝 관련
https://www.baeldung.com/java-chained-exceptions
Understanding Java Exception Chaining with Code Examples
This Java tutorial helps you understand the concept of exception chaining (or exception wrapping, exception propagation) and apply it to your Java daily coding. Basically, exception chaining is the process of re-throwing multiple exceptions across different abstraction layers of a program.
https://www.codejava.net/java-core/exception/understanding-java-exception-chaining-with-code-examples
  • Error vs Exception 관련
Exception Vs Error in Java - Javatpoint
The general meaning of exception is a deliberate act of omission while the meaning of error is an action that is inaccurate or incorrect. In Java, Exception, and Error both are subclasses of the Java Throwable class that belongs to java.lang package. But there exist some significant differences between them.
https://www.javatpoint.com/exception-vs-error-in-java
Java 예외(Exception) 처리에 대한 작은 생각
일상생활에서도 기본적인 것은 고민하지 않고 습관처럼 사용하는 경우가 있다. 초급 개발자인 나에게 '예외(Exception)'이 바로 그런 것이었다. 처음 JAVA수업 때 강사님께 "왜 로직을 try문으로 감싸고, 또 catch(e)는 무엇인가요?"라는 질문을 한 적이 있다. 돌아온 대답은 "이렇게 안하면 에러가 나니까."였다. 나는 이것을 안 하면 어떤 일이 벌어지는지
https://www.nextree.co.kr/p3239/
  • 예외 처리 방법 및 팁
Top 20 Java Exception Handling Best Practices
This post is another addition in best practices series available in this blog. In this post, I am covering some well-known and some little known practices which you must consider while handling exceptions in your next java programming assignment. Follow this link to read more about exception handling in java.
https://howtodoinjava.com/best-practices/java-exception-handling-best-practices/
당신의 Checked Exception은 필요 없다
백엔드 API를 만들며 사용자의 잘못된 입력을 Checked 예외, Unchecked 예외 중 어떤 예외로 던질지 고민하면서 처음 생각해보게 됐다. 정의들을 찾아보니 회복 가능성 / 발생 기대성 / 코드 내외부의 문제 여부를 보통 언급하던데 명확하게 이해가 돼진 않았다. 보통 Unchecked로 많이 처리하길래 왜 그러냐고 주변에 같이 공부하는 사람들에게 물어봤을 때 대답은 크게 3가지 였다.
https://velog.io/@sangmin7648/%EB%8B%B9%EC%8B%A0%EC%9D%98-Checked-Exception%EC%9D%80-%ED%95%84%EC%9A%94-%EC%97%86%EB%8B%A4

Uploaded by N2T

'몰랐던거 > JAVA & SPRING' 카테고리의 다른 글

[OOP] Visitor 패턴 정리하기  (0) 2022.12.09
[OOP] Command 패턴 정리하기  (0) 2022.12.09
[OOP] Observer 패턴 정리하기  (0) 2022.11.12
[JAVA] JAVA에서의 Data Type  (0) 2022.11.12
[OOP] Proxy 패턴 정리하기  (0) 2022.11.10