[JS] 예외처리가 뭐에요? (try-catch)

kimminjunnn·2025년 5월 25일

CS

목록 보기
24/34

예외 처리

우리는 프로그래밍을 하며 크고 작은 에러들을 만난다.

에러들을 만나면 슥 긁어서 구글링을 하며 해결을 하곤 하지만, 해결을 하는 것만큼 중요한 건 처음 코드를 구현할 때부터 에러가 날 상황을 예측하며 미리 설계해두는 것이다.

이를 프로그래밍에서는 '예외 처리 (exception handling)' 이라고 한다.

예외 처리를 잘 해두면 다음과 같은 장점이 있다:

프로그램의 갑작스런 종료를 방지할 수 있다.
→ 예상치 못한 오류로 인해 앱이 중단되는 것을 막고, 사용자 경험을 지킬 수 있다.

에러의 원인을 쉽게 파악할 수 있다.
→ 로그나 메시지로 에러 정보를 출력해두면, 디버깅이 훨씬 쉬워진다.

대체 흐름(Fallback)을 제공할 수 있다.
→ 예를 들어 네트워크 요청이 실패하면 로컬 데이터를 보여주는 등, 사용자에게 최소한의 경험을 보장할 수 있다.

보안적인 측면에서도 유리하다.
→ 민감한 정보가 노출되지 않도록 에러 상황을 통제할 수 있다.

모듈 간의 책임을 명확히 나눌 수 있다.
→ 어느 모듈에서 어떤 예외를 발생시키고, 누가 그것을 처리할지를 설계함으로써 코드 구조가 깔끔해진다.


try-catch문

자바스크립트 뿐만 아니라 많은 프로그래밍 언어에서 예외 처리 를 위해 try-catch (finally) 구문을 사용한다.

try {
  // 오류가 발생할 가능성이 있는 코드
} catch (error) {
  // 오류가 발생했을 때 실행되는 코드
} finally {
  // (선택) 오류와 관계없이 항상 실행되는 코드
}

try : 에러가 발생할 수 있는 코드를 작성


catch : 에러가 발생했을 때 실행되는 코드

에러에 대한 정보를 담고 있는 객체(e 매개변수)를 사용할 수 있다.

  • e.name : 에러 이름
  • e.message : 에러 상세 내용을 담고 있는 문자 메시지
  • e.stack : 현재 호출 스택. 에러를 유발한 중첩 호출들의 순서 정보를 가진 문자열로 디버깅 목적으로 사용된다.

e.stack에러가 발생했을 때의 호출 스택(Call Stack) 정보를 문자열로 담고 있는 속성

"이 에러가 어디서 어떻게 발생했는지를 추적할 수 있는 로그"를 담고 있다

에러 타입과 메시지: Error: 문제 발생!

어떤 함수에서, 어떤 파일의 몇 번째 줄에서 에러가 발생했는지

위에서 아래로 함수 호출 흐름이 기록됨 (가장 마지막에 호출된 함수가 맨 위)

a() 호출됨 → Stack: [a]

a() 내부에서 b() 호출 → Stack: [a, b]

b() 내부에서 c() 호출 → Stack: [a, b, c]

c()에서 throw new Error() → 💥 에러 발생

현재까지 호출된 함수들을 위에서 아래로 출력함
의미
at c에러가 실제 발생한 위치
at bc()를 호출한 함수
at ab()를 호출한 함수
at <anonymous>a()를 호출한 위치 (즉, 전역 실행 컨텍스트)

여기서 anonymous는 "익명 함수" 또는 "이름 없는 실행 컨텍스트"를 뜻함
브라우저 콘솔은 내부적으로 전체 입력을 익명 함수에 감싸서 실행하기에
그래서 function a() {}처럼 이름을 줬어도 결국 로 출력됨.


finally : 에러 발생 여부와 관계없이 항상 실행됨 (선택)

  • try블록 안에서의 에러 발생 여부와 관계 없이 무조건 실행되어야 하는 코드
  • return, break, continue등으로 코드의 실행 흐름이 즉시 이동되더라도 무조건 실행된다.

에러가 안 났을 때: try -> finally
에러가 났을 때: try -> 에러발생 -> catch -> finally

finally는 왜, 어떻게 쓸 수 있을까?

어떠한 로직에 대해 에러의 유무와 상관없이, 작업 후 초기화를 반드시 해야된다고 가정하자. 그래서 다음과 같이 try - catch 문 뒤에 작업 내역 삭제 로직을 실행하도록 구성하였다.

try {
  작업
} catch (e) {
  에러 핸들링
}

작업 내역 삭제

try문안에 작업이 에러가 뜨면 catch에서 핸들링을 하고 스크립트가 종료되게 되기 때문이다.
따라서 finally 예외처리를 명시함으로서 스크립트 종료되기전에 반드시 삭제가 실행되도록 지정해주어야 한다.

try {
  작업
} catch (e) {
  에러 핸들링
} finally {
  작업 내역 삭제
}

throw

자바스크립트에서는 개발자가 임의로 에러를 발생시킬 수 있다.
개발자가 예외를 던지면, 예외 객체가 생성되고, 이 객체는 프로그램 실행 중에 catch 블록에서 처리되게 된다.

try {
  const even = parseInt(prompt('짝수를 입력하세요'));
  if (even % 2 !== 0) {
    throw new Error('짝수가 아닙니다.');
  }
} catch (e) {
  alert(e.message);
}
// 3을 입력할 경우
// Error: 짝수가 아닙니다.

throw new Error('짝수가 아닙니다.') 에서
e.name = Error
e.message = 짝수가 아닙니다.

가 된다.


Error 의 종류

throw new Error 할때 이 'Error'에 들어가는 것이 e.name 인데 종류가 있다.

에러 이름설명발생 예시
Error모든 에러의 기본형. 커스텀 에러 생성에 사용됨throw new Error("기본 에러")
TypeError타입이 잘못되었을 때 발생null.f(), "str"()
ReferenceError정의되지 않은 변수에 접근 시 발생console.log(x) (x가 정의되지 않음)
SyntaxError문법이 잘못되었을 때 발생eval("foo bar")
RangeError값이 허용 범위를 벗어났을 때 발생new Array(-1)
EvalErroreval() 사용 관련 오류 (거의 발생 안함)(특정 환경에서만 발생)
URIErrorencodeURI, decodeURI에 잘못된 인자 사용 시 발생decodeURI("%")

또한 사용자가 Error의 새로운 종류를 만들어 정의하는 것도 가능하다.

사용자 정의 Error

class의 extends 를 통하여 Error의 클래스를 상속하여 정의한다.

// 사용자 정의 예외 타입
class MyError extends Error { // Error 부모 클래스를 상속한다.
constructor(message) {
  super(message);
  this.name = 'MyError';
}
}

try {
throw new MyError('Something went wrong');
} catch (error) {
// 에러 객체를 받아서 처리
if (error.name === "MyError") {
	console.log(error.message); // 'Something went wrong'
}
}

비동기 함수에서의 예외 처리

try - catch는 기본적으로 동기적으로 동작한다. 그래서 setTimeout처럼 비동기적으로 처리되는 함수에서 발생한 예외는 try-catch문에서 잡아낼 수 없다.

  try {
  setTimeout(function() {
     noSuchVariable; // 스크립트는 여기서 죽습니다.
  }, 1000);
} catch (e) { // 스크립트는 이미 멈췄기 때문에, 비동기 환경에서 뭐가 됬든 작동 안함.
  alert( "작동 멈춤" );
}

setTimeout에 넘겨진 익명 함수는 엔진이 try..catch를 떠난 다음에서야 실행되기 때문이다. 따라서 스케줄 된 함수 내부의 예외를 잡으려면, try..catch를 반드시 함수 내부에 구현해야 한다.

setTimeout(function() {
try {
  noSuchVariable; // 이제 try..catch에서 에러를 핸들링 할 수 있습니다!
} catch {
  alert( "에러를 잡았습니다!" );
}
}, 1000);
profile
Frontend Engineers

0개의 댓글