코린이 개념잡기/JavaScript

에러와 에러 객체

폭주하는 세발자전거 2024. 12. 18. 17:33

자바스크립트에서 에러가 발생하면 그 순간 프로그램 자체가 멈춰버리고 이후의 코드가 동작하지 않는다.

console.log('시작!');

const title = '해피 크리스마스!';
console.log(title);

console.log('끝!');

//출력 값
시작!
해피 크리스마스!
끝!

//하지만 만약 실수로 코드를 잘못 입력하게 되면..?

console.log('시작!');

const title = '해피 크리스마스!';
console.log(title);
console.log(christmas); //선언하지 않은 변수 or 함수를 값으로 입력

console.log('끝!');

//출력 값
시작!
해피 크리스마스!
Uncaught ReferenceError: christmas is not defined at index.js:19
//에러가 발생한 순간 프로그램이 멈춰버려서 이후에 나와야 하는 끝!은 출력되지 못한다.

자바스크립트에서는 에러가 발생하면 에러에 대한 정보를 name과 message라는 프로퍼티로 담고 있는 에러 객체가 만들어지는데, 대표적인 에러 객체는 SyntaxError, ReferenceError, TypeError 이다.

//존재하지 않는 함수나 변수를 호출할 때 발생되는 레퍼런스에러(ReferenceError)
//Uncaught ReferenceError: christmas is not defined at index.js:1

//잘못된 방식으로 자료형을 다루려고 할 때 발생되는 타입에러(TypeError)
const title = '코끼리';
console.log(title());

//Uncaught TypeError: title is not a function at index.js:6

//문법에 맞지 않을 때 발생되는 신택스에러(SyntaxError), 코드를 실행시키기도 전에 에러를 발생시킨다.(코드실행X)
const ti-tle = '코끼리';
console.log(title);

//Uncaught SyntaxError: Missing initializer in const declaration index.js:11

에러 객체는 직접 만들 수도 있다. new 키워드와 에러 객체 이름을 딴 함수를 통해 에러 객체를 만들 수 있고, throw 키워드로 에러를 발생시킬 수 있다.

throw new TypeError('타입 에러가 발생했습니다.');

try...catch문

try {
  // 실행할 코드
} catch (error) {
  // 에러 발생 시 동작할 코드
}

try...catch문은 자바스크립트에서 대표적인 에러 처리 방법이다.
try문 안에서 실행할 코드를 작성하고, try문에서 에러가 발생한 경우에 실행할 코드를 catch 문 안에 작성하면 되는데, 이 때 try문에서 발생한 에러 객체가 catch문의 첫 번째 파라미터로 전달된다. 만약, try문에서 에러가 발생하지 않을 경우 catch문의 코드는 동작하지 않는다.

try...catch문은 실행이 가능한 코드 내에서 에러를 다룬다. SyntaxError처럼 코드를 실행조차 하지 않는 에러는 try...catch문을 활용할 수 없다. 실행이 가능한 코드에서 발생한 에러를 예외(Exception)이라고 하는데, 실행가능한 코드에서 발생한 에러를 다루는 과정을 가리켜서 예외처리(Exception Handling)라고 한다.

try {
  console.log('에러 전');
  
  const codeName = 'Kelly';
  console.log('codeName');
  
  coneName = 'Alex'; //에러가 발생하는 지점
  
  const language = 'JavaScript';
  console.log(language);
} catch (error) {
  console.log('에러 후');
};

//출력 되는 값
에러 전
Kelly
에러 후

try 블록 내에서 에러가 발생된 시점 이후로는 실행되지 않고, 에러가 발생하면 catch문이 실행됐다. 만약 에러가 발생하는 지점을 없애준다면 코드는

try {
  console.log('에러 전');
  
  const codeName = 'Kelly';
  console.log('codeName');
  
  const language = 'JavaScript';
  console.log(language);
} catch (error) {
  console.log('에러 후');
};

//출력 되는 값
에러 전
Kelly
JavaScript

"에러 후"가 출력되지 않는다. try 문 안에 에러가 없기 때문에 catch문 안의 코드가 동작하지 않은것이다.

function printMembers(members) {
  for (const member of members) {
    console.log(member);
  }
};

const teamA = ['영진', '정윤', '혜미', '수진'];
printMembers(teamA);

const codeName = { name: 'Kelly' };
printMembers(codeName);

const teamB = ['종훈', '철수', '태희', '재석'];
printMembers(teamB);

//출력되는 값
영진
정윤
혜미
수진
//Uncaught TypeError: title is not a function at index.js:11

위 코드를 예시로 봤을 때 printMembers(codeName); 에러로 인해서 그 아래 코드는 실행되지 않고 프로그램이 멈췄다. 이런상황에서 콘솔창에 teamB에 접근해봐도 변수 teamB는 선언조차 되지 않았다.

콘솔창에서 보는 에러 상황

이럴 때 함수 내부에 try...catch문을 활용하게 되면 try 문 안에서 에러가 발생하더라도 catch문에서 에러 객체를 다루기 때문에 프로그램을 멈추지 않고 그 이후에 코드들을 안전하게 실행시킬 수가 있다

function printMembers(members) {
  try {
    for (const member of members) {
      console.log(member);
    }
  } catch (err) {
    console.error(err);
    alert(`${err.name}가 발생했습니다. 콘솔창을 확인해 주세요.`);
  }
};

const teamA = ['영진', '정윤', '혜미', '수진'];
printMembers(teamA);

const codeName = { name: 'Kelly' }; //배열이 아닌 객체값 사용, 객체는 for...of 문을 사용할 수 없다.
printMembers(codeName);

const teamB = ['종훈', '철수', '태희', '재석'];
printMembers(teamB);

방금 전과는 다르게 에러가 발생했다고 alert 창이 알려준다. 

콘솔창도 확인해보면 teamA의 요소들은 정상적으로 출력이 됐고, 에러 메세지도 똑같이 출력이 되긴 했지만, 에러가 발생한 다음에도 catch문을 통해 에러를 다뤘기 때문에 프로그램이 멈추지 않는다. (훨씬 안정적으로 프로그램을 만들 수 있다.)

그리고 try...catch문에서 에러의 유무와 상관없이 항상 동작해야할 코드가 필요하다면 finally문을 활용할 수 있다.

try {
  // 실행할 코드
} catch (error) {
  // 에러가 발상했을 때 실행할 코드
} finally {
  // 항상 실행할 코드
}

다시 말하자면, try문에서 어떤 코드를 실행할 때 에러 여부와 상관 없이 항상 실행할 코드를 작성하는 것이다.

function printMembers(...members) {
  for (const member of members) {
    console.log(member);
  }
}

try {
  printMembers('영훈', '윤수', '동욱');
} catch (err) {
  alert('에러가 발생했습니다!');
  console.error(err);
} finally {
  const end = new Date();
  const msg = `코드 실행을 완료한 시각은 ${end.toLocaleString()}입니다.`;
  console.log(msg);
}

+ finally문에서 에러가 발생한 경우에는 다시 그 위에 있는 catch문으로 넘어가진 않는다. 마아아안약에 finally문에서도 에러 처리가 필요한 경우에는 try...catch문을 중첩해서 활용하는 방법이 있다.

try {
  try {
    // 실행할 코드
  } catch (err) {
    // 에러가 발생했을 때 실행할 코드
  } finally {
    // 항상 실행할 코드
  }
} catch (err) {
  // finally문에서 에러가 발생했을 때 실행할 코드
}