try...catch
try...catch
try...catch 문은 try 블록과 catch 블록, finally 블록 중 하나 또는 둘 모두로 구성됩니다. try 블록의 코드가 먼저 실행되고, 예외를 던지면 catch 블록의 코드가 실행됩니다. finally 블록의 코드는 제어 흐름이 전체 구조를 빠져나가기 전에 항상 실행됩니다.
출처: try...catch
본문
try...catch 문은 try 블록과 catch 블록, finally 블록 중 하나 또는 둘 모두로 구성됩니다. try 블록의 코드가 먼저 실행되고, 예외를 던지면 catch 블록의 코드가 실행됩니다. finally 블록의 코드는 제어 흐름이 전체 구조를 빠져나가기 전에 항상 실행됩니다.
구문 (Syntax)
try {
tryStatements
} catch (exceptionVar) {
catchStatements
} finally {
finallyStatements
}
tryStatements: 실행될 문들입니다.catchStatements:try블록에서 예외가 던져지면 실행되는 문입니다.exceptionVar: 관련catch블록에 잡힌 예외를 담는 선택적 식별자 또는 패턴입니다.catch블록이 예외 값을 사용하지 않으면exceptionVar와 그 주변 괄호를 생략할 수 있습니다.finallyStatements: 제어 흐름이try...catch...finally구조를 빠져나가기 전에 실행되는 문들입니다. 예외가 던져졌는지 잡혔는지와 관계없이 실행됩니다.
설명 (Description)
try 문은 항상 try 블록으로 시작합니다. 그리고 catch 블록이나 finally 블록 중 하나가 반드시 있어야 합니다. 둘 다 있을 수도 있습니다. 이로써 try 문에는 세 가지 형태가 있습니다.
try...catchtry...finallytry...catch...finally
if나 for 같은 다른 구조와 달리, try, catch, finally 블록은 단일 문이 아니라 반드시 블록이어야 합니다.
try doSomething(); // SyntaxError
catch (e) console.log(e);
catch 블록은 try 블록에서 예외가 던져졌을 때 무엇을 할지 지정하는 문들을 포함합니다. try 블록(또는 try 블록에서 호출된 함수) 내의 어떤 문이든 예외를 던지면 제어가 즉시 catch 블록으로 이동합니다. try 블록에서 예외가 던져지지 않으면 catch 블록은 건너뜁니다.
finally 블록은 제어 흐름이 try...catch...finally 구조를 빠져나가기 전에 항상 실행됩니다. 예외가 던져졌는지 잡혔는지와 관계없이 항상 실행됩니다.
하나 이상의 try 문을 중첩할 수 있습니다. 내부 try 문에 catch 블록이 없으면, 바깥쪽 try 문의 catch 블록이 대신 사용됩니다.
Catch 바인딩 (Catch binding)
try 블록에서 예외가 던져지면, exceptionVar(즉 catch (e)의 e)가 예외 값을 담습니다. 이 바인딩을 사용해 던져진 예외에 대한 정보를 얻을 수 있습니다. 이 바인딩은 catch 블록의 스코프에서만 사용할 수 있습니다.
단일 식별자일 필요는 없습니다. 구조 분해(destructuring) 패턴을 사용해 여러 식별자를 한 번에 할당할 수 있습니다.
try {
throw new TypeError("oops");
} catch ({ name, message }) {
console.log(name); // "TypeError"
console.log(message); // "oops"
}
catch 절이 만든 바인딩은 catch 블록과 같은 스코프에 있으므로, catch 블록에서 선언된 변수는 catch 절이 만든 바인딩과 같은 이름을 가질 수 없습니다. (이 규칙에는 예외가 하나 있지만, 이는 폐기된 구문입니다.)
try {
throw new TypeError("oops");
} catch ({ name, message }) {
var name; // SyntaxError: Identifier 'name' has already been declared
let message; // SyntaxError: Identifier 'message' has already been declared
}
예외 바인딩은 쓸 수 있습니다(writable). 예를 들어 예외 값이 Error 객체인지 확인하기 위해 정규화할 수 있습니다.
try {
throw "Oops; this is not an Error object";
} catch (e) {
if (!(e instanceof Error)) {
e = new Error(e);
}
console.error(e.message);
}
예외 값이 필요 없다면, 주변 괄호와 함께 생략할 수 있습니다.
function isValidJSON(text) {
try {
JSON.parse(text);
return true;
} catch {
return false;
}
}
finally 블록
finally 블록은 try 블록과 catch 블록이 실행된 후, try...catch...finally 블록 뒤에 오는 문들보다 먼저 실행할 문들을 포함합니다. 제어 흐름은 항상 finally 블록에 들어가며, 다음 중 한 가지 방식으로 진행될 수 있습니다.
try...finally구조에서 제어 흐름이try블록을 빠져나온 직후(마지막 문 또는throw,return,break,continue문 이후);try...catch...finally구조에서 제어 흐름이catch블록을 빠져나온 직후;try...catch...finally구조에서 제어 흐름이throw문을 통해 나가지 않는 한try블록을 빠져나온 직후(그 경우 제어 흐름은 먼저catch블록으로 들어갑니다).
finally 블록이 try나 catch 블록의 제어 흐름 문(return, throw, break, continue) 이후에 들어가면, 그 문의 효과는 finally 블록에서 실행된 마지막 문 이후까지 연기됩니다. 예를 들어 try 블록에서 예외가 던져지고 그 예외를 처리할 catch 블록이 없어도, finally 블록은 여전히 실행되고 예외는 finally 블록 실행이 끝난 직후에 던져집니다.
단, finally 블록에서 실행된 마지막 문 자체가 제어 흐름 문이라면 그 문이 이전 문의 효과를 덮어씁니다(연기되지 않음). finally 블록에서 return, throw, break, continue 같은 제어 흐름 문을 사용하는 것은 일반적으로 좋지 않습니다. 이전에 실행된 제어 흐름 문의 효과를 덮어쓸 수 있기 때문이며, 이는 거의 의도된 바가 아니기 때문입니다. 대부분의 경우 finally 블록은 주 로직을 수정하지 않는 정리(cleanup) 코드에만 사용해야 합니다.
예제: 무조건적인 catch 블록
catch 블록이 사용되면, try 블록 내에서 어떤 예외가 던져지든 catch 블록이 실행됩니다.
try {
throw new Error("My exception"); // generates an exception
} catch (e) {
// statements to handle any exceptions
logMyErrors(e); // pass exception object to error handler
}
예제: 조건부 catch 블록
try...catch 블록을 if...else if...else 구조와 결합해 "조건부 catch 블록"을 만들 수 있습니다.
try {
myRoutine(); // may throw three types of exceptions
} catch (e) {
if (e instanceof TypeError) {
// statements to handle TypeError exceptions
} else if (e instanceof RangeError) {
// statements to handle RangeError exceptions
} else if (e instanceof EvalError) {
// statements to handle EvalError exceptions
} else {
// statements to handle any unspecified exceptions
logMyErrors(e); // pass exception object to error handler
}
}
일반적인 사용 사례는 예상되는 작은 오류 하위 집합만 잡아(침묵시키고) 다른 경우에는 오류를 다시 던지는 것입니다.
try {
myRoutine();
} catch (e) {
if (e instanceof RangeError) {
// statements to handle this very common expected error
} else {
throw e; // re-throw the error unchanged
}
}
예제: 중첩된 try 블록
모든 예외는 다시 던져지지 않는 한 가장 가까운 바깥쪽 catch 블록에 의해 한 번만 잡힙니다. 물론 "내부" 블록에서 발생한 새 예외(catch 블록의 코드가 던지는 동작을 할 수 있으므로)는 "바깥쪽" 블록에 의해 잡힙니다.
예제: finally를 사용한 리소스 정리
finally 블록의 한 사용 사례입니다. 코드가 파일을 열고 그 파일을 사용하는 문들을 실행하며, finally 블록은 예외가 던져지더라도 사용 후 파일이 항상 닫히도록 보장합니다.
openMyFile();
try {
// tie up a resource
writeMyFile(theData);
} finally {
closeMyFile(); // always close the resource
// any uncaught exception is deferred here
}
같은 방식으로, try 블록의 어떤 return 문의 효과도 finally 블록의 끝까지 연기되지만, 반환 값 표현식은 finally 블록에 들어가기 전에 평가됩니다.
finally 블록에서의 return
finally 블록의 return 문은 이전 return 문(그 반환 값까지 포함)의 효과를 덮어씁니다.
function doIt() {
const order = ["z"];
try {
order.push("try");
return order.sort(); // "z" is now after "try"
} finally {
order.push("finally");
return order;
}
}
doIt();
// returns ["try", "z", "finally"], not ["finally", "try", "z"] or ["try", "z"]
같은 논리가 다른 제어 흐름 문에도 적용됩니다. 어떤 값이 아닌 다른 값이 반환될 수 있습니다.
function doIt() {
try {
throw "try"; // makes control flow enter the `catch` block
} catch {
throw "catch"; // makes control flow enter the `finally` block
} finally {
return "finally"; // returns "finally" instead of throwing "catch"
}
}
doIt(); // returns "finally"
이 효과는 의도되지 않을 가능성이 높으므로 finally 블록에서 제어 흐름 문을 사용하는 것은 권장되지 않습니다.