본문 바로가기
이직&취업/Java 기초 상식

Checked Exception 와 Unchecked Exception

by journeylabs 2025. 3. 23.
728x90
반응형

목차

    Checked Exception 와 Unchecked Exception

    Java 개발자라면 반드시 이해해야 할 핵심 개념 중 하나가 바로 Checked Exception과 Unchecked Exception입니다. 이 두 종류의 예외는 프로그램의 안정성과 유지보수성에 큰 영향을 미치며, 적절한 예외 처리 전략을 수립하는 데 필수적인 요소입니다. 이 글에서는 Checked Exception과 Unchecked Exception의 차이점을 명확히 이해하고, 효과적인 예외 처리 방법을 익힐 수 있도록 상세한 내용을 제공합니다.

    1. 용어에 대한 상세 설명

    • Exception (예외): 프로그램 실행 중에 발생하는 비정상적인 상황을 의미합니다. 예외는 프로그램의 정상적인 흐름을 방해하며, 적절히 처리되지 않으면 프로그램이 비정상적으로 종료될 수 있습니다.
    • Checked Exception (체크 예외): 컴파일 시점에 예외 처리 여부를 검사하는 예외입니다. Checked Exception이 발생할 가능성이 있는 코드를 작성할 경우, 반드시 try-catch 블록으로 처리하거나 throws 구문을 사용하여 예외를 던져야 합니다.
    • Unchecked Exception (언체크 예외): 컴파일 시점에 예외 처리 여부를 검사하지 않는 예외입니다. Unchecked Exception은 주로 프로그램의 논리적인 오류로 인해 발생하며, 개발자의 부주의로 인해 발생하는 경우가 많습니다.

    2. 주요 개념 및 특징

    구분 Checked Exception Unchecked Exception
    정의 컴파일 시점에 예외 처리 여부를 검사하는 예외 컴파일 시점에 예외 처리 여부를 검사하지 않는 예외
    예외 처리 강제 여부 반드시 try-catch 블록 또는 throws 구문으로 처리해야 함 예외 처리가 강제되지 않음
    발생 원인 외부 환경의 변화, 시스템 오류 등 예측 가능한 예외 프로그램의 논리적인 오류, 개발자의 실수 등 예측 불가능한 예외
    주요 클래스 IOException, SQLException  RuntimeException, Error 
    목적 프로그램의 안정성을 높이고, 예외 발생 가능성을 명시적으로 알림 개발 편의성을 높이고, 예외 처리 코드를 간결하게 유지

    Checked Exception의 특징

    • 예외 처리 강제: Checked Exception은 컴파일러에 의해 예외 처리 여부가 검사되므로, 예외 처리 코드를 반드시 작성해야 합니다. 이는 개발자가 예외 발생 가능성을 인지하고, 적절한 대비를 하도록 유도합니다.
    • 명시적인 예외 처리: Checked Exception을 처리하기 위해서는 try-catch 블록을 사용하거나 throws 구문을 사용하여 예외를 던져야 합니다. 이는 코드의 가독성을 높이고, 예외 발생 경로를 명확하게 파악할 수 있도록 합니다.
    • 프로그램 안정성 향상: Checked Exception은 예외 처리 코드를 강제함으로써, 프로그램이 예외 상황에 적절하게 대응하도록 합니다. 이는 프로그램의 안정성을 높이고, 예기치 않은 오류로 인한 프로그램 종료를 방지합니다.

    Unchecked Exception의 특징

    • 예외 처리 선택: Unchecked Exception은 예외 처리가 강제되지 않으므로, 개발자는 필요에 따라 예외 처리 코드를 작성할 수 있습니다. 이는 코드의 간결성을 높이고, 불필요한 예외 처리 코드를 줄일 수 있도록 합니다.
    • 개발 편의성 향상: Unchecked Exception은 예외 처리가 강제되지 않으므로, 개발자는 예외 처리 부담을 줄이고, 개발 속도를 높일 수 있습니다.
    • 프로그램 논리 오류: Unchecked Exception은 주로 프로그램의 논리적인 오류로 인해 발생하므로, 개발자는 코드의 논리적인 오류를 수정하여 예외 발생을 방지해야 합니다.

    3. 상속 관계

    Java의 모든 예외 클래스는 Throwable 클래스를 상속받습니다. Throwable 클래스는 크게 Error 클래스와 Exception 클래스로 나뉩니다.

    • Error: 시스템 레벨에서 발생하는 심각한 오류를 나타냅니다. 일반적으로 복구 불가능한 오류이며, 프로그램에서 처리할 필요가 없습니다. (예: OutOfMemoryError, StackOverflowError)
    • Exception: 프로그램 실행 중에 발생할 수 있는 예외를 나타냅니다. Exception 클래스는 다시 IOException 등의 Checked Exception과 RuntimeException 등의 Unchecked Exception으로 나뉩니다.
    Object
      └ Throwable
          ├ Error
          │  └ ...
          └ Exception
              ├ IOException (Checked Exception)
              │  └ ...
              └ RuntimeException (Unchecked Exception)
                  ├ NullPointerException
                  ├ IllegalArgumentException
                  ├ ...

    3.1. Throwable 클래스: 예외 계층 구조의 최상위 클래스

    • 역할: Throwable은 Java 예외 계층 구조의 최상위 클래스입니다. 모든 예외 클래스는 직접 또는 간접적으로 Throwable 클래스를 상속받습니다. 즉, Throwable은 예외로 간주될 수 있는 모든 객체의 공통된 속성과 동작을 정의합니다.
    • 주요 메서드:
      • getMessage(): 예외에 대한 상세 메시지를 반환합니다.
      • printStackTrace(): 예외 발생 당시의 호출 스택 정보를 출력합니다 (디버깅에 유용).
      • getCause(): 예외를 발생시킨 원인이 되는 예외를 반환합니다 (예외 체이닝).

    3.2. Error 클래스: 시스템 레벨의 심각한 문제

    • 역할: Error 클래스는 시스템 레벨에서 발생하는 심각한 문제를 나타냅니다. 일반적으로 프로그램이 복구할 수 없는 상황을 의미하며, 개발자가 직접 처리하기보다는 시스템 자체의 문제로 간주됩니다.
    • 특징:
      • Error는 Unchecked Exception입니다. 즉, 컴파일러가 예외 처리 여부를 강제하지 않습니다.
      • 일반적으로 애플리케이션 코드에서 Error catch하는 것은 권장되지 않습니다.
    • 주요 하위 클래스:
      • OutOfMemoryError: JVM이 더 이상 메모리를 할당할 수 없을 때 발생합니다.
      • StackOverflowError: 메서드 호출 스택이 제한된 크기를 초과할 때 발생합니다 (주로 재귀 호출 문제).
      • NoClassDefFoundError: 클래스 파일이 존재하지 않을 때 발생합니다. (컴파일 시에는 존재했지만, 런타임 시에 찾을 수 없는 경우)

    3.3. Exception 클래스: 애플리케이션 레벨의 예외

    • 역할: Exception 클래스는 애플리케이션 실행 중에 발생할 수 있는 예외 상황을 나타냅니다. 개발자가 예외 처리 메커니즘을 통해 적절히 처리할 수 있는 예외를 의미합니다.
    • 특징:
      • Exception은 Checked Exception과 Unchecked Exception으로 나뉩니다.
      • 애플리케이션 코드에서 Exception catch하여 예외를 처리하거나, throws 구문을 사용하여 예외를 호출 스택 위로 던질 수 있습니다.

    3.4. RuntimeException 클래스: Unchecked Exception의 대표

    • 역할: RuntimeException 클래스는 Unchecked Exception의 대표적인 예외 클래스입니다. 주로 프로그램의 논리적인 오류나 잘못된 사용으로 인해 발생하는 예외를 나타냅니다.
    • 특징:
      • RuntimeException은 Unchecked Exception이므로, 컴파일러가 예외 처리 여부를 강제하지 않습니다.
      • 하지만, RuntimeException이 발생할 가능성이 있는 코드는 예외 처리 코드를 작성하여 프로그램의 안정성을 높이는 것이 좋습니다.
    • 주요 하위 클래스:
      • NullPointerException: 객체가 null인 상태에서 메서드를 호출하거나 필드에 접근하려고 할 때 발생합니다.
      • IllegalArgumentException: 메서드에 잘못된 인수를 전달했을 때 발생합니다.
      • IndexOutOfBoundsException: 배열이나 리스트의 인덱스가 유효 범위를 벗어났을 때 발생합니다.
      • ClassCastException: 객체를 잘못된 타입으로 캐스팅하려고 할 때 발생합니다.
      • ArithmeticException: 수학 연산에서 예외적인 상황이 발생했을 때 발생합니다 (예: 0으로 나누기).

    3.5. IOException 클래스: Checked Exception의 대표

    • 역할: IOException 클래스는 입출력 작업 중에 발생할 수 있는 예외를 나타냅니다. 파일 읽기/쓰기, 네트워크 통신 등과 관련된 예외가 여기에 속합니다.
    • 특징:
      • IOException은 Checked Exception이므로, 컴파일러가 예외 처리 여부를 강제합니다.
      • try-catch 블록 또는 throws 구문을 사용하여 IOException을 처리해야 합니다.
    • 주요 하위 클래스:
      • FileNotFoundException: 지정된 파일을 찾을 수 없을 때 발생합니다.
      • EOFException: 파일의 끝에 도달했을 때 발생합니다.
      • SocketException: 소켓 통신 중에 오류가 발생했을 때 발생합니다.

    3.6. 사용자 정의 예외 (Custom Exception)

    • 정의: 개발자는 필요에 따라 직접 예외 클래스를 정의할 수 있습니다.
    • 방법: Exception 클래스 또는 RuntimeException 클래스를 상속받아 새로운 예외 클래스를 만듭니다.
    • 장점:
      • 애플리케이션의 특정 상황에 맞는 예외를 정의하여 예외 처리 로직을 명확하게 구분할 수 있습니다.
      • 예외 메시지를 통해 예외 발생 원인을 상세하게 설명할 수 있습니다.
    // Checked Exception으로 사용자 정의 예외 생성
    class MyCheckedException extends Exception {
        public MyCheckedException(String message) {
            super(message);
        }
    }
    
    // Unchecked Exception으로 사용자 정의 예외 생성
    class MyUncheckedException extends RuntimeException {
        public MyUncheckedException(String message) {
            super(message);
        }
    }
    
    public class CustomExceptionExample {
        public static void main(String[] args) {
            try {
                if (/* 특정 조건 */ true) {
                    throw new MyCheckedException("Checked Exception 발생!");
                }
            } catch (MyCheckedException e) {
                System.err.println("MyCheckedException 처리: " + e.getMessage());
            }
    
            if (/* 특정 조건 */ true) {
                throw new MyUncheckedException("Unchecked Exception 발생!"); // 처리하지 않아도 컴파일 에러 X
            }
        }
    }

    3.7. 예외 체이닝 (Exception Chaining)

    • 정의: 예외가 발생한 원인을 다른 예외로 감싸서 전달하는 기법입니다.
    • 목적: 예외 발생 경로와 원인을 추적하기 쉽게 합니다.
    • 방법: 예외 객체를 생성할 때 원인이 되는 예외를 인수로 전달합니다.
    public class ExceptionChainingExample {
        public static void main(String[] args) {
            try {
                methodA();
            } catch (Exception e) {
                System.err.println("최상위 예외 처리: " + e.getMessage());
                System.err.println("원인 예외: " + e.getCause());
            }
        }
    
        static void methodA() throws Exception {
            try {
                methodB();
            } catch (IOException e) {
                throw new Exception("methodA에서 예외 발생", e); // Exception 체이닝
            }
        }
    
        static void methodB() throws IOException {
            throw new IOException("methodB에서 IOException 발생");
        }
    }
    • Throwable: 모든 예외의 최상위 클래스
    • Error: 복구 불가능한 시스템 레벨 오류
    • Exception: 애플리케이션 레벨에서 처리 가능한 예외
    • RuntimeException: Unchecked Exception (주로 논리 오류)
    • IOException: Checked Exception (입출력 관련 오류)
    • 사용자 정의 예외: 애플리케이션 특정 예외를 정의
    • 예외 체이닝: 예외 발생 원인 추적을 위한 기법

    4. 예제 및 예제 상세 설명

    Checked Exception 예제: IOException

    import java.io.BufferedReader;
    import java.io.FileReader;
    import java.io.IOException;
    
    public class CheckedExceptionExample {
        public static void main(String[] args) {
            BufferedReader br = null;
            try {
                br = new BufferedReader(new FileReader("test.txt"));
                String line = br.readLine();
                System.out.println(line);
            } catch (IOException e) {
                System.err.println("IOException 발생: " + e.getMessage());
            } finally {
                try {
                    if (br != null) {
                        br.close();
                    }
                } catch (IOException e) {
                    System.err.println("BufferedReader close 중 IOException 발생: " + e.getMessage());
                }
            }
        }
    }

    예제 설명:

    • 이 코드는 test.txt 파일을 읽어 첫 번째 줄을 출력하는 코드입니다.
    • FileReader 생성자 및 BufferedReader readLine() 메서드는 IOException을 발생시킬 수 있는 Checked Exception입니다.
    • 따라서 try-catch 블록을 사용하여 IOException을 처리하고 있습니다.
    • finally 블록에서는 BufferedReader를 닫아 리소스를 해제합니다. close() 메서드 또한 IOException을 발생시킬 수 있으므로, try-catch 블록으로 감싸줍니다.
    • 만약 try-catch 블록이 없다면, 컴파일 에러가 발생합니다.

    Unchecked Exception 예제: NullPointerException

    public class UncheckedExceptionExample {
        public static void main(String[] args) {
            String str = null;
            try {
                System.out.println(str.length()); // NullPointerException 발생 가능
            } catch (NullPointerException e) {
                System.err.println("NullPointerException 발생: " + e.getMessage());
            }
        }
    }

    예제 설명:

    • 이 코드는 str 변수가 null인 상태에서 length() 메서드를 호출하여 NullPointerException을 발생시키는 코드입니다.
    • NullPointerException은 Unchecked Exception이므로, try-catch 블록으로 처리하지 않아도 컴파일 에러가 발생하지 않습니다.
    • 하지만, 예외가 발생하면 프로그램이 비정상적으로 종료될 수 있으므로, try-catch 블록을 사용하여 예외를 처리하는 것이 좋습니다.
    • 이 예제에서는 try-catch 블록을 사용하여 NullPointerException을 처리하고, 예외 메시지를 출력합니다.

    throws 구문 사용 예제 (Checked Exception)

    import java.io.IOException;
    
    public class ThrowsExample {
        public static void readFile() throws IOException {
            // 파일 읽기 로직
            // IOException 발생 가능
            throw new IOException("파일을 읽는 중 오류가 발생했습니다.");
        }
    
        public static void main(String[] args) {
            try {
                readFile();
            } catch (IOException e) {
                System.err.println("IOException 발생: " + e.getMessage());
            }
        }
    }

    예제 설명:

    • readFile() 메서드는 IOException을 발생시킬 수 있으며, throws IOException 구문을 사용하여 예외를 던집니다.
    • main() 메서드에서는 readFile() 메서드를 호출하고, try-catch 블록을 사용하여 IOException을 처리합니다.
    • throws 구문을 사용하면, 메서드를 호출하는 쪽에서 예외 처리를 담당하게 됩니다.

    5. 주의 사항

    • Checked Exception 처리: Checked Exception은 반드시 try-catch 블록으로 처리하거나 throws 구문을 사용하여 예외를 던져야 합니다. 예외 처리를 소홀히 하면 컴파일 에러가 발생하므로 주의해야 합니다.
    • Unchecked Exception 처리: Unchecked Exception은 예외 처리가 강제되지 않지만, 발생 가능한 예외를 미리 예측하고 적절한 예외 처리 코드를 작성하는 것이 좋습니다.
    • 예외 처리 남용 방지: 모든 예외를 try-catch 블록으로 감싸는 것은 코드의 가독성을 떨어뜨리고, 성능 저하를 유발할 수 있습니다. 예외 처리는 필요한 부분에만 적용하고, 예외 처리 로직을 간결하게 유지하는 것이 좋습니다.
    • 예외 메시지: 예외 발생 시 예외 메시지를 적절하게 출력하여 디버깅에 활용할 수 있도록 합니다. 예외 메시지는 예외 발생 원인을 명확하게 설명하고, 문제 해결에 도움이 될 수 있도록 작성해야 합니다.
    • Custom Exception: 필요에 따라 Custom Exception을 정의하여 사용할 수 있습니다. Custom Exception을 사용하면, 예외의 의미를 명확하게 표현하고, 예외 처리 로직을 더욱 세분화할 수 있습니다.

    Checked Exception vs Unchecked Exception 선택 기준

    • 예측 가능성: 예외 발생 가능성을 예측할 수 있다면 Checked Exception을 사용하는 것이 좋습니다. Checked Exception은 예외 처리 코드를 강제함으로써, 프로그램의 안정성을 높일 수 있습니다.
    • 복구 가능성: 예외 발생 시 프로그램이 정상적으로 복구될 수 있다면 Unchecked Exception을 사용하는 것이 좋습니다. Unchecked Exception은 예외 처리 코드를 간결하게 유지하고, 개발 편의성을 높일 수 있습니다.
    • API 설계: API를 설계할 때, Checked Exception을 사용하면 API 사용자가 예외 발생 가능성을 인지하고, 적절한 예외 처리 코드를 작성하도록 유도할 수 있습니다.

    Checked Exception과 Unchecked Exception은 각각 장단점을 가지고 있으며, 상황에 따라 적절하게 선택하여 사용하는 것이 중요합니다. Checked Exception은 프로그램의 안정성을 높이는 데 도움이 되지만, 과도하게 사용하면 코드의 복잡성을 증가시킬 수 있습니다. Unchecked Exception은 개발 편의성을 높이는 데 도움이 되지만, 예외 처리를 소홀히 하면 프로그램의 안정성을 저해할 수 있습니다. 따라서, Checked Exception과 Unchecked Exception의 차이점을 명확히 이해하고, 적절한 예외 처리 전략을 수립하는 것이 중요합니다.

    728x90
    반응형

    '이직&취업 > Java 기초 상식' 카테고리의 다른 글

    Java Spring Bean의 생명주기(Lifecycle)  (7) 2025.03.24
    Java Collection Framework란?  (8) 2025.03.23
    Transaction 란?  (14) 2025.03.22
    SOLID 원칙  (4) 2025.03.22
    JAVA thread safe 란?  (12) 2025.03.21