예외와 에러 처리

Grace·2022년 6월 8일
0

JavaScript

목록 보기
4/11
post-thumbnail

예외 처리는 에러를 컨트롤하는 매커니즘입니다. 에러 처리라고 하지 않고 예외 처리라고 하는 이유는 예상치 못한 상황에 대처하는 방식이기 때문입니다.
예상한 에러와 예상치 못한 에러(예외)를 구분하는 기준은 불명확하고 상황에 따라 크게 달라집니다.

Error 객체

자바스크립트에는 내장된 Error 객체가 있고 이 객체는 에러 처리에 간편하게 사용할 수 있습니다. Error 인스턴스를 만들면서 에러 메시지를 지정할 수 있습니다.

const err = new Error('invalid email')

Error 인스턴스를 만드는 것만으로는 아무 일도 일어나지 않습니다. 이 인스턴스는 에러와 통신하는 수단입니다.

function validateEmail(email){
  return email.mach(/@/) ? email : new Error(`invalid email : ${email}`)
}

이 함수를 사용할 때는 instanceof 연산자를 써서 Error 인스턴스가 반환됐는지 확인합니다. 에러 메시지는 message 프로퍼티에 있습니다.

const email = 'jane@doe.com'

const validatedEmail = vaildateEmail(email)
if(validatedEmail instanceof Error) {
  console.log(`Error: ${validatedEmail.message}`)
} else {
  console.log(`Valid email: ${validatedEmail}`)
}

이 방법도 Error 인스턴스를 활용하는 유효하고 유용한 방법이긴 하지만, Error 인스턴스는 예외 처리에서 더 자주 사용됩니다.

try/catch와 예외 처리

예외 처리는 try...catch 문을 사용합니다. 뭔가를 시도하고, 예외가 있으면 그것을 캐치한다는 뜻이 잘 드러납니다. 예상치 못한 에러에 대처하려면 try...catch 문으로 코드 전체를 감쌀 수 있습니다

const email = null

try {
  const validatedEmail = vaildateEmail(email)
  if(validatedEmail instanceof Error) {
    console.log(`Error: ${validatedEmail.message}`)
  } else {
    console.log(`Valid email: ${validatedEmail}`)
  }
} catch(err) {
  console.error(`Error: ${err.message}`)
}

에러를 캐치했으므로 프로그램은 멈추지 않습니다. 에러를 기록하고 계속 진행할 수 있습니다. 물론 문제가 해결된 건 아닙니다. 유효한 이메일 주소가 필수라면 프로그램이 계속 진행할 수 없기는 마찬가지겠죠.
실행 흐름은 에러가 일어나는 즉시 catch 블록으로 이동합니다.

에러 일으키기

직접 에러를 일으켜서 예외 처리 작업을 시작할 수도 있습니다.

예외 처리 기능이 있는 다른 언어와는 달리, 자바스크립트는 에러를 일으킬 때 꼭 객체만이 아니라 숫자나 문자열 등 어떤 값이든 catch절에 넘길 수 있습니다. 하지만 Error 인스턴스를 넘기는 것이 가장 편리합니다. 대부분의 catch블럭은 Error 인스턴스를 받을 것이라고 간주하고 만듭니다.

function billPay(amount, payee, account) {
  if(amount > account.balance) throw new Error("insufficient funds")
  account.transfer(payee, amount)
}

throw를 호출하면 현재 함수는 즉시 실행을 멈춥니다.

예외 처리와 호출 스택

프로그램이 함수를 호출하고, 그 함수는 다른 함수를 호출하고, 호출된 함수는 또 다른 함수를 호출하는 일이 반복됩니다. 자바스크립트 인터프리터는 이런 과정을 모두 추적하고 있어야 합니다. 함수 a가 함수 b를 호출하고 함수 b는 함수 c를 호출한다면, 함수 c가 실행을 마칠 때 실행 흐름은 함수 b로 돌아갑니다. 그리고 b가 실행을 마칠 때 실행 흐름은 함수 a로 돌아갑니다. 바꿔 말해, c가 실행 중일 때는 a와 b는 완료될 수 없습니다. 이렇게 완료되지 않은 함수가 쌓이는 것을 호출 스택이라 부릅니다.

c에서 에러가 일어난다면 b는 c가 반환하는 값을 사용해야 할 수도 있으므로, b에서도 에러가 일어납니다. 따라서 a에서도 에러가 일어납니다. 요약하자면, 에러는 캐치될 때까지 호출 스택을 따라 올라갑니다.

에러는 호출 스택 어디에서든 캐치할 수 있습니다. 어딘가에서 이 에러를 캐치하지 않으면 자바스크립트 인터프리터는 프로그램을 정지시킵니다. 이런 것을 처리하지 않은 예외, 캐치하지 않은 예외라고 부르며 프로그램이 충돌하는 원인이 됩니다. 에러가 일어날 수 있는 곳은 정말 다양하므로 가능한 에러를 모두 캐치하기에는 정말 어렵습니다.

에러를 캐치하면 호출 스택에서 문제 해결에 유용한 정보를 얻을 수 있습니다.

대부분의 자바스크립트 환경에서 Error 인스턴스에는 스택을 문자열로 표현한 stack 프로퍼티가 있습니다. 이 기능은 자바스크립트 표준은 아니지만 대부분의 환경에서 지원합니다.

function a() {
  console.log('a: calling b')
  b()
  console.log('a: done')
}
function b() {
  console.log('b: calling c')
  c()
  console.log('b: done')
}
function c() {
  console.log('c: throwing error')
  throw new Error('c error')
  console.log('c: done')
}
function d() {
  console.log('d: calling c')
  c()
  console.log('d: done')
}

try{
  a()
} catch(err){
  console.log(err.stack)
}

try {
  d()
} catch(err) {
  console.log(err.stack)
}
a: calling b
b: calling c
c: throwing error
c@debugger eval code:13:1
b@debugger eval code:8:4
a@debugger eval code:3:4
@debugger eval code:23:4

d: calling c
c: throwing error
c@debugger eval code:13:1
d@debugger eval code:18:4
@debugger eval code:29:4

@ 기호가 있는 행은 스택 추적이며 '가장 깊은' 함수(c)에서 시작하고 함수가 남지 않았을 때 (브라우저 자체) 끝납니다. 스택 추적 두 가지가 나타나 있습니다. 하나는 a가 b를, b가 c를 호출했음을 보여주고 다른 하나는 d에서 c를 호출했음을 보여줍니다.

try...catch...finally

try 블록의 코드가 HTTP 연결이나 파일 같은 일종의 '자원'을 처리할 때가 있습니다. 프로그램에서 이 자원을 계속 가지고 있을 수는 없으므로 에러가 있든 없든 어느 시점에서는 이 자원을 해제해야 합니다. try 블럭에는 문을 원하는 만큼 쓸 수 있고, 그 중 어디에서든 에러가 일어나서 자원을 해제할 기회가 아예 사라질 수도 있으므로 try 블럭에서 자원을 해제하는 것은 안전하지 않습니다. 에러가 일어나지 않으면 실행되지 않는 catch 블럭 역시 안전하지 않습니다. 이런 상황에서 finally 블럭이 필요합니다. 에러가 일어나든, 일어나지 않든 반드시 호출됩니다.

try{
  console.log("this line is executed...")
  throw new Error("whoops")
  console.log("this line is not...")
} catch(err) {
  console.log("there was an error...")
} finally {
  console.log("...always executed")
  console.log("perform cleanup here")
}
profile
기술블로그 이전:: https://meercat.tistory.com/

0개의 댓글