try-catch는 모든 에러를 처리할 수 있을까?

재능없는 개발자·2023년 5월 30일
0

try-catch는 모든 에러를 처리할 수 있을까?

async function test() {
  try {
    await func1()
    await func2()
    await func3()
    await func4()
  } catch (error) {
    console.error(error)
    next(error)
  }
}

또한 이런 경우 func1~func4번 각각 함수마다 try-catch를 작성해줘야 할까?

throw

function test(x) {
    if ( x < 0 ) {
    	throw "에러다 에러";
    }
}

console.log(test());

try-catch전에 먼저 throw에 대해 알아보자. 우리는 throw를 통해서 강제로 예외를 발생시킬 수 있다.

throw 뒤에 문자열을 적어주면 해당 문자열로 에러를 표시해 준다.
function test(x) {
    if ( x < 0 ) {
    	throw new Error("에러다 에러");
    }
}

console.log(test());

하지만 보통은 throw뒤에 Error객체를 사용한다.

Error 객체를 사용하게 되면 에러가 어디서 발생했는지에 대한 정보를 얻을 수 있다.

에러가 발생하게 되면 다음 코드는 실행되지 않는다. 예외가 발생하면 즉시 프로그램 실행을 중단하고, 가장 가까운 예외 처리기로 넘어간다. 만약 부모로 거슬러 올라가면서 예외 처리기를 찾지 못한다면 에러로 취급되어 사용자에게 보고된다.

예외 처리기는 우리가 알다시피 try-catch의 catch이다. 부모에서 이미 try-catch를 적어주었다면, 자식은 사용하지 않아도 되는걸 알았다. 따라서 위의 코드는 test함수에서 try-catch를 작성해줬기 때문에, 각각의 함수에 대해서는 try-catch를 작성해주지 않아도 된다는 것을 깨달았다.

try-catch

function test(x) {
    if ( x < 0 ) {
    	throw new Error("에러다 에러");
    }
}

try {
   test(-1)
} catch (error) {
   console.log(error)
}

catch는 에러를 발생시키지 않고 에러를 handling하게 해준다.

router.get("/test", (req, res, next) => {
    try {
    	throw new Error("errorrrr!!");
    } catch (error) {
    	return console.error(error);
    }
});

catch는 Error객체 안의 메세지를 인자로 받아, 어디서 에러가 발생 하였는가와, 에러 메세지를 띄워준다. 메세지만 출력하고 싶다면, console.error(error.message)를 해주면 된다.

async function test() {
  throw Error("error!");
}

router.get("/test", async (req, res, next) => {
  await test();
});

하지만, 이런 비동기 함수가 들어간 코드를 보자. 실제 어플리케이션에선 대부분 이런 비동기 함수가 쓰인다.

app이 crashed되면서 서버가 멈추어 버렸다. 멈추면서 다음 요청이 전달되지 않는다.

이처럼 비동기함수에서의 에러를 처리하기 위해 꼭 try-catch를 사용해야한다.

await / async를 사용할때 try-catch를 사용하도록 하자.

async function test() {
  throw Error("error!");
}

router.get("/test", async (req, res, next) => {
  try {
    await test();
  } catch (error) {
    return console.error(error);
  }
});

이렇게 try-catch문으로 잡아준다면, 프로세스가 멈추는 걸 막아줄 수 있다.

그렇다면 모든 에러를 잡아줄 수 있을까?

프로젝트에 실시간 알림 기능을 도입해야해서 node의 scheduler를 이용해야 했다.

async function test() {
  cron.schedule("50 * * * * *", function () {
      throw new Error("error on schedule!");
  });
}

router.get("/test", async (req, res, next) => {
  try {
    await test();
  } catch (error) {
    console.error(error);
    next(error);
  }
});

저번 글에 의하면, 에러는 예외처리기를 거슬러 올라가며 찾는다 했다. 그렇다면 이 throw 에러도 처리되어야 했지만, cron에 설정해둔 대로 50초가 되었을때, 아무 일도 일어나지 않았다. throw에러가 무시된 것이다.

인터넷을 찾아보니 try-catch는 동기적으로 작동한다고 나와있다. scheduler에 적혀진 코드는 try-catch를 떠난 뒤에야 실행되기 때문에,
cron처럼 schedule된 코드에서 발생한 에러는 잡을 수 없는 것이다. 때문에 setTimeout에 적혀진 callback함수에도 try-catch를 적용하지 않으면 예외를 잡을 수 없다.

해결방법

async function test() {
  cron.schedule("50 * * * * *", function () {
    try {
      console.log("node-cron 실행됨");
      throw new Error("error on schedule!");
    } catch (error) {
      console.error(error);
    }
  });
}

router.get("/test", async (req, res, next) => {
  try {
    await test();
  } catch (error) {
    console.error(error);
    next(error);
  }
});

간단하다, 이렇게 scheduler에 적혀진 function에도 try-catch를 적용해주면 된다.

이렇게 하면, 시간이 한참 지나고 실행된 scheduler의 함수에서 발생한 에러도 catch할 수 있다.

profile
https://www.youtube.com/watch?v=__9qLP846JE

0개의 댓글