본문 바로가기

Javascript

try catch문에 finally가 필요한 이유

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());