Javascript

try catch문에 finally가 필요한 이유

tea-tea 2024. 11. 29. 14:19

finally 블록은 try catch 문의 실행 이후 무조건 실행되므로 리소스 해제 등의 작업에 적합하다.

그런데, 굳이 finally를 쓰지 않고 아래처럼 try catch 뒤에 실행될 코드를 적으면 되는거 아닐까.

function init() {
  try {
    throw new Error("에러1");
  } catch (err) {
    console.error(err);
  }

  console.log("후속 작업");
}

init();

 

아니다. finally만의 고유 기능 때문에 사용해야 하는 경우가 있다.

 

1. catch문에서 에러가 날 경우, 일반 코드는 실행되지 않지만, finally 문은 실행된다.

finally를 사용하는 경우

function useFinally() {
  try {
    throw new Error("에러1");
  } catch (err) {
    throw new Error("에러2");
  } 
  
  // catch문에서 에러가 나도 무조건 실행이 됨
  finally {
    console.log("후속 작업");
  }
}

useFinally();

 

finally를 사용하지 않는 경우

function notUseFinally() {
  try {
    throw new Error("에러1");
  } catch (err) {
    throw new Error("에러2");
  }
  
  // catch 문에서 에러가 나면 실행되지 않음
  console.log("후속 작업");
}

notUseFinally();

 

2. try문이나 catch 문에서 early return으로 함수를 조기 종료해도 finally는 실행된다.

finally를 사용하는 경우

function useFinally() {
  try {
    return "try 반환값";
  } catch (err) {
    console.error(err);
  } finally {
    console.log("후속 코드 실행");
  }
}
console.log(useFinally());

// 콘솔 출력 결과
// "후속 코드 실행"
// "try 반환값"

 

finally를 사용하지 않는 경우

function notUseFinally() {
  try {
    return "try 반환값";
  } catch (err) {
    console.error(err);
  }
  console.log("후속 코드 실행");
}

console.log(notUseFinally());

// 콘솔 출력 결과
// "try 반환값"

 

주의사항. finally의 반환 값 혹은 예외는 기존 반환 값을 덮어쓴다.

표면 상으로는 finally가 먼저 실행된 후 early return이 실행되는 것 처럼 보이지만,

실제로는 early return문이 먼저 실행되되, 이것이 반환 값으로 예약되고, 이후 finally가 완료된 후에 호출자 함수로 반환이 이뤄진다. (즉, return => 리턴 값 예약 => finally {} => 호출자 함수로 리턴 값 및 제어권 반환 순서이다)

여기서 더 주의할 건, finally의 return문이나 throw문은 try나 catch의 반환값 혹은 던진 예외를 덮어쓸 수 있다

 

아래 경우에서는 모두 finally 반환 값이 다른 조기 반환값이나 예외를 덮어쓴다.

 

 

try에서 리턴하는 경우

function useFinally() {
  try {
    return "try 반환값";
  } catch (err) {
    console.error(err);
  } finally {
    return "finally 반환값";
  }
}

console.log(useFinally());

 

catch에서 에러를 던지는 경우

function useFinally() {
  try {
    throw new Error("에러1");
  } catch (err) {
    console.error(err);
  } finally {
    return "finally 반환값";
  }
}

console.log(useFinally());

 

단, try catch 블록보다 먼저 실행된 조기 반환은 애초에 try catch 블록이 실행조차 안되었으므로, 조기 반환 값이 최종적으로 반환된다.

function useFinally() {
  return "일반 반환";

  try {
    throw new Error("에러1");
  } catch (err) {
    console.error(err);
    throw new Error("에러1");
  } finally {
    return "finally 반환값";
  }
}

console.log(useFinally());