[ Spring ] 자바예외 와 예외 처리

Wooju Kang ·2024년 9월 19일

[ FrameWork ] Spring

목록 보기
2/10
post-thumbnail

GIF 출처 : https://sigridjin.medium.com/spring-transaction-관리에-대한-메모-f391fd2885b4
그림 자료 출처 : 김영한 - Spring DB 1 / 2 자료

🖥 Contents


1 ) 자바의 예외 이해

2 ) 체크 예외 ( Check Exception ) 와 언체크 예외 ( Unchecked Exception )

3 ) 올바른 예외 사용





1 ) 자바의 예외 이해


  • 예외란?
    : 자바에서 프로그램 실행 중에 발생할 수 있는 오류 또는 비정상적인 상황을 처리하기 위한 메커니즘을 의미한다.
    자바의 예외는 크게 체크 예외 , 언체크 예외로 구분할 수 있다. 먼저 차상위 예외인 Exception과 Error에 대해 알아보자
    • Exception
      : 애플리케이션 로직에서 사용할 수 있는 실질적 최상위 예외
      -> 개발자가 다루는 예외들의 모음

    • Error
      : 메모리 부족이나 심각한 시스템 오류와 같이 애플리케이션 복구 불가능한 시스템 예외를 의하며 해당 예외는 개발자가 처리하려 하면 안된다.

기본적으로 예외에 대하여 catch 혹은 Throw의 행동을 취할 수 있다. 만약 예외가 발생한 상황에서 예외를 잡을 수 없다면 예외를 호출한 곳에 던지는 것이 기본적인 원칙이다.
예외를 처리하지 못할경우 마지막에 예외 로그를 출력하면서 시스템이 종료된다.





2 ) 체크 예외 ( Check Exception ) 와 언체크 예외 ( Unchecked Exception )


앞서 언급했듯이 개발자가 다루는 예외는 체크 예외와 언체크 예외가 존재한다. 그럼 둘의 특징과 차이점은 무엇인가?

  • 체크 예외 ( Checked Exception )
    : RuntimeException을 제외한 모든 Exception 하위 예외로 체크 예외는 ' 잡아서 처리하거나 ' , ' 밖으로 던지도록 선언 ' 해야 한다.

  • 언체크 예외 ( Unchecked Exception )
    : RuntimeException과 그 하위 예외를 의미하며 말 그대로 컴파일러가 예외를 체크하지 않는다. 즉 , 체크 예외와 다르게 명시적으로 코드를 통해서 잡아서 처리하지 않아도 된다.

다음은 체크 예외와 언체크 예외의 예시를 보여주는 코드이다.

  • 언체크 예외
package hello.jdbc.exception.basic;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

import java.net.ConnectException;
import java.sql.Connection;
import java.sql.SQLException;

public class CheckedAppTest {

    @Test
    void checked() {
        Controller controller = new Controller();
        Assertions.assertThatThrownBy(()->controller.request())
                .isInstanceOf(Exception.class);

    }
    static class Controller{
        Service service = new Service();

        public void request() throws SQLException,ConnectException{
            service.logic();
        }
    }

    static class Service{
        Repository repository = new Repository();
        NetworkClient networkClient = new NetworkClient();

        public void logic() throws SQLException,ConnectException{
            repository.call();
            networkClient.call();
        }

    }

    static class NetworkClient{
        public void call() throws ConnectException{
            throw new ConnectException();
        }

    }

    static class Repository {

        public void call() throws SQLException{
            throw new SQLException();
        }
    }
}

체크 예외의 경우 메소드에서 모든 예외에 대하여 throws가 선언이 되어있다. 만약 SQLException , ConnectionException 예외 처럼 체크 예외를 사용하는 경우에 던지거나 잡지 않을 경우에 컴파일 오류가 발생한다.

  • 언체크 예외
package hello.jdbc.exception.basic;

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

import java.net.ConnectException;
import java.sql.SQLException;

@Slf4j
public class UnCheckedAppTest {

    @Test
    void checked() {
        Controller controller = new Controller();
        Assertions.assertThatThrownBy(()->controller.request())
                .isInstanceOf(Exception.class);

    }

    @Test
    void printEx(){
        Controller controller = new Controller();
        try{
            controller.request();
        }catch(Exception e){
            log.info("ex",e);

        }
    }
    static class Controller{
        Service service = new Service();

        public void request(){
            service.logic();
        }
    }

    static class Service{
        Repository repository = new Repository();
        NetworkClient networkClient = new NetworkClient();

        public void logic() {
            repository.call();
            networkClient.call();
        }

    }

    static class NetworkClient{
        public void call(){
            throw new RuntimeConnectException("연결 실패");
        }

    }

    static class Repository {

        public void call(){
            try{
                runSQL();
            }catch(SQLException e){
                throw new RuntimeSQLException(e);
            }

        }

        public void runSQL()throws SQLException{
            throw new SQLException("ex");
        }
    }

    static class RuntimeConnectException extends RuntimeException{
        public RuntimeConnectException(String message){
            super(message);
        }
    }

    static class RuntimeSQLException extends RuntimeException{
        public RuntimeSQLException(Throwable cause){
            super(cause);
        }
    }


}

언체크 예외의 경우 던지지 않아도 컴파일 오류가 발생하지 않는다. 그럼 이런 의문이 들 수 있다. 예외는 좋지 않으니까 throws를 선언하는 체크 예외가 좋은게 아닐까?

그러나 앞서 언급했듯이 예외는 개발자가 처리할 수 없으며 발생하지 않아야한다. 또한 명시적으로 모든 예외를 던지거나 잡는 명시적인 코드는 여러 문제를 발생시킨다.

예를들어 JDBC 기술에서 JPA나 Mybatis 등 다른 기술로의 이전문제가 발생하는데 SQLException과 같은 JDBC 기술에서 사용하는 예외의 경우에도 변경이 필요하며 OCP 원칙에도 어긋나기 때문이다.



3 ) 올바른 예외 사용


앞서 언급한 바와 같이 체크 , 언체크 예외는 서로 다른 장-단점을 지니고 있기 때문에 하나의 예외만을 사용하는 것은 지양하는 것이 좋다. 그러므로 개발자는 다음과 같은 원칙을 준수해야한다.

  1. 기본적으로 언체크 ( 런타임 ) 예외를 사용한다.

  2. 체크 예외는 비지니스 로직상 의도적으로 던지는 예외에만 사용해야한다.
    • 예시

      계좌 이체 실패 예외 ( 트랜잭션 실패 )
      결제시 포인트 부족 예외
      로그인 ID , PW 불일치 예외





profile
배겐드 📡

0개의 댓글