3 분 소요

이번 포스트는 Exception Handling에 대해 다룬다. 이 포스트를 마지막으로 소프트웨어 공학 태그에 대한 포스트는 마무리 될 예정이다.

Exception Handling

Exception

  • Exception(예외) : 프로그램의 실행 중에 발생하여 정상적인 명령 흐름을 방해하는 사건

Exception Object

  • Exception이 발생했을 때 Exception Object가 만들어진다.
  • 이 객체는 에러의 타입과 에러 발생 당시에 프로그램의 상태 정보를 포함한다.

Exception Object를 생성하여 Runtime System에 넘기는 행위

=> Throwing an exception

Exception Handling의 장점

1. Separting Error-Handling Code from “Regular” Code

정상적인 코드 흐름오류 처리 코드분리할 수 있다.

  • 코드 가독성이 높아지고, 유지 보수하기에도 좋다.
  • 예전에는 예상 가능한 오류 상황들(파일이 열리지 않으면.? 파일 길이를 모르면? 메모리 할당이 안되면? 등)에 대한 모든 예외 케이스를 if 문 등으로 직접 처리하였기에 코드가 복잡했다.
    • 이는 정상적인 논리가 if 문 중첩으로 묻히게 된다. (구별하기가 어려워진다.)

“try - catch”문 사용

  • 정상 흐름인 코드 (try 블록)와 오류 처리 코드(catch 블록)가 명확히 분리된다.
  • 코드 가독성이 높아지고, 유지보수하기에도 유리하다.
  • 각 오류별 처리 코드도 독립적이고 명확하게 작성이 가능하다.

2. Propagating Errors Up the Call Stack

Exception은 호출 스택(Call Stack)을 따라서 전파가 가능하다.

현재 함수에서 처리하지 않으면 상위 호출 함수로 자동 전달된다.

  • 특정 함수에서 오류 처리 책임을 가지지 않아도 된다.
    • 상위의 호출자가 처리가 가능하다.

3. Grouping and Differentiating Error Types

Exception은 계층적 구조 (Class Hierarchy)로 설계가 가능하다.

  • 다양한 에러 타입이 구분이 가능하다.
  • 유사한 오류는 그룹으로 묶어서 처리가 가능하다.
  • 특수한 오류는 별도로 구분하여 처리가 가능하다.
  • Catch 구문에서 상위 타입과 하위 타입을 구분하여 처리가 가능하다.

Exception Etiquette

“Don’t catch an exception unless you know what to do with it!”

  • 예외는 그저 잡기만 하고 아무 의미 있는 처리를 하지 않는 다는 것은 좋지 않다는 뜻
  • 오류 발생 시 조용히 해결되면 오류의 존재조차 알 수 없다.
    • 프로그램 상태가 잘못된 상태로 계속 진행될 수 있다.

자신이 처리할 책임이 없다면 Throw로 상위에 전파하는 것이 좋다.

Exception-Handling Fundamentals

  • Java의 exception handling : try, catch, throw, throws, finally
  • C# : try, catch, throw, finally

Finally 문은 try문을 나가기 전에 관계없이 항상 수행한다.

  • Throwable Inheritance Hierarchy

Throwable Inheritance

Exception Types

Throwable

  • Java에서 모든 오류 / 예외 처리를 위한 최상위 클래스
  • 즉, catch 할 수 있는 건 전부 Throwable을 상속받은 클래스
  • Error, Exception 이라는 2개의 subclass를 가지고 있다.

1. Error

  • JVM이 발생시키는 시스템 레벨 오류
  • 보통 회복하기에 불가능하고, catch에서 처리하는 것이 바람직하지 않다.
  • 예시 ) 메모리 부족, 재귀 호출 폭주로 스택 초과, JVM 내부 오류

2. Exception

  • 애플리케이션 수준에서 발생하는 예외
  • 정상적인 코드 흐름에서 예외 상황이 발생할 수 있다. -> catch가 가능하다.

Exception 종류

1. Checked Exception

  • Well-written application이라면 예상하고 회복할 수 있어야 하는 예외
  • 이 상황은 올 수 있고, 그에 대한 처리를 해야 한다. => 강제로 처리해야한다.
  • 컴파일 시점에 Catch or Specify Requirement가 적용된다.
    • 반드시 try-catch문으로 처리하거나
    • throws로 상위로 명시적으로 전달해야 한다.
  • 정리
    • 의도 : 프로그램에서 처리 가능해야 하는 예외
    • 처리 강제 여부 : O (컴파일 시점에서는 반드시 처리해야 한다.)
    • 주 용도 : 외부 환경 요인

2. Error

  • 애플리케이션 외부 요인으로 발생한다.
  • 일반적으로 애플리케이션이 회복 불가능하다. => try-catch를 하지 않는다.

3. Runtime Exception (Unchecked Exception)

  • 애플리케이션의 내부 논리 오류로 발생한다.
  • 보통 프로그래밍 버그 (논리 오류 / 잘못된 API 사용) 등이 원인이다.
  • 주로 코드 자체 수정이 우선이다. => Catch보다는 버그 수정이 필요하다.
  • 따로 잡아야 한다기보다는 애초에 발생하지 않도록 설계하는 것이 중요하다.
  • 정리
    • 의도 : 프로그래밍 오류 -> 내부 논리 오류 발생 시
    • 처리 강제 여부 : X
    • 주 용도 : 버그 -> 프로그램 코드에서 수정 필요

Multiple catch Clauses

  • 하나의 try 블록 내에서 여러 가지 서로 다른 Exception이 발생이 가능하다.
    • 이를 각기 다른 catch 블록에서 처리가 가능하다.

하나의 try문에서 생길 수 있는 다양한 Exception에 대해서 catch 블록을 여러 개 나열하여 각각에 대해 다르게 처리

‘throw’ keyword

  • throw는 exception Object를 생성해서 강제로 발생시키는 키워드
  • try-catch문 없이 프로그래머가 의도적으로 특정 Exception을 발생시킬 때 사용된다.

처리 흐름 (throw 실행)

  1. 현재 실행중이던 메서드 중단
  2. Exception Object가 생성되어 호출 스택 상위로 전파
  3. 적절한 try-catch 블록에서 처리될 때까지 계속 전파
  4. 만약 어디서도 처리하지 않으면 JVM 까지 전파되어 프로그램의 비정상 종료

‘throws’ keyword

  • throws는 메서드 선언부에서 사용된다.
  • 해당 메서드가 어떤 Exception을 발생시킬 수 있는지를 명시한다.

처리 흐름(throws 실행)

  1. 메서드에서 Checked Exception 발생 가능
  2. 메서드 호출자는 try-catch로 처리하거나, 호출자도 다시 throws로 선언해서 상위로 넘긴다.
  3. 최종적으로 JVM까지 Exception 전파 시 처리되지 않으면 프로그램의 비정상 종료

‘finally’ keyword

  • finally는 try-catch-finally 구조에서 사용된다.
  • 목적 : 예외 발생 여부와 관계없이 항상 실행되는 블록을 의미한다.
    • 예외 발생 흐름과 무관하여 실행이 보장된다.