[JAVA] 예외처리

오잉·2022년 11월 22일
0

JAVA

목록 보기
3/4

Exception의 종류

1) Checked Exception

  • 반드시 예외를 처리해야 함
  • 컴파일시 발생하는 예외
  • 트랜잭션을 rollback 하지 않음
  • 프로그램 작성시 이미 예측가능한 예외
  • Exception의 상속받는 하위 클래스 중 RuntimeException을 제외한 모든 예외 (IOException, SQLException)

2) Unchecked Exception

  • 명시적인 처리를 강제하지 않음
  • 실행시 발생하는 예외
  • 발생할 수도 안할 수도 있는 경우
  • 트랜잭션을 rollback 함
  • RuntimeException의 하위 예외 (NullPointerException, IllgalArgumentException, IndexOutOfBoundException, SystemException)

예외 처리 방법

1) 예외복구 (try-catch)

  • 예외가 발생하여도 애플리케이션은 정상적인 흐름으로 진행된다.
  • 예외 상황에서 프로그램의 실행을 종료하지 않고 예외처리 (예외 상황에서 벗어나도록 코드를 작성하거나 시스템을 망가트리지 않는 상황에서 자연스럽게 프로그램이 종료되고 디버깅 정보를 남기는 등의 처리)
class FoolException extends Exception {
}

public class Sample {
    public void sayNick(String nick) {
        **try** {
            if("fool".equals(nick)) {
                **throw new FoolException();**
            }
            System.out.println("당신의 별명은 "+nick+" 입니다.");
        } **catch(FoolException e)** {
            System.err.println("FoolException이 발생했습니다.");
        }
    }

    public static void main(String[] args) {
        Sample sample = new Sample();
        sample.sayNick("fool");
        sample.sayNick("genious");
    }
}
  • try : 예외가 발생할만한 코드
  • catch : 발생한 예외를 어떻게 처리할 건지 (여러개 있을 수 있음)
  • finally : 예외 발생 유무에 상관없이 무조건 수행되어야할 코드

2) 예외처리 회피 (throws)

  • 예외처리를 뒤로 미루기
  • 예외가 발생하면 throws를 통해 호출한쪽으로 예외를 던지고 그 처리를 회피 (호출부에서 이 예외를 처리해줘야함)
public class Sample {
    public void sayNick(String nick) **throws FoolException** {
            if("fool".equals(nick)) {
                **throw new FoolException();**
            }
            System.out.println("당신의 별명은 "+nick+" 입니다.");
    }

    public static void main(String[] args) {
        Sample sample = new Sample();
        **try** {
            sample.sayNick("fool");
            sample.sayNick("genious");
        }** catch (FoolException e) **{
            System.err.println("FoolException이 발생했습니다.");
        }
    }
}

=> 프로그램에서 Exception을 처리하는 위치는 중요하다. 프로그램의 수행여부를 결정하기도 하고 트랜잭션 처리와도 밀접한 관계가 있기 때문

3) 예외 전환

  • 예외를 잡아서 다른 예외를 던지는 것
  • 호출한 쪽에서 예외를 받아서 처리할 때 좀 더 명확하게 인지할 수 있도록 돕기 위한 방법 (어떤 예외인지 분명해야 처리가 수월해지기 때문)
  • ex) Checked Exception 중 복구가 불가능한 예외가 잡혔다면 이를 Unchecked Exception으로 전환하여서 다른 계층에서 일일이 예외를 선언할 필요가 없도록 할 수 있다.
catch(SQLException e) {
	...
    throw DuplicateUserIdException();
}

조심할 부분

예외를 잡고 아무런 처리도 하지 않는 것은 정말 위험하다.
try-catch로 예외를 잡아놓고 catch를 비워두면 컴파일 오류는 나지 않지만, 예외가 발생했을 때 그 원인을 파악하기 어려워 개발 및 유지보수에 좋지 않은 영향을 끼친다
-> 어떤 처리를 해야 하는지 모르더라도 무작정 catch하고 무시하거나 throw해버리는 행위는 신중해야 한다.

메소드를 정의할 때 메소드에서 발생할 수 있는 예외를 최대한 자세하게 명시하는 것이 좋다.
Exception 클래스로 퉁쳐버리면 이 메소드를 호출하는 쪽에서 예외처리하는 코드가 복잡해진다. Exception으로 받은 예외가 NumberFormatException인 경우도 있고, IlegalArugmentException, IOException 등 일 수도 있다. 따라서 모든 경우에 대한 코드를 작성할 수 밖에 없다.


+) 트랜잭션 (Transaction)

  • 트랜잭션 : 하나의 작업 단위
    ex) 쇼핑몰의 "상품발송"이라는 트랜잭션에는 포장/영수증발행/발송이 포함
상품발송() {
    포장();
    영수증발행();
    발송();
}
포장() {}
영수증발행() {}
발송() {}

-> 쇼핑몰의 운영자는 이 3가지 일들 중 하나라도 실패하면 3가지 모두 취소(롤백)하고 "상품발송" 전의 상태로 되돌리고 싶을 것이다 -> 예외처리 어떻게?

상품발송() {
    try {
        포장();
        영수증발행();
        발송();
    }catch(예외) {
        모두취소();  // 하나라도 실패하면 모두 취소한다.
    }
}
포장() throws 예외 {}
영수증발행() throws 예외 {}
발송() throws 예외 {}

이렇게 포장/영수증발행/발송 메서드에서는 예외를 throws하고(넘기고) 상품발송 메서드에서 throws된 예외들을 처리하여 모두 취소하는 것이 완벽한 트랜잭션 처리 방법
(포장, 영수증발행, 발송이라는 세개의 단위작업 중 하나라도 실패할 경우 "예외"가 발생되어 상품발송이 모두 취소 될 것)


<참고자료>

https://wikidocs.net/229
https://www.nextree.co.kr/p3239/
https://hbase.tistory.com/157

profile
오잉이라네 오잉이라네 오잉이라네 ~

0개의 댓글