예외 처리

**예외 처리**(例外 處理, exception handling) 또는 **오류 처리**(error handling)는 일반적인 실행의 흐름을 바꾸는 몇 가지 조건을 처리하도록 설계한 프로그래밍 언어의 개념이나 컴퓨터 하드웨어 구조를 말한다. 일반적으로 프로그램이 처리되는 동안 특정한 문제가 일어났을 때 처리를 중단하고 다른 처리를 하는 것을 예외 처리라고 한다. 에러 감지 및 정정(error detection and correction)과는 구별된다.
# 예외
컴퓨팅 및 컴퓨터 프로그래밍에서 예외 처리는 프로그램 실행 중에 예외(특수 처리가 필요한 비정상적이거나 예외적인 조건) 발생에 응답하는 프로세스이다. 일반적으로 예외는 일반적인 실행 흐름을 중단하고 미리 등록된 예외 처리기(exception handler)를 실행한다. 이러한 수행 방식에 대한 세부 사항은 하드웨어 예외인지 소프트웨어 예외인지, 그리고 소프트웨어 예외가 구현되는 방법에 따라 달라진다. 예외는 컴퓨터 시스템의 여러 계층에 의해 정의되며 일반적인 계층에는 CPU 정의 인터럽트, 운영 체제(OS) 정의 신호, 프로그래밍 언어 정의 예외가 있다. 각 계층은 상호 연관될 수 있지만 예외 처리를 위한 서로 다른 방법이 필요하다. CPU 인터럽트는 OS 신호로 바뀔 수 있다. 일부 예외, 특히 하드웨어 예외는 원활하게 처리되어 중단된 곳에서 실행이 재개될 수 있다.
# 정의
인터럽트 예외의 정의는 각 프로시저에 "정상적으로" 종료되는 일련의 상황인 전제 조건이 있다는 관찰을 기반으로 한다. 예외 처리 메커니즘을 사용하면 이 전제 조건을 위반하는 경우(예: 비정상적인 인수 집합에 대해 프로시저가 호출된 경우) 프로시저에서 예외를 발생시킬 수 있다. 그러면 예외 처리 메커니즘이 예외를 처리한다. 전제 조건과 예외의 정의는 주관적이다. "정상적인" 상황은 전적으로 프로그래머에 의해 정의된다. 프로그래머는 0으로 나누기가 정의되지 않은 것으로 간주하여 예외가 발생하거나 0을 반환하거나 특수 "ZERO DIVIDE" 값(예외가 필요하지 않음)을 반환하는 등의 동작을 고안할 수 있다. 일반적인 예외에는 유효하지 않은 인수(예: 값이 함수 도메인 외부에 있음), 사용할 수 없는 리소스(예: 누락된 파일, 네트워크 드라이브 오류 또는 메모리 부족 오류) 또는 루틴이 정상적인 오류를 감지한 경우가 포함된다. 특별한 처리가 필요한 조건(예: 주의, 파일 끝) 사회적 압력은 예외의 범위와 예외 처리 메커니즘의 사용에 큰 영향을 미친다. 즉, 일반적으로 핵심 라이브러리에서 볼 수 있는 사용 예와 기술 서적, 잡지 기사, 온라인 토론 포럼 및 조직의 코드 표준에 있는 코드 예제이다. 예외 처리는 메커니즘이 정상적인 반환 값과 잘못된 반환 값을 구별한다는 점에서 반술어 문제를 해결한다. C와 같이 기본 제공 예외 처리 기능이 없는 언어에서는 루틴이 일반 반환 코드 및 errno 패턴과 같은 다른 방식으로 오류를 신호해야 한다. 넓은 관점에서 볼 때 오류는 예외의 적절한 하위 집합으로 간주될 수 있으며 errno와 같은 명시적인 오류 메커니즘은 예외 처리의 (상세한) 형태로 간주될 수 있다. "예외"라는 용어는 "오류"보다 더 선호된다. 이는 어떤 것이 잘못되었음을 의미하지 않기 때문이다. 한 프로시저나 프로그래머에 의해 오류로 간주되는 조건은 다른 프로시저나 프로그래머에게는 그렇게 간주되지 않을 수 있다. "예외"라는 용어는 "이상"이라는 의미가 예외 발생이 비정상적이거나 비정상적임을 나타내기 때문에 오해의 소지가 있을 수 있다. 실제로는 예외 발생이 프로그램에서 정상적이고 일반적인 상황일 수 있다. 예를 들어 키(key)에 연결된 값이 없는 경우 연관 배열에 대한 조회 함수가 예외를 발생시킨다고 가정해 보겠다. 상황에 따라 이 "키 부재" 예외는 성공적인 조회보다 훨씬 더 자주 발생할 수 있다.
# 역사
최초의 하드웨어 예외 처리는 1951년 유니박 I에서 발견되었다. 산술 오버플로는 제어를 전송하거나 결과를 수정할 수 있는 주소 0에서 두 개의 명령을 실행했다.access-date=4 January 2022 소프트웨어 예외 처리는 1960년대와 1970년대에 개발되었다. 예외 처리는 1980년대 이후 많은 프로그래밍 언어에서 널리 채택되었다.
# 예외의 원인
예외의 문제는 보통 다음과 같은 데에서 비롯한다. * 컴퓨터 하드웨어 문제 * 운영 체제의 설정 실수 * 라이브러리 손상 * 사용자의 입력 실수 ** 존재하지 않는 파일 이름 입력 ** 숫자값 입력 칸에 일반 문자 입력 * 받아들일 수 없는 연산 * 할당하지 못하는 기억 장치 접근 ** 페이지 실패 등
# 예외 안전
메모리 누수, 잘못된 출력, 왜곡된 자료와 같이 코드의 런타임 실패가 악영향을 미치지 않는다면, 이러한 코드를 예외 안전이라고 부른다. 몇 가지 수준의 예외 안전은 다음과 같다: # **실패 투명성** (no throw guarantee) # **강력한 불변의 예외 안전** (Commit or rollback semantics) # **기본 예외 안전** (Basic exception safety) # **최소 예외 안전** (no-leak guarantee) # **예외 보증을 하지 않는 안전** (No exception safety)
# 프로그래밍 언어에서의 예외 지원
예외를 눈치채지 못하다가 해당 문제를 실제 동작으로 넘길 경우, 치명적인 문제를 일으킬 수 있다. 이를 막으려면 예외가 일어난 부분을 철저하게 검사해야 한다. 다만 프로그램의 크기가 크다면 일반적인 예외 처리보다 더 많은 기술을 요구할 수도 있다. C++, 에이다, 자바, 자바스크립트와 같은 프로그래밍 언어는 예외 처리 기능을 포함하고 있다. 이러한 언어에서는 예외 처리를 자동화하고 있다. 예외 처리를 담당하는 핸들러를 찾아 순서대로 콜 스택을 거슬러 올라가 올바른 핸들러를 찾아내면 그 곳에 처리를 맡긴다. 스킴(Scheme) 언어는 기본적으로 예외를 처리하지 않지만 라이브러리 수준에서 예외를 처리할 수 있다. 표준 규격 SRFI-34에 정의되어 있다.
# 예제
try { line = console.readLine(); if (line.length() == 0) { throw new EmptyLineException("콘솔에서 읽어들인 줄이 비어 있습니다!"); } console.printLine("안녕하세요, %s님!" % line); console.printLine("프로그램이 성공적으로 실행되었습니다"); } catch (EmptyLineException e) { console.printLine("안녕하세요!"); } catch (Exception e) { console.printLine("오류: " + e.message()); } finally { console.printLine("프로그램이 지금 종료되었습니다"); }