[JavaScript] 오류 처리(try catch) 및 에러 핸들러

기본적인 try...catch 사용법

try 블록 내의 코드를 실행하다가 오류가 발생하면, 즉시 실행이 중단되고 제어 흐름이 catch 블록으로 넘어갑니다. catch 블록은 발생한 오류 객체를 매개변수로 받아, 오류의 상세 정보를 확인하고 처리할 수 있습니다.

try {
  console.log("프로그램을 시작합니다.");

  // 존재하지 않는 함수 호출로 인해 ReferenceError 발생
  undefinedFunction();

  console.log("이 코드는 실행되지 않습니다.");
} catch (error) {
  console.error("오류가 발생했습니다.");
  console.error("오류 이름:", error.name);
  console.error("오류 메시지:", error.message);
}

console.log("프로그램이 계속 실행됩니다.");

// 출력
// 프로그램을 시작합니다.
// 오류가 발생했습니다.
// 오류 이름: ReferenceError
// 오류 메시지: undefinedFunction is not defined
// 프로그램이 계속 실행됩니다.
  • undefinedFunction()을 호출하는 부분에서 ReferenceError가 발생하여 catch 블록으로 제어가 넘어갑니다.
  • catch 블록에서는 오류 메시지를 콘솔에 출력하고, 프로그램은 정상적으로 종료되지 않고 계속 실행됩니다.

try..catch는 런타임 에러와 동기적 코드만 처리

try...catch 구문은 코드가 정상적으로 파싱되고 실행되는 경우에만 동작합니다. 구문 오류(SyntaxError) 는 코드가 실행되기 전에 파싱 단계에서 발생하므로 try...catch로 잡아낼 수 없습니다. 또한, 비동기 코드 내에서 발생하는 오류도 해당 비동기 코드 내부에서 처리하지 않으면 try...catch로 잡아낼 수 없습니다.

더보기

비동기 코드란
자바스크립트 비동기 코드는 작업의 완료를 기다리지 않고 다음 코드를 실행합니다.

구문 오류 예시

try {
  // 잘못된 문법으로 인해 파싱 단계에서 오류 발생
  eval("function invalidSyntax {");
} catch (error) {
  console.error("이 메시지는 출력되지 않습니다.");
}

console.log("이 메시지도 출력되지 않습니다.");
  • eval 함수 내의 잘못된 코드는 구문 오류를 발생시키며, 이는 파싱 단계에서 감지되므로 catch 블록으로 제어가 넘어가지 않습니다.

비동기 코드 오류 예시

try {
  setTimeout(function () {
    // 비동기 함수 내에서 발생하는 오류는 여기서 처리해야 합니다.
    nonExistentFunction();
  }, 1000);
} catch (error) {
  console.error("이 메시지는 출력되지 않습니다.");
}
  • setTimeout 콜백 함수 내에서 발생한 오류는 비동기적으로 실행되므로, 외부의 try...catch로 잡아낼 수 없습니다.
  • 이러한 경우, 비동기 함수 내부에서 try...catch를 사용해야 합니다.

비동기 코드 올바른 오류 처리

setTimeout(function () {
  try {
    nonExistentFunction();
  } catch (error) {
    console.error("비동기 코드에서 오류를 잡아냈습니다.");
    console.error("오류 메시지:", error.message);
  }
}, 1000);

throw 연산자를 사용하여 사용자 정의 오류 발생시키기

자바스크립트에서는 throw 연산자를 사용하여 명시적으로 오류를 발생시킬 수 있습니다. 이는 오류 조건을 감지했을 때 예외를 발생시켜 호출자에게 알려주는 데 유용합니다. 일반적으로 내장된 Error 객체나 이를 상속받은 객체를 사용하여 일관된 오류 정보를 전달합니다.

function calculateSquareRoot(number) {
  if (typeof number !== "number") {
    throw new TypeError("입력 값은 숫자여야 합니다.");
  }
  if (number < 0) {
    throw new RangeError("음수의 제곱근은 실수가 아닙니다.");
  }
  return Math.sqrt(number);
}

try {
  const result = calculateSquareRoot(-9);
  console.log("결과:", result);
} catch (error) {
  console.error("오류 종류:", error.name);
  console.error("오류 메시지:", error.message);
}

오류의 재발생(Rethrowing)

catch 블록에서 모든 오류를 처리하는 대신, 일부 오류만 처리하고 나머지는 다시 발생시켜 상위 호출자로 전달할 수 있습니다. 이를 통해 오류 처리를 보다 세밀하게 관리할 수 있습니다.

function readJSON(jsonString) {
  try {
    const data = JSON.parse(jsonString);
    if (!data.name) {
      throw new SyntaxError("필수 속성 'name'이 누락되었습니다.");
    }
    return data;
  } catch (error) {
    if (error instanceof SyntaxError) {
      console.error("JSON 파싱 오류:", error.message);
    } else {
      // 예상치 못한 오류는 다시 발생시켜 상위에서 처리하도록 합니다.
      throw error;
    }
  }
}

try {
  const obj = readJSON('{"age": 30}');
  console.log(obj);
} catch (error) {
  console.error("프로그램 실행 중 오류 발생:", error.message);
}

finally 블록

finally 블록은 try...catch 문에서 선택적으로 사용할 수 있으며, 오류 발생 여부와 관계없이 항상 실행됩니다.

function connectToServer() {
  console.log("서버에 연결을 시도합니다.");
  throw new Error("서버 연결 실패");
}

try {
  connectToServer();
} catch (error) {
  console.error("오류 발생:", error.message);
} finally {
  console.log("연결 시도 종료");
}

전역 오류 핸들러를 통한 예외 처리

try...catch로 잡아내지 못한 오류를 처리하기 위해 브라우저나 Node.js 환경에서는 전역 오류 핸들러를 제공합니다.

브라우저에서의 전역 오류 핸들러 (window.onerror)

window.onerror = function (message, source, lineno, colno, error) {
  console.error("전역 오류 발생:", message);
  console.error("오류 발생 위치:", `${source} (${lineno}:${colno})`);
  console.error("오류 객체:", error);
  // 오류 정보를 서버로 전송하거나 사용자에게 알림을 표시하는 등의 처리 가능
};

// 정의되지 않은 함수 호출로 인해 오류 발생
undefinedFunction();

사용자 정의 오류 클래스 만들기

class ValidationError extends Error {
  constructor(message) {
    super(message);
    this.name = "ValidationError";
  }
}

function validateUser(user) {
  if (!user.name) {
    throw new ValidationError("사용자 이름이 필요합니다.");
  }
  if (!user.age || user.age < 0) {
    throw new ValidationError("유효한 나이가 필요합니다.");
  }
}

try {
  const user = { age: -5 };
  validateUser(user);
} catch (error) {
  if (error instanceof ValidationError) {
    console.error("유효성 검사 오류:", error.message);
  } else {
    console.error("예상치 못한 오류:", error.message);
  }
}