try-catch — 에러를 다루는 기본기
이 토픽을 마치면
try-catch의 동작 원리를 이해하고, 어디서 에러를 잡아야 하는지 판단할 수 있게 됩니다.
에러가 나면 프로그램이 멈춥니다
const data = JSON.parse("not json");
console.log("이 줄은 실행 안 됩니다");JSON.parse가 에러를 던지면, 그 아래 코드는 모두 실행되지 않습니다. 브라우저에서는 해당 스크립트 실행이 멈춥니다.
try-catch로 에러를 잡습니다
try {
const data = JSON.parse("not json");
} catch (error) {
console.log("파싱 실패:", error.message);
}
console.log("이 줄은 실행됩니다");try 블록 안에서 에러가 발생하면, 즉시 catch 블록으로 이동합니다. 프로그램이 멈추지 않습니다. catch 이후의 코드도 정상 실행됩니다.
error 객체는 .message(에러 메시지)와 .stack(호출 스택) 속성을 갖습니다.
throw로 에러를 직접 던집니다
JavaScript가 자동으로 던지는 에러 말고, 직접 에러를 던질 수도 있습니다:
function divide(a, b) {
if (b === 0) {
throw new Error("0으로 나눌 수 없습니다");
}
return a / b;
}
try {
divide(10, 0);
} catch (error) {
console.log(error.message); // "0으로 나눌 수 없습니다"
}throw는 함수 실행을 즉시 중단하고, 가장 가까운 catch로 에러를 전달합니다. 중간에 있는 함수들은 전부 건너뜁니다.
finally — 무조건 실행
try {
const file = openFile("data.txt");
processFile(file);
} catch (error) {
console.log("처리 실패");
} finally {
closeFile(file);
}finally는 에러가 나든 안 나든 무조건 실행됩니다. 파일 닫기, 네트워크 연결 해제, 로딩 상태 초기화 같은 "정리 작업"에 씁니다.
setLoading(true);
try {
const data = await fetchData();
setData(data);
} catch (error) {
setError(error.message);
} finally {
setLoading(false);
}async/await와 try-catch
비동기 함수에서도 try-catch는 동일하게 동작합니다:
async function loadUser(id) {
try {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return await res.json();
} catch (error) {
console.log("사용자 로드 실패:", error.message);
return null;
}
}await 앞에 try를 쓰면 네트워크 에러와 HTTP 에러 모두 잡을 수 있습니다.
어디서 잡아야 하나
모든 곳에 try-catch를 넣으면 코드가 지저분해집니다. 원칙이 있습니다:
에러를 복구할 수 있는 곳에서 잡습니다. API 호출이 실패하면 에러 메시지를 보여주거나 재시도할 수 있는 곳에서 잡습니다. 단순히 로그만 찍을 거면 잡을 필요가 없습니다.
// 나쁜 예: 잡기만 하고 아무것도 안 함
try {
doSomething();
} catch (e) {
// 무시
}
// 좋은 예: 사용자에게 피드백을 줌
try {
await saveData(formData);
showSuccess("저장되었습니다");
} catch (error) {
showError("저장 실패: " + error.message);
}핵심
try-catch는 에러가 프로그램을 멈추지 않게 합니다.throw로 의도적으로 에러를 던지고, 가장 가까운catch가 잡습니다. 에러를 복구할 수 있는 곳에서만 catch하세요.