프로그래밍 언어/JAVA

[JAVA] 자바 예외처리 이해하기

s워니얌 2023. 2. 15. 12:57

 

 

📑 자바 예외 계층

 

 

🎈 Object : 예외도 객체이다. 모든 객체의 최상위 부모는 Object

 

🎈 Throwable : 최상위 예외이다. 하위에 Exception과 Error가 있다.

 

🎈 Error : 메모리 부족이나 심각한 시스템 오류와 같이 애플리케이션에서 복구 불가능한 시스템 예외이다. 애플리케이션 개발자는 이 예외를 잡으려고 해서는 안된다. 

상위 예외를 catch로 잡으면 그 하위 예외까지 함께 잡는다. 따라서 애플리케이션 로직에서는 Throwable로 잡으면 안되는데, 앞서 이야기한 Error 예외도 잡을 수 있기 때문이다. 애플리케이션 로직은 이런 이유로 Exception 부터 필요한 예외로 생각하고 잡으면 된다. 

 

🎈 Exception : 체크 예외

애플리케이션 로직에 사용할 수 있는 실질적인 최상위 예외이다.

Exception과 그 하위 예외는 모두 컴파일러가 체크하는 체크 예외이다. 단 RuntimeException은 예외로 한다.

 

🎈 RuntimeException : 언체크 예외, 런타임 예외

컴파일러가 체크하지 않는 언체크 예외이다. RuntimeException 이름에 따라서 RuntimeException과 그 하위 언체크 예외를 런타임 에러라고 많이 부른다. 

 

 

 


 

📑 예외 처리 기본 규칙

 

예외는 잡아서 처리하거나, 처리할 수 없으면 밖으로 던져야한다. 

 

 

 

📑 체크 예외 예제 

 

 

 

 

위의 코드를 살펴보면 예외를 던지고 있다. 이렇게 던질때는 extends로 밖으로 던지는 걸 선언해야한다. 아님 빨간줄 뜬다. 

 

 

 

아래 코드는 예외를 잡아서 try~catch로 처리하는 것. 

 

 

 

 


 

예외를 던지는 테스트를 진행할때 이렇게 아래 코드처럼 짜게 되면 예외가 발생했을 때 에러를 터뜨리기 때문에 아래 주석처리한 코드처럼 만약 에러가 발생하면 에러 대신에 MyCheckedException.class를 호출해줘라는 코드로 작성해야 한다. 

 

 

 

 

package hello.jdbc.exception.basic;

import lombok.extern.slf4j.Slf4j;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

@Slf4j
public class CheckedTest {


    @Test
    void checked_catch(){
        Service service = new Service();
        service.callCatch();
    }

    @Test
    void checked_throw(){
        Service service = new Service();
        Assertions.assertThatThrownBy(()->service.callThrow()).isInstanceOf(MyCheckedException.class);
    }


    /**
     * Exception을 상속받은 예외는 체크 예외가 된다.
     */
    static class MyCheckedException extends Exception{
        //컴파일러가 체크하는 체크 예외

        public MyCheckedException(String message) {
            super(message);
        }
    }

    /**
     * checked 예외는
     * 예외를 잡아서 처리하거나, 던지거나 둘중 하나를 필수로 선택해야한다.
     */
    static class Service {
        Repository repository = new Repository();
        /**
         * 예외를 잡아서 처리하는 코드
         * */
        public void callCatch()  {
            try {
                repository.call();
            } catch (MyCheckedException e) {
                log.info("예외 처리, message={}", e.getMessage(),e);
            }

        }

        /**
         * 체크 예외를 밖으로 던지는 코드
         * 체크 예외는 예외를 잡지 않고 밖으로 던지려면 throws 예외를 메서드에  필수로 선언해야한다.
         * @throws MyCheckedException
         */
        public void callThrow() throws MyCheckedException {
            repository.call();
        }
    }

    static class Repository{
        public void call() throws MyCheckedException {
            //exception을 터뜨리는다.
            throw new MyCheckedException("ex"); //던지려면 무조건 throws로 선언을 해야된다.
        }
    }
}

 

 

 

 

 

 

📑 언체크 예외 예제

 

언체크 예외는 체크 예외와 기본적으로 동일하다. 차이가 있다면 예외를 던지는 throws를 선언하지 않고, 생략할 수 있다는 것! 이 경우 자동으로 예외를 던진다. 

 

package hello.jdbc.exception.basic;

import lombok.extern.slf4j.Slf4j;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

@Slf4j
public class UncheckedTest {


    @Test
    void unchecked_catch(){
        Service service = new Service();
        service.callCatch();
    }

    @Test
    void unchecked_throw(){
        Service service = new Service();
        Assertions.assertThatThrownBy(()->service.callThrow()).isInstanceOf(MyUncheckedException.class);

    }



    /**
     * RuntimeException을 상속받은 예외는 언체크 예외가 된다.
     */
    static class MyUncheckedException extends  RuntimeException{
        public MyUncheckedException(String message) {
            super(message);
        }
    }

    /**
     * Uncehced 예외는 예외를 잡거나, 던지지 않아도 된다.
     * 예외를 잡지 않으면 자동으로 밖으로 던진다.
     */
    static class Service{
        Repository repository = new Repository();

        /**
         * 필요한 경우 예외를 잡아서 처리하면 된다.
         */
        public void callCatch(){
            try{
                repository.call();
            }catch (MyUncheckedException e){
                log.info("예외 처리, message={}", e.getMessage(),e);
            }
        }

        /**
         * 예외를 잡지 않아도 된다. 자연스럽게 상위로 넘어간다.
         * 체크 예외와 다르게 throws 예외 선언을 하지 않아도 된다.
         */
        public void callThrow(){
            repository.call();
        }
    }



    static class Repository{
        public void call(){
            throw new MyUncheckedException("ex");
        }
    }
}

 

 

 

📑 체크 VS 언체크 언제 사용할까? 

 

🎈 기본적으로 언체크(런타임) 예외를 사용하자

 

🎈 체크 예외는 비즈니스 로직상 의도적으로 던지는 예외만 사용하자. 이 경우 해당 예외를 잡아서 반드시 처리해야 하는 문제일 때만 체크 예외를 사용한다.  (ex. 계좌 이체 실패 예외, 결제시 포인트 부족 예외, 로그인 ID, PW 불일치 예외) 

 

 

 

 

반응형