Java - 예외처리 (throw / try / catch / finally)

idkwhattodo·2022년 8월 29일
0

Java 문법

목록 보기
3/18
post-thumbnail

1. 예외처리

프로그램이 실행되는 동안 문제가 발생하면 프로그램이 자동으로 중단되는데, 이럴 경우 프로그램이 대처할 수 있도록 처리하는 것이 예외처리라고 한다.

예외가 발생하는 이유에는 여러가지가 있지만, 일반적으로 잘못된 코드를 작성했거나 사용자가 개발자가 원하지 않는 방향으로 프로그램을 사용했을 때 발생한다.

※ 프로그램 실행 중 발생하는 오류 => 예외(Exception)

※ 프로그래밍 언어의 문법적인 오류 => 에러(Error)


2. throw

"예외"란 무언가 예외적인 상황이나 에러가 발생했음을 가리키는 신호이다.

예외를 "발생시키다(throw)"라는 것은 그러한 에러나 예외 상황을 알린다는 뜻이다.

한편, 예외를 "잡아내다(catch)"라는 것은 예외를 처리한다는 뜻인데, 즉 그 예외를 회복하기 위해 무언가 필요하거나 적절한 행동을 취한다는 뜻이다.

정리하자면, 예외를 강제로 발생시켜야 할 경우가 생길 때는 "throw"를 사용하고, 그 예외를 잡아내는 데에는 "try/catch/finally"를 사용한다.

function factorial(x) { 
    // 만약 전달인자가 유효하지 않으면 예외를 발생
    if (x < 0) { 
        throw new Error('x는 음수가 아니어야 합니다.') 
    } // 유효하다면, 값을 계산하여 정상적으로 반환
    for (var f = 1; x > 1; f *= x, x--) /* 비어 있음 */ 
    return f; 
}

예외가 발생하면 인터프리터는 정상적인 프로그램 실행을 즉시 중단하고 가장 가까운 예외 처리기로 넘어간다.(예외 처리기는 "catch"를 사용하여 작성된다.)

예외를 발생시켰던 코드 블록이 catch절과 연결되어 있지 않으면, 인터프리터는 바로 상위 단계를 감싸고 있는 코드 블록에 연결되어 있는지를 확인한다. (예외 처리기를 찾을 때까지 반복)

만일 예외를 처리할 try/catch/finally가 없는 함수 안에서 예외가 발생했다면, 해당 함수를 호출했던 블록으로 그 예외가 전파되어 올라간다.

즉, 호출 스택(call stack)을 따라서 예외가 전파되어 올라가고, 그래도 아무런 예외 처리기도 찾을 수 없으면 이 예외는 에러로 취급되어 사용자에게 보고되어진다.


※ 예외를 강제로 발생시키는 이유

개체를 잘못 사용하는 사용자에게 예외를 강제로 발생시켜서 사용자에게 주의를 줄 수도 있고 예외와 관련된 처리를 해달라고 부탁할 수 도 있기 때문.

3. try/catch/finally

1) try

예외가 발생할지 발생하지 않을지 모르는 코드 블록 정의

2) catch

try 블록 내부에서 예외가 발생할 경우 호출되는 문장 블록

3) finally

try 블록에서 일어난 일에 관계없이 항상 실행이 보장되어야 할 뒷정리용 코드


※ catch나 finally 블록은 생략할 수 있으나, try 블록은 catch나 finally 중 적어도 하나 이상의 블록과 함께 사용되어야 한다.

※ try, catch, finally 블록은 모두 중괄호로 시작하여 중괄호로 끝난다. 이 중괄호들은 필수로 요구되는 문법의 일부로서 생략할 수 없다. (해당절에 단 하나의 문장만 있더라도)


위의 사항을 정리하자면 아래와 같다.

try { 
    /** 
    * 정상이라면 이 코드는 아무런 문제없이 블록의 시작부터 끝까지 실행된다. 
    * 하지만 경우에 따라 예외가 발생할 수 있다. 
    * 예외는 throw 문에 의해 직접적으로 발생할 수도 있고, 
    * 또는 예외를 발생시키는 메서드의 호출에 의해 발생할 수도 있다. 
    */ 
} catch (e) { 
    /** 
    * 이 블록 내부의 문장들은 오직 try 블록에서 예외가 발생할 경우에만 실행된다. 
    * 이 문장들에선 지역 변수 e를 사용하여 Error 객체 또는 앞에서 던진 다른 값을 참조할 수 있다. 
    * 이 블록에서는 어떻게든 그 예외를 처리할 수도 있고, 
    * 그냥 아무것도 하지 않고 예외를 무시할 수도 있고, 
    * 아니면 throw 를 사용해서 예외를 다시 발생시킬 수도 있다. 
    */ 
} finally { 
    /** 
    * 이 블록에는 try 블록에서 일어난 일에 관계없이 무조건 실행될 코드가 위치한다. 
    * 이 코드는 try 블록이 어떻게든 종료되면 실행된다. 
    * try 블록이 종료되는 상황은 다음과 같다. 
    * 1) 정상적으로 블록의 끝에 도달했을 때 
    * 2) break, continue 또는 return 문에 의해서 
    * 3) 예외가 발생했지만 catch 절에서 처리했을 때 
    * 4) 예외가 발생했고 그것이 잡히지 않은 채 퍼져나갈 때 
    */ 
}

아래의 코드는 try/catch의 예제이다.

try { 
    // 사용자에게 번호 입력을 요청 
    var n = prompt("정수를 입력해 주세요."); 
    // 사용자의 입력이 유효하다고 가정하고 그 숫자의 계승(factorial)을 계산한다. 
    var f = factorial(n); 
    // 결과를 표시한다. 
    console.log(n + "! = " + f); 
} catch (ex) { // 만약 사용자의 입력이 유효하지 않다면 이곳에 도달한다. 
    // 사용자에게 에러가 무엇인지 알린다. 
    alert(ex); 
}

위 코드는 finally 절이 없는 try/catch의 예제이다. finally는 catch 만큼 자주 쓰이는 편은 아니지만, 종종 유용할 때가 있다.

일단 try 블록이 일부라도 실행되면 finally 절의 실행이 보장된다.

이때 try 블록의 코드가 어떻게 끝나는지는 상관 없고, 보통 finally 절은 try 절 코드의 뒷정리를 위해 사용되는 것이 일반적이다.

만일 return, continue, break 문 등으로 인해 try 블록에서 제어가 빠져 나왔다면, 이들 문장이 인도하는 곳으로 제어가 이동하기에 앞서 finally 블록이 실행된다.

만일 try 블록에서 예외가 발생했는데, 거기에 이 예외를 처리하기 위한 catch 블록이 있다면, 제어는 일단 catch 블록으로 이동한 후에 finally 블록으로 넘어오게 된다.

만약에 일어난 예외를 처리할 catch 블록이 현 단계에 없다면, 일단 finally 블록으로 제어가 이동했다가 그 다음에 상위 단계로 전파되어 올라간다.

또한, finally 블록 자체에서 return, continue, break 또는 throw 문을 사용해 제어를 이동시키거나 또는 예외를 발생시키는 메서드를 호출하여 제어를 이동시킬 수도 있다.

이런 경우 finally 블록이 실행된 후에 있을 예정이었던 제어 이동은 취소되고, finally에서 가리키는 새로운 제어 이동이 일어난다.

예를들어, finally 절에서 예외를 발생시키면 그 예외가 기존에 발생해서 처리중이던 예외를 대신하게 된다.

만일 finally 절에서 return 문을 실행했다면, 기존에 예외가 발생했고, 아직 미처 처리되지 않았다 하더라도 이 메소드는 정상적으로 반환된다.

try와 finally는 catch가 없어도 함께 쓰일 수 있다.

이러한 경우 try 절에서 return, continue, return 문 등이 실행된 것과 관계없이, finally 블록은 그저 무조건 실행이 보장되는 뒷정리 코드의 역할을 하게 된다.

예를들어 아래의 코드에서는 루프 카운터 변수가 정상적으로 증가될 수 있게 try/finally 문을 사용하고 있다.

만약 continue 문에 의해 루프 반복이 갑자기 끝났다 하더라도 루프 카운터 변수는 증가되는 코드이다.

var i = 0, total = 0; 
while (i < a.length) { 
    try { 
        if(typeof a[i] != 'number' || isNaN(a[i])) { // 만일 이것이 숫자가 아니라면 
            continue; // 이 루프의 다음 반복으로 넘어간다. 
            } 
            total += a[i]; // 숫자라면 total에 이 숫자를 더한다. 
         } finally { 
             i++; // 위에서 continue 를 사용했지만 무조건 i를 증가시키도록 한다. 
         } 
}

아래는 try/catch/finally에 대한 정리이다.

// try ~ catch ~ finally 절 : 자바스크립트의 예외 처리 구문(C#/Java와 비슷)
// fake(); // 정의되지 않은 함수를 호출하여 에러가 발생
try {
	// 예외(에러)가 발생할 만한 코드를 입력
	fake(); // 정의되지 않은 함수 호출
} catch(e) {
	// 예외가 발생했을 때 실행되는 영역
	alert(e.name + ", " + e.message);
} finally {
	// 예외가 발생하던 발생하지 않던 무조건 실행되는 영역
	alert('무조건 실행되는 영역')
}

아래의 코드는 try/catch/finally의 예제이다.

// 예외를 무조건 발생시킴
// throw {name : '예외 이름', message : '예외 내용'}
try {
	var a = 3;
	var b = 0;
	if(b === 0 ) {
		throw {name : 'divideByZeroException', message: '0으로 나눌수가 없습니다.'}
	}
} catch(e) {
	alert(e.name + ", " + e.message); // 에외 처리구문 (예 : 비정상 종료를 정상종료하도록 하기위함)
} finally {
	alert('프로그램을 정상종료합니다.')
}
profile
공부겅부

0개의 댓글