공부/whiteship-java

9주차: 예외 처리

chulphan 2021. 1. 16. 11:24

백기선님께서 주최하시는 자바 스터디

github.com/whiteship/live-study

 

whiteship/live-study

온라인 스터디. Contribute to whiteship/live-study development by creating an account on GitHub.

github.com

 

9주차 과제는 예외 처리이다.

에러가 났을 때 대처하는 방법인 예외 처리는 어느 언어에서나 중요한 것 같다

 

과제 내용:

 

▶ 자바에서 예외 처리 방법 (try, catch, throw, throws, finally)

  • try
    예외를 던질 수 있는 가능성을 가진 코드를 명시하는 곳. 여기서 예외가 발생하면 catch 절로 이동한다. 하지만 catch 나 finally 블록 없이도 사용할 수 있다

  • catch
    예외가 발생했을 때 프로그램에 의해 탐지되는 블록. 반드시 try 블록 다음에 명시되어야 한다. 필수적으로 명시되는 것은 아니다

  • throw
    예외를 명시적으로 일으킬 때 사용하는 키워드

  • throws
    예외를 일으킬 때 사용하는 키워드가 아니라, 예외를 '선언' 할 때에 사용하는 키워드. 이 키워드는 프로그램 또는 메소드에서 예외가 발생할 수 있음을 알리는 용도로 사용된다

  • finally
    프로그램이 정상적으로 실행 됐든 아니면 예외가 발생했든지 상관없이 항상 특정한 코드를 실행시켜야 하는 경우가 있다. 그러한 코드를 명시하는 곳이 finally 블록이며, 반드시 try-catch 블록의 다음에 위치해야 한다. 
    이 블록에 명시된 코드는 정상적인 실행이든 예외가 발생했든 반드시 실행된다.
    필수적으로 명시해줘야 하는 블록은 아니다

 

파일을 여는 프로그램을 통해서 위의 키워드를 이용해 파일에 접근하는 방법을 알아보자

아래 코드를 보자

public class Application {
    public static void main(String[] args) {
      Scanner scanner = null;
      try {
          File file = new File("/Users/chulkim/study/live_study_6week/src/com/example/week_6/input.txt");
          scanner = new Scanner(file);
          while (scanner.hasNextLine()) {
              String line = scanner.nextLine();
              System.out.println(line);
          }
      } catch (FileNotFoundException e) {
          e.printStackTrace();
      } finally {
          if (scanner != null) {
              scanner.close();
          }
      }
    }
}

try 문에서는 예외가 발생할 수 있는 가능성에 대한 코드를 명시한다. 여기서는 Scanner 객체가 파일을 찾지 못하는 예외가 발생할 수 있는 가능성이 있다.

 

파일을 찾지 못하는 예외가 발생할 수 있으므로 catch 문에는 FileNotFoundException 예외를 감지하여 처리할 수 있도록 한다. 여기서는 단순히 에러에 대한 메세지를 콘솔에 출력하는 작업을 한다.

 

scanner 변수에 객체가 할당 되어 있으면, 이 프로그램이 종료할 때에는 할당을 해제 해주어 GC가 이 자원을 수거해 갈 수 있도록 해줘야 한다. 이런 작업은 try 문에서의 작업이 정상적으로 수행되었든, 예외가 발생했든지에 상관없이 이루어져야 하기 때문에 finally 문에 scanner 변수를 할당 해제 해주는 작업을 수행하였다

 

※ try-with-resources

try-with-resources 는 하나 이상의 자원을 선언하는 try statement 이다. 또 try-with-resources 는 statement 의 끝에서 선언된 자원들에 대한 변수들이 할당 해제 됨을 보장한다. (JAVA7 이후)

 

그래서 위의 코드와 같은 작업을 하는 더 간단한 코드를 아래와 같이 작성할 수 있다.

 

public class Application {
    public static void main(String[] args) {
      File file = new File("/Users/chulkim/study/live_study_6week/src/com/example/week_6/input.txt");
      try (Scanner scanner = new Scanner(file);) {
          while (scanner.hasNextLine()) {
              String line = scanner.nextLine();
              System.out.println(line);
          }
      } catch (FileNotFoundException e) {
          e.printStackTrace();
      }
    }
}

 

위의 코드에서는 finally 블록 내에서 scanner 변수에 할당된 객체를 close 해줌으로써 할당을 해제해주었지만, try-with-resources 는 scanner에 할당된 변수를 자동으로 close 해주는 것을 보장해주어서 코드를 간단하게 만들어줄 뿐만 아니라 할당 해제에 대해 개발자들이 신경을 쓰고 있을 필요가 없어졌다

▶ 자바가 제공하는 예외 계층 구조

Java가 제공하는 예외 계층 구조는 대략적으로 아래의 그림과 같다

클래스 과제에서 배웠던 것처럼, 모든 클래스의 슈퍼 클래스는 Object 이다.

그리고 뒤에서 알아볼 Error 클래스와 Exception 클래스는 Throwable 을 상속 받아 구현되며

Excepion 클래스는 수 많은 서브 클래스들을 가진다. 더 많은 Exception 의 서브 클래스들은 docs.oracle.com/javase/7/docs/api/ 에서 볼 수 있다.

▶ Exception 과 Error 의 차이는?

먼저 Exception 의 정의에 대해서 알아보자

 

♣ Exception

Exception 클래스는 프로그램 혹은 외부 요소에 의해 발생한 에러를 표현한다. 즉, 애플리케이션 내부에서 일어난 오류에 대한 예외를 나타낸다. 그리고 Exception 클래스는 기본 생성자(아무 인자도 없는)와 문자열을 하나 받는 생성자를 제공하는데, 이 생성자는 에러 메세지를 나타낼 수 있는 문자열이다.

그리고 Exception 은 Checked Exception 과 Unchecked Exception 으로 나뉜다.

 

♣ Error

Error 클래스는 시스템이나 자원들에 관한 문제 때문에 프로그램이 종료되는 상황을 나타낸다

에러는 일반적으로 흔하지 않은 문제 또는 회복하기가 어려운 상황을 나타낸다. 이러한 문제는 프로그래머의 실수에 의해 나타나는 것은 아니며 시스템이 제대로 동작하지 않거나 자원이 제대로 할당되지 않았을 때 나타난다

 

예로는 AssertionError, IOError, LinkageError, VirtualMachineError 등이 있다

 

 

Exception 과 Error 의 차이는 애플리케이션 내에서 오류를 감지하여 처리할 수 있느냐 아니냐의 차이다

우리가 Exception 에 해당하는 예외를 미리 알 수 있다면 적절하게 처리하여(catch 문에서) 실행 중인 프로그램이나 애플리케이션의 동작이 멈추지 않도록 조절할 수 있을 것이다

 

▶ RuntimeException과 RE가 아닌 것의 차이는?

먼저, RuntimeException 과 RuntimeException 을 상속받은 모든 서브 클래스는 Unchecked Exception 으로 분류되며, RuntimeException 을 상속받지 않는 모든 서브 클래스는 Checked Exception 으로 분류된다. 

 

그러므로 Checked Exception 과 Unchecked Exception 에 대해 알아보면 이 주제에 대해서 알아보는 것과 같을 것 같다

 

그럼 Checked Exception 과 Unchecked Exception 에 대해서 알아보자

 

먼저 Checked Exception 의 특징은 다음과 같다

 

♣ Checked Exception

  • 컴파일 시 발생한다
  • 컴파일러는 checked exception 에 대해서 확인한다
  • 컴파일 시간에 조작 될 수 있다
  • JVM 은 이 예외에 대해서 감지하고 처리해주는 것이 필요하다

 

그 다음은 Unchecked Exception 에 대한 특징이다

 

♣ Unchecked Exception

  • 런타임에 발생한다
  • 컴파일러는 Unchecked Exception 타입들에 대해서는 확인하지 않는다
  • Unchecked Exception 들은 컴파일 시에 감지되거나 처리될 수 없다. 왜냐하면 프로그램 내에 실수로 인해서 발생된 것이기 때문에
    (즉, 개발자의 실수로 인해서 발생하는 에러이기 때문에)
  • JVM 은 Unchecked Exception 에 대한 예외에 대해서 개발자가 감지하거나 처리하는 것을 요구하지 않는다

즉, Checked Exception 과 Unchecked Exception 의 차이는 예외를 try-catch 등을 사용해서 처리해줘야하는지,

또 예외를 확인할 수 있는 시점이 컴파일 시인지 런타임인지에 따라 구분할 수 있다는 것이다

마지막으로 Unchecked Exception 은 예외가 발생할 시 트랜잭션을 roll-back 하는 반면에 Checked Exception 은 roll-back 을 하지 않고 예외를 던진다.

 

▶ 커스텀한 예외를 만드는 방법

♣ 커스텀한 예외를 사용하는 이유

Java 에는 FileNotFoundException, IOException 등과 같이 이미 정의되어 있는 예외가 존재한다

 

그럼에도 불구하고 사용자가 예외를 정의할 수 있는데, 우리가 정의한 예외를 사용하면 우리의 로직에 따라 예외를 발생시켜 무엇이 잘못 된 건지 더 명확하게 알 수 있도록 메세지를 제공할 수 있기 때문이다

 

♣ Checked Exception 커스텀 예외를 만드는 방법

예를 들어 우리가 장을 보고 있다고 가정하자.

이미 우유를 카트에 넣어놨는데, 까먹고 우유를 다시 집어 넣으려고 할 때 예외를 처리하고 싶다.

 

이 때는 사실 Java에 이미 정의된 IllegalArgumentException 으로 처리해도 되지만, 정확하게 우리가 어떤 물건을 카트에 집어넣었길래 에러가 나는지 확실하게 알 수 없다

 

조금 더 정확하게 에러 내용을 알 수 있게 하기 위해서 ItemAlreadyExistException 이라는 예외를 정의해서 사용할 것이다

 

위에서 살펴봤듯이 Checked Exception 타입의 모든 예외는 Exception 을 상속 받아 사용해야한다

 

그리고 CartService 클래스를 정의하고 메소드를 정의할 때 throws 를 통해 ItemAlreadyExistException 이 발생할 수 있음을 명시한다

이제 실제 애플리케이션에서 milk 를 넣으려고 하면 예외가 발생한다

우리가 직접 정의한 예외를 사용함으로 인해서 서비스 로직에서 어떤 것이 잘못됐는지 더 명확하게 알 수 있게 됐다

 

 

♣ Unchecked Exception 커스텀 예외를 만드는 방법

위의 예제는 정확하게 잘 동작한다.

그런데 오늘 집에 오는 손님 중에 땅콩 알레르기가 있어서, 땅콩을 사가면 안되는데 카트에 추가했다고 생각해보자.

이 때는 카트에 담은 물건 자체가 잘못되었다는 예외를 던져주려고 한다.

 

먼저, Unchecked Exception 은 RuntimeException 을 상속 받아 사용한다

그리고 땅콩을 받았는지 체크하는 메소드를 정의한다

이제 유효성을 체크하는 메소드를 정의하고, 위에 itemInvalid가 false 를 반환할 경우 예외를 던진다

※ checkValidate 는 따로 throws 를 통해 예외가 발생할 수 있음을 명시하지 않았다. ItemInvalidException 예외는 RuntimeException 을 상속 받아 사용하고 있고 Unchecked Exception 이기 때문에 컴파일러에서 이 예외에 대해서는 감지하지 않기 때문이다

 

이제 코드를 실행하면 똑같이 예외가 발생한다.

 

[참고 사이트]

https://www.softwaretestinghelp.com/try-catch-finally-and-throw-in-java/

https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html

https://www.scientecheasy.com/2020/08/exception-hierarchy-in-java.html/ 

https://docs.oracle.com/javase/7/docs/api/ 

https://www.geeksforgeeks.org/checked-vs-unchecked-exceptions-in-java/ 

https://www.nextree.co.kr/p3239/ 

https://www.codejava.net/java-core/exception/how-to-create-custom-exceptions-in-java 

https://www.baeldung.com/java-new-custom-exception 

https://stackabuse.com/how-to-make-custom-exceptions-in-java/ 

'공부 > whiteship-java' 카테고리의 다른 글

11주차: enum  (0) 2021.01.30
10주차: 멀티쓰레드 프로그래밍  (0) 2021.01.22
8주차: 인터페이스  (0) 2021.01.08
7주차: 패키지  (0) 2021.01.01
6주차: 상속  (0) 2020.12.24