UnisquadsU
로그인
홈
이용약관·개인정보처리방침·콘텐츠정책·© 2026 Unisquads
이용약관·개인정보처리방침·콘텐츠정책
© 2026 Unisquads. All rights reserved.

예외 처리 (r1)

이 문서의 과거 버전 (r1)을 보고 있습니다. 수정일: 2026.02.26 03:43

예외 처리

정의

프로그램 실행 중 발생하는 예상치 못한 오류나 비정상적인 상황을 처리하는 메커니즘

주요 용도

프로그램의 비정상 종료 방지

에러 발생 시 사용자에게 적절한 메시지 제공

리소스 정리(예: 파일 닫기, 메모리 해제) 보장

관련 분야

소프트웨어 공학

프로그래밍 언어

핵심 구성 요소

예외 발생(Throw/Raise)

예외 처리(Catch/Except/Rescue)

예외 전파

일반적 처리 흐름

1. 예외 발생

2. 현재 실행 중인 코드 블록에서 처리기 탐색

3. 처리기를 찾지 못하면 호출 스택을 따라 상위로 전파

4. 적절한 처리기에서 예외 처리 실행

5. 처리 후 프로그램 정상 흐름 복구 또는 종료

상세 정보

예외의 종류

검사 예외(Checked Exception): 컴파일 타임에 처리 강제

비검사 예외(Unchecked Exception/Runtime Exception): 컴파일 타임에 처리 강제되지 않음

에러(Error): 복구 불가능한 심각한 시스템 문제

주요 프로그래밍 언어별 키워드

Java, C#: try, catch, finally, throw, throws

Python: try, except, else, finally, raise

JavaScript: try, catch, finally, throw

장점

오류 처리 코드와 정상 로직의 분리로 가독성 향상

에러 전파가 명시적이고 제어 가능

프로그램의 안정성과 견고성 향상

단점

과도한 사용 시 성능 저하 가능성

처리되지 않은 예외로 인한 프로그램 중단 가능

잘못된 예외 처리로 인한 리소스 누수 가능

예외 처리 시 주의사항

가능한 낮은 수준에서 예외 처리

구체적인 예외 타입으로 캐치

예외를 무시하거나 포괄적으로 캐치하지 않기

finally 블록을 이용한 필수 정리 작업 수행

예외 메시지는 명확하고 유익하게 작성

대안적 오류 처리 방식

오류 코드 반환

어설션(Assertion)

옵셔널 타입(Optional Type) 사용

모나딕 에러 처리(함수형 프로그래밍)

1. 개요

예외 처리는 프로그램 실행 중 발생하는 예상치 못한 오류나 비정상적인 상황을 처리하는 메커니즘이다. 이는 소프트웨어 공학과 프로그래밍 언어 설계에서 신뢰성 있는 소프트웨어를 구축하기 위한 핵심 기법 중 하나로 자리 잡았다.

예외 처리의 주요 목적은 프로그램의 비정상 종료를 방지하고, 에러 발생 시 사용자에게 적절한 메시지를 제공하며, 파일 닫기나 메모리 해제와 같은 리소스 정리를 보장하는 데 있다. 이를 통해 프로그램이 예측 불가능한 조건 하에서도 견고하게 동작하도록 돕는다.

예외 처리의 일반적인 흐름은 다음과 같다. 먼저 예외가 발생하면, 현재 실행 중인 코드 블록에서 해당 예외를 처리할 수 있는 핸들러를 탐색한다. 적절한 핸들러를 찾지 못하면, 예외는 호출 스택을 따라 상위로 전파된다. 최종적으로 적절한 처리기에서 예외를 처리한 후, 프로그램은 정상 흐름으로 복귀하거나 안전하게 종료된다.

이 메커니즘의 핵심 구성 요소는 예외를 발생시키는 Throw 또는 Raise, 예외를 포착하여 처리하는 Catch 또는 Except, 그리고 예외를 상위 함수나 메서드로 넘기는 예외 전파이다. 이러한 구조는 Java, Python, C++ 등 현대의 주요 프로그래밍 언어에서 공통적으로 구현되어 있다.

2. 예외 처리의 목적

예외 처리의 주요 목적은 프로그램의 견고성과 신뢰성을 높이는 데 있다. 프로그램 실행 중에는 사용자의 잘못된 입력, 네트워크 연결 실패, 파일 접근 오류, 메모리 부족 등 다양한 예상치 못한 오류 상황이 발생할 수 있다. 이러한 비정상적인 상황을 적절히 관리하지 않으면 프로그램이 갑작스럽게 중단되어 사용자 경험을 해치고, 처리되지 않은 데이터나 열린 리소스가 시스템에 남아 문제를 일으킬 수 있다. 따라서 예외 처리는 이러한 오류를 감지하고, 통제된 방식으로 대응함으로써 프로그램의 안정성을 보장하는 핵심 기법이다.

구체적인 목적 중 하나는 프로그램의 비정상 종료를 방지하는 것이다. 예외가 발생했을 때 이를 적절히 포착하여 처리하면, 프로그램이 오류 메시지를 출력하고 종료되는 대신 다른 대체 작업을 수행하거나, 사용자에게 상황을 알리고 정상적인 상태로 복귀할 수 있다. 예를 들어, 파일을 읽는 도중 파일이 없어도 프로그램 전체가 중단되지 않고, 사용자에게 "파일을 찾을 수 없습니다"라는 메시지를 보여주고 다시 시도할 기회를 제공할 수 있다.

또 다른 중요한 목적은 리소스 관리를 보장하는 것이다. 데이터베이스 연결, 파일 핸들, 네트워크 소켓과 같은 시스템 리소스는 사용 후 반드시 정리되어야 한다. 예외가 발생하면 프로그램 흐름이 갑자기 중단되어 이러한 정리 코드가 실행되지 않을 위험이 있다. 많은 프로그래밍 언어의 예외 처리 메커니즘은 finally 블록이나 자원 자동 관리 구문을 제공하여, 예외 발생 여부와 관계없이 반드시 실행되어야 하는 정리 코드를 안전하게 실행하도록 돕는다. 이를 통해 메모리 누수나 리소스 고갈을 방지할 수 있다.

마지막으로, 예외 처리는 오류의 원인을 명확히 진단하고 전달하는 데 목적이 있다. 예외 객체는 일반적으로 오류의 유형, 발생 위치, 상세 메시지 등의 정보를 담고 있다. 이 정보는 개발자가 프로그램을 디버깅하거나, 시스템 로그를 통해 문제를 분석하는 데 유용하게 활용된다. 또한, 사용자에게는 기술적 세부 사항 대신 이해하기 쉬운 안내 메시지를 제공함으로써 더 나은 사용자 경험을 제공할 수 있다.

3. 예외 처리 메커니즘

3.1. 예외 발생 (Throw/Raise)

예외 발생(Throw/Raise)은 예외 처리 메커니즘의 첫 번째 단계로, 프로그램 실행 중 오류나 예외적인 조건이 감지되었을 때 해당 상황을 공식적으로 알리는 행위이다. 개발자가 의도적으로 특정 조건에서 예외를 발생시킬 수도 있고, 런타임 환경이나 운영체제가 자동으로 예외를 발생시키는 경우도 있다. 이 과정은 일반적으로 throw(자바, C++, 자바스크립트), raise(파이썬), 또는 이에 상응하는 키워드를 사용하여 수행된다.

예외를 발생시킬 때는 보통 예외 객체를 생성하여 던진다. 이 객체는 발생한 예외의 유형(예: 파일을 찾을 수 없음, 0으로 나누기, 네트워크 연결 실패)과 관련된 상세 정보(예: 오류 메시지, 스택 트레이스, 오류 코드)를 담고 있다. 예를 들어, 사용자 입력 값이 유효하지 않거나, 필요한 파일이 존재하지 않거나, 네트워크 요청이 타임아웃되는 상황에서 적절한 예외를 발생시켜 프로그램의 정상적인 논리 흐름을 중단시킨다.

예외가 발생하면, 현재 실행 중인 함수나 메서드의 일반적인 코드 실행은 즉시 중단된다. 이후 프로그램의 제어 흐름은 해당 예외를 처리할 수 있는 적절한 예외 처리기(catch 또는 except 블록)를 찾기 위해 호출 스택을 거슬러 올라가며 탐색한다. 이렇게 예외가 발생하는 지점에서 처리 가능한 곳까지 전파되는 과정이 시작된다.

3.2. 예외 포착 (Catch/Handle)

예외 포착은 예외 처리 메커니즘의 핵심 단계로, 발생한 예외를 감지하고 적절히 대응하는 과정이다. 대부분의 프로그래밍 언어는 try-catch(또는 try-except)와 같은 구문을 제공하여, 잠재적으로 예외가 발생할 수 있는 코드 블록(try)을 정의하고, 그 뒤에 해당 예외를 처리할 코드 블록(catch 또는 except)을 명시한다. 이때 하나의 try 블록에 여러 개의 catch 블록을 두어 서로 다른 유형의 예외를 구분하여 처리할 수 있다. 예를 들어, 파일 입출력 중 발생하는 IOException과 잘못된 인자로 인한 IllegalArgumentException을 각각 다른 방식으로 처리하도록 코드를 작성할 수 있다.

예외를 포착할 때는 구체적인 예외 유형부터 포착하는 것이 일반적인 모범 사례이다. 이는 더 포괄적인 예외 클래스(예: 모든 예외의 상위 클래스)에 의한 처리 블록이 먼저 실행되면, 그 하위의 구체적인 예외는 절대 포착되지 않는 문제를 방지하기 위함이다. 예외 처리 블록 내에서는 로깅을 통해 오류 정보를 기록하거나, 사용자에게 이해하기 쉬운 메시지를 제공하며, 필요 시 정리 작업을 수행한 후 프로그램의 실행을 정상 흐름으로 되돌릴 수 있다. 일부 언어에서는 예외 발생 여부와 관계없이 반드시 실행되어야 하는 정리 코드를 위한 finally 블록을 함께 제공하기도 한다.

효과적인 예외 포착은 프로그램의 견고성과 신뢰성을 높이는 데 기여한다. 단순히 예외를 포착하여 프로그램의 비정상 종료만 막는 것은 충분하지 않으며, 예외의 원인을 분석하고 시스템 상태를 안정적으로 유지하거나 복구하는 조치가 뒤따라야 한다. 또한, 과도하게 광범위한 예외 포착은 오류 원인을 숨기고 디버깅을 어렵게 만들 수 있으므로, 예외 처리 전략을 신중하게 설계하는 것이 중요하다.

3.3. 예외 전파 (Exception Propagation)

예외 전파는 예외가 발생한 지점에서 직접 처리되지 않을 경우, 해당 예외 객체가 호출 스택을 따라 상위의 호출자에게 자동으로 전달되는 과정을 의미한다. 이는 호출 스택의 현재 함수(메서드)가 예외를 처리할 적절한 예외 처리기를 가지고 있지 않다면, 그 함수의 실행이 중단되고 예외는 해당 함수를 호출한 상위 함수로 넘어가는 방식으로 이루어진다. 이 과정은 프로그램의 최상위 진입점(예: main 함수)까지 계속되거나, 적절한 catch 블록(예외 처리기)을 가진 코드 블록을 만날 때까지 반복된다.

예외 전파의 핵심은 예외 처리를 발생 지점이 아닌, 그 예외를 처리할 수 있는 적절한 수준(예: 비즈니스 로직 계층, 사용자 인터페이스 계층)에서 수행할 수 있도록 한다는 점이다. 예를 들어, 데이터베이스 접근 로직에서 SQL 예외가 발생했을 때, 해당 로직 내에서는 리소스만 정리하고, 실제 사용자에게 오류 메시지를 표시하거나 대체 작업을 결정하는 것은 상위의 애플리케이션 컨트롤러에서 처리하도록 예외를 전파할 수 있다. 이는 관심사의 분리 원칙을 따르는 설계를 가능하게 한다.

대부분의 현대 프로그래밍 언어는 예외 전파를 기본 메커니즘으로 지원한다. Java와 C++에서는 예외가 처리되지 않으면 자동으로 호출 스택을 거슬러 올라가 전파되며, Python에서도 마찬가지로 예외는 호출 체인을 따라 전파된다. JavaScript의 비동기 프로그래밍 환경에서는 Promise 체인이나 async/await 구문을 통해 비동기 예외의 전파가 관리된다. 예외 전파 경로를 명시적으로 차단하려면, 모든 함수에서 가능한 예외를 포착하여 처리하거나, 특정 언어에서 제공하는 throws 절(Java)을 사용해 선언해야 한다.

4. 예외의 종류

4.1. 검사 예외 (Checked Exception)

검사 예외(Checked Exception)는 컴파일 시점에 반드시 처리하거나 선언해야 하는 예외 유형이다. 주로 프로그램 외부 요인으로 발생하는 복구 가능한 오류 상황을 나타내며, 자바와 같은 언어에서 명시적으로 도입되었다. 컴파일러는 이러한 예외가 발생할 수 있는 코드를 검사하여, 개발자가 예외를 try-catch 문으로 처리하거나 메소드 선언부에 throws 절을 추가해 호출자에게 예외 전파를 명시하도록 강제한다.

검사 예외의 대표적인 예로는 입출력 작업 중 발생하는 IOException, 데이터베이스 접근 시 발생할 수 있는 SQLException, 파일을 찾지 못했을 때의 FileNotFoundException 등이 있다. 이러한 예외들은 프로그램의 실행 환경에 의존적이며, 네트워크 연결 실패나 잘못된 사용자 입력과 같이 개발자가 사전에 완전히 통제할 수 없는 상황에서 주로 발생한다. 따라서 컴파일러의 검사를 통해 이러한 잠재적 오류에 대한 대비를 강제함으로써 프로그램의 견고성(Robustness)을 높이는 것이 주요 목적이다.

그러나 검사 예외는 과도하게 사용될 경우 소프트웨어 개발의 생산성을 저하시키고 코드를 복잡하게 만들 수 있다는 비판도 존재한다. 특히 상위 계층으로 예외를 전파하기 위해 많은 메소드 선언에 throws 절을 추가해야 하거나, 복구가 사실상 불가능한 경우에도 처리 코드를 작성해야 하는 등의 문제가 제기된다. 이에 따라 C++나 파이썬, 자바스크립트와 같은 많은 현대 프로그래밍 언어들은 검사 예외 개념을 채택하지 않고, 모든 예외를 비검사 예외(Unchecked Exception)로 처리하는 방식을 선호하는 경향이 있다.

4.2. 비검사 예외 (Unchecked Exception)

비검사 예외는 컴파일 시점에 컴파일러가 강제적으로 예외 처리를 요구하지 않는 예외를 말한다. 주로 프로그램의 논리적 오류나 런타임 환경에서 발생하는 심각한 문제를 나타내며, 개발자가 예측하기 어렵거나 회복이 불가능한 경우가 많다. 자바에서는 RuntimeException 클래스와 그 하위 클래스들이 이 범주에 속하며, 파이썬의 ValueError, IndexError와 같은 내장 예외나 C++의 표준 예외 대부분도 비검사 예외에 해당한다고 볼 수 있다.

비검사 예외의 핵심 특징은 예외 처리를 강제하지 않는다는 점이다. 이는 메소드 시그니처에 예외를 선언할 필요가 없으며, 예외를 포착하지 않아도 컴파일 오류가 발생하지 않는다. 따라서 코드의 가독성을 높이고, 특히 복구 가능성이 낮은 오류에 대해 불필요한 예외 처리 문법을 줄일 수 있다. 예를 들어, NullPointerException이나 ArrayIndexOutOfBoundsException은 대부분 코드의 버그를 나타내므로, 이를 포착하여 복구하기보다는 버그를 수정하는 것이 더 바람직한 접근 방식이다.

주로 발생하는 비검사 예외의 종류는 다음과 같다.

예외 유형 (예시)

일반적 발생 원인

NullPointerException (자바)

객체 참조가 null인 상태에서 메소드를 호출하거나 필드에 접근할 때

IndexError (파이썬)

시퀀스(리스트, 문자열 등)의 범위를 벗어난 인덱스로 접근할 때

ArithmeticException (자바)

정수를 0으로 나누는 등의 산술 연산 오류 발생 시

IllegalArgumentException

메소드에 전달된 인자의 값이 부적절할 때

비검사 예외는 프로그램의 정확성을 보장하기 위한 핵심적인 논리를 검증하는 데 사용되기도 한다. 예를 들어, 공개 메소드의 시작 부분에서 전달받은 매개변수의 유효성을 검사하고, 조건을 만족하지 않으면 IllegalArgumentException을 발생시키는 것은 방어적 프로그래밍의 일반적인 패턴이다. 이러한 예외는 호출하는 쪽에서 올바른 인자를 제공해야 한다는 계약을 위반했음을 명시적으로 알리는 역할을 한다.

4.3. 에러 (Error)

에러는 프로그램 실행 중에 발생하는 심각한 문제로, 일반적으로 애플리케이션 수준에서 복구하기 어렵거나 불가능한 상황을 가리킨다. 이는 메모리 부족이나 가상 머신 오류와 같이 시스템 레벨에서 발생하는 치명적인 문제를 포함한다. 대부분의 프로그래밍 언어에서 에러는 비검사 예외의 범주에 속하며, 개발자가 명시적으로 예외 처리를 강제하지 않는 경우가 많다. 이는 에러가 발생한 상황 자체가 복구 가능성이 낮기 때문이다.

에러의 대표적인 예로는 자바의 OutOfMemoryError, StackOverflowError, VirtualMachineError 등이 있다. 이러한 에러는 런타임 환경 자체에 심각한 문제가 생겼음을 의미하며, 애플리케이션 코드만으로는 이를 해결하기 매우 어렵다. 따라서 일반적인 예외 처리 메커니즘을 통해 에러를 포착하더라도, 프로그램의 정상적인 실행 흐름으로 복귀시키기보다는 로그를 남기고 안전하게 종료하는 처리가 권장된다.

에러와 검사 예외 및 비검사 예외의 가장 큰 차이는 발생 원인과 복구 가능성에 있다. 예외는 잘못된 사용자 입력이나 네트워크 연결 실패와 같이 프로그램 로직 내에서 예측하고 처리할 수 있는 비정상 조건을 다루는 반면, 에러는 JVM이나 런타임 시스템 자체의 결함이나 한계로 인해 발생하는 외부적 요인이 주된 원인이다. 이로 인해 에러는 프로그램의 설계나 구현 오류보다는 실행 환경의 문제로 간주된다.

5. 주요 프로그래밍 언어별 구현

5.1. Java

Java는 예외 처리를 언어의 핵심 기능으로 통합한 대표적인 객체 지향 프로그래밍 언어이다. Java에서 모든 예외는 java.lang.Throwable 클래스를 상속받는 객체로 표현되며, 주요 하위 클래스로는 검사 예외를 나타내는 Exception과 비검사 예외인 RuntimeException, 그리고 심각한 Error가 있다. 이 계층 구조는 객체 지향의 상속 개념을 활용하여 예외를 체계적으로 분류하고 관리할 수 있게 한다.

Java의 예외 처리 메커니즘은 try, catch, finally, throw, throws 키워드를 중심으로 구성된다. 예외가 발생할 가능성이 있는 코드는 try 블록 안에 작성하고, 발생한 예외를 처리하는 코드는 catch 블록에 정의한다. finally 블록은 예외 발생 여부와 관계없이 반드시 실행되어야 하는 코드, 예를 들어 파일 입출력 스트림을 닫거나 데이터베이스 연결을 해제하는 등의 리소스 정리 작업을 보장하는 데 사용된다. 예외를 명시적으로 발생시키려면 throw 문을 사용하며, 메서드가 호출자에게 특정 검사 예외를 전파할 수 있음을 알리려면 메서드 선언부에 throws 절을 추가한다.

Java의 예외는 크게 두 가지 범주로 나뉜다. Exception 클래스의 하위 클래스 중 RuntimeException을 제외한 대부분은 검사 예외로 분류된다. 이는 컴파일러가 해당 예외가 발생할 수 있는 코드를 검사하고, 프로그래머가 반드시 catch 블록으로 처리하거나 throws로 선언하도록 강제한다. 반면, RuntimeException과 그 하위 클래스(예: NullPointerException, ArrayIndexOutOfBoundsException)는 비검사 예외로, 컴파일 시점에 처리 강제가 없으며 주로 프로그래밍 오류를 나타낸다. Error 클래스는 시스템 수준의 심각한 문제를 나타내어 일반적으로 애플리케이션 코드에서 복구를 시도하지 않는다.

Java 7부터는 여러 예외를 하나의 catch 블록에서 처리할 수 있는 멀티-캐치 문법과, try-with-resources 문이 도입되었다. try-with-resources는 AutoCloseable 인터페이스를 구현한 리소스를 선언부에 명시하면 try 블록이 종료될 때 자동으로 리소스를 닫아주어, 명시적인 finally 블록 없이도 메모리 누수와 같은 문제를 방지하는 데 크게 기여했다. 이러한 발전은 Java의 예외 처리를 더욱 안전하고 간결하게 만드는 방향으로 이어지고 있다.

5.2. Python

파이썬은 try, except, else, finally 키워드를 사용하여 명시적이고 유연한 예외 처리를 지원한다. 예외는 raise 문으로 발생시키며, except 블록에서 특정 예외 타입을 포착하여 처리한다. 파이썬의 모든 예외는 BaseException 클래스를 상속받으며, 대부분의 일반적인 예외는 Exception 클래스의 하위 클래스에 속한다.

파이썬의 예외는 크게 SyntaxError와 같은 문법 오류와 실행 중 발생하는 런타임 예외로 구분된다. 주요 내장 예외로는 ValueError, TypeError, IndexError, KeyError, FileNotFoundError 등이 있다. 사용자는 Exception 클래스를 상속받아 커스텀 예외를 정의할 수 있다. try 블록의 코드가 예외 없이 실행되면 else 블록이 실행되며, finally 블록은 예외 발생 여부와 관계없이 항상 실행되어 리소스 정리를 보장한다.

파이썬은 자바의 검사 예외와 같은 개념이 존재하지 않아 모든 예외가 비검사 예외에 해당한다. 이는 개발자에게 예외 처리에 대한 유연성을 주지만, 필요한 예외를 처리하지 않을 가능성도 동시에 내포한다. 예외 객체는 보통 에러 메시지 외에도 추가적인 속성을 가질 수 있어, 상세한 오류 정보를 전달하는 데 활용된다.

5.3. C++

C++에서 예외 처리는 try, catch, throw 키워드를 사용하여 구현된다. 프로그램은 try 블록 내에서 코드를 실행하며, 여기서 발생한 예외는 throw 문에 의해 명시적으로 던져진다. 던져진 예외는 해당 try 블록과 연결된 catch 블록에서 포착되어 처리된다. C++의 예외는 기본 타입(정수, 문자열 등)부터 사용자 정의 클래스 객체에 이르기까지 모든 타입이 될 수 있으며, 보통은 std::exception 클래스나 그 파생 클래스의 객체를 사용하는 것이 일반적이다.

C++의 예외 처리 메커니즘은 예외 전파를 지원한다. 즉, 현재 함수에서 예외를 처리할 적절한 catch 블록이 없다면, 예외는 함수의 호출자(caller)에게 자동으로 전파된다. 이 과정은 호출 스택을 거슬러 올라가며, 최종적으로 main 함수에서도 처리되지 않으면 프로그램이 비정상 종료된다. 이러한 설계는 오류 발생 지점과 오류 처리 지점을 분리시켜 코드의 모듈성을 높이는 데 기여한다.

C++11 이후에는 예외 처리의 안전성과 효율성을 높이기 위한 몇 가지 기능이 추가되었다. 대표적으로 noexcept 지정자가 있으며, 이는 함수가 예외를 던지지 않음을 컴파일러에 알려 최적화를 유도한다. 또한 스마트 포인터와 RAII(Resource Acquisition Is Initialization) 패턴의 광범위한 사용은 예외 발생 시에도 메모리나 파일 핸들 같은 리소스가 자동으로 정리되도록 보장한다. 이는 예외 안전성(exception safety)을 확보하는 핵심 기법이다.

기능/키워드

설명

try

예외 발생을 감시할 코드 블록을 정의한다.

catch

try 블록에서 던져진 특정 타입의 예외를 포착하여 처리하는 블록을 정의한다.

throw

예외를 발생(던지기)시킨다.

std::exception

C++ 표준 라이브러리에서 제공하는 대부분의 예외 클래스의 기반 클래스이다.

noexcept (C++11)

해당 함수가 예외를 던지지 않음을 지정하는 키워드이다.

5.4. JavaScript

자바스크립트의 예외 처리는 ECMAScript 표준에 정의된 try...catch...finally 문을 중심으로 이루어진다. 자바스크립트 엔진은 코드 실행 중 오류가 발생하면 해당 오류 정보를 담은 Error 객체를 생성하며, 이를 throw 문을 통해 명시적으로 발생시킬 수도 있다. try 블록 내에서 예외가 발생하면 실행 흐름이 즉시 중단되고, 제어권이 가장 가까운 catch 블록으로 이동하여 예외를 처리한다. finally 블록은 예외 발생 여부와 관계없이 항상 실행되어 리소스 정리와 같은 필수 작업을 수행하도록 보장한다.

자바스크립트의 예외는 대부분 비검사 예외에 속하며, 이는 컴파일 시점이 아닌 런타임에만 확인 가능함을 의미한다. 발생할 수 있는 예외 타입으로는 일반적인 Error 외에도 구체적인 상황을 나타내는 SyntaxError, ReferenceError, TypeError, RangeError 등이 있다. 비동기 프로그래밍 환경에서의 예외 처리는 특별한 주의가 필요하다. 예를 들어, Promise 체인 내에서 발생한 예외는 .catch() 메서드를 통해 처리해야 하며, async/await 문법을 사용할 경우 기존의 try...catch 블록으로 처리할 수 있다.

예외 타입

설명

Error

일반적인 오류 객체의 기본형

SyntaxError

문법 오류가 있을 때 발생

ReferenceError

존재하지 않는 변수를 참조할 때 발생

TypeError

변수나 매개변수가 유효하지 않은 타입일 때 발생

RangeError

숫자 변수나 매개변수가 유효한 범위를 벗어났을 때 발생

자바스크립트의 예외는 호출 스택을 따라 전파된다. 현재 함수에서 예외를 처리하지 않으면, 예외는 해당 함수를 호출한 상위 함수로 전달되며, 최상위 수준(전역 컨텍스트)까지 전파되어 처리되지 않으면 프로그램이 중단된다. 이러한 특성은 오류의 근원지를 찾고 중앙 집중식으로 처리하는 데 유용하게 활용될 수 있다.

6. 예외 처리의 모범 사례

예외 처리를 효과적으로 설계하고 구현하기 위한 모범 사례는 소프트웨어 공학의 중요한 부분이다. 첫째, 예외는 진정한 예외적인 상황, 즉 프로그램의 정상적인 흐름에서 벗어난 오류에 대해서만 사용해야 한다. 흐름 제어를 위해 예외를 사용하는 것은 성능 저하를 초래할 수 있으며 코드의 가독성을 떨어뜨린다. 둘째, 예외를 포착할 때는 가능한 한 구체적인 예외 유형을 명시하는 것이 좋다. 너무 포괄적인 예외 클래스를 포착하면 의도하지 않은 다른 오류까지 숨겨 디버깅을 어렵게 만들 수 있다.

셋째, 예외를 포착한 후에는 적절한 조치를 취하거나, 조치가 불가능하다면 호출자에게 예외를 다시 전파하는 것이 바람직하다. 아무런 조치 없이 예외를 무시하거나 단순히 로그만 기록하는 것은 문제를 은폐할 위험이 있다. 넷째, 리소스 누수를 방지하기 위해 파일 입출력이나 데이터베이스 연결, 네트워크 소켓과 같은 리소스를 사용한 후에는 예외 발생 여부와 관계없이 정리하는 코드를 반드시 실행해야 한다. 많은 현대 프로그래밍 언어는 try-with-resources나 using 문과 같은 구문을 제공하여 이를 자동화한다.

모범 사례

설명

주의사항

구체적 예외 포착

발생 가능한 특정 예외 유형별로 처리 블록을 구성

Exception이나 Throwable 같은 최상위 클래스로 일괄 포착 지양

예외 무시 금지

포착한 예외에 대한 최소한의 처리(로깅, 변환, 재전파) 수행

빈 catch 블록 사용 지양

리소스 자동 정리

언어가 제공하는 자원 관리 구문 활용

finally 블록에서 명시적 정리보다 선호

유익한 예외 메시지

예외 원인과 위치를 파악할 수 있는 충분한 정보 제공

디버깅을 위한 스택 트레이스 보존

마지막으로, 사용자 정의 예외를 생성할 때는 기존 예외 계층 구조를 고려하고, 예외 객체에 오류의 맥락을 이해하는 데 도움이 되는 명확하고 유익한 메시지를 포함시켜야 한다. 또한 애플리케이션의 상위 레벨에서 최종적으로 예외를 처리하여 사용자에게 친숙한 오류 메시지를 표시하거나, 안전하게 작업을 롤백하는 전략을 수립하는 것이 좋다. 이러한 원칙들은 코드 유지보수성을 높이고 시스템의 견고성을 강화하는 데 기여한다.

7. 예외 처리의 대안

예외 처리의 전통적인 방식 외에도 프로그램의 오류 상황을 관리하는 여러 대안적 접근법이 존재한다. 이러한 대안들은 예외 처리의 복잡성을 줄이거나, 성능 오버헤드를 최소화하거나, 특정 프로그래밍 패러다임에 더 적합하도록 설계되었다.

대표적인 대안으로는 반환 코드를 활용하는 방법이 있다. 이는 C 프로그래밍 언어와 같은 오래된 언어에서 흔히 사용되며, 함수가 정상적으로 실행되었는지 여부를 특정한 값(예: NULL, -1, false)을 반환함으로써 호출자에게 알린다. 호출자는 반환된 값을 확인하여 오류를 처리해야 한다. 이 방식은 런타임 오버헤드가 적지만, 모든 호출 지점에서 반환값을 명시적으로 체크해야 하므로 코드가 장황해지고 실수로 체크를 누락하기 쉽다는 단점이 있다.

또 다른 접근법은 옵셔널 타입이나 결과 타입을 사용하는 것이다. 이는 함수형 프로그래밍 언어에서 널리 채택된 개념으로, 하스켈의 Maybe나 러스트의 Result 타입이 대표적이다. 함수는 성공한 경우 값을, 실패한 경우 실패 원인을 담은 특별한 컨테이너를 반환한다. 호출자는 패턴 매칭 등을 통해 이 컨테이너를 안전하게 "풀어" 값을 사용하거나 오류를 처리한다. 이 방식은 컴파일 타임에 오류 처리의 누락을 강제할 수 있어 안전성이 높지만, 새로운 타입 시스템에 대한 이해가 필요하다.

마지막으로, 어설션은 주로 개발 및 디버깅 단계에서 사용되는 방법이다. 프로그램이 반드시 만족해야 할 조건(예: 인자가 NULL이 아님)을 코드 내에 명시하고, 그 조건이 위반되면 프로그램을 즉시 중단시킨다. 이는 복구 불가능한 논리적 오류를 빠르게 발견하는 데 목적이 있으며, 일반적으로 배포 버전에서는 비활성화된다. 따라서 어설션은 예외 처리나 반환 코드를 완전히 대체하기보다는 보조적인 역할을 한다.

8. 관련 문서

  • MDN Web Docs - 예외 처리 문법

  • Microsoft Learn - 예외 처리 및 throw

  • Oracle Java Tutorials - 예외

  • Python 공식 문서 - 내장 예외

  • W3Schools - Java 예외 처리

  • TCP School - 예외 처리

  • 점프 투 파이썬 - 예외 처리

  • 모던 C++ 예외 처리

리비전 정보

버전r1
수정일2026.02.26 03:43
편집자unisquads
편집 요약AI 자동 생성