사라지는 원본 에러
카테고리: error-handling · 이 패턴은 8기 PR 26건 중 17건에서 관찰됩니다.
catch 블록에서 원본 에러를 버리고 고정 문자열로 치환한다.
catch에서 잡은 원본 Error 객체를 그대로 버리고 동일한 안내 문자열만 다시 던지거나 표시하는 패턴입니다. 타임아웃, 네트워크 오류, HTTP 4xx/5xx 응답, 파싱 실패가 모두 한 종류의 에러로 평탄화되기 때문에 호출자는 원인을 구분할 수 없고 디버깅 단서도 함께 사라집니다.
문제 코드
다음은 실제 8기 크루 PR에서 추출한 코드입니다. 작성자는 익명 처리하고 원본 PR 링크만 남깁니다.
사례 1
} catch (error) {
const newError = new Error();
newError.name = "API 요청중 에러가 발생했습니다."
newError.message = `${error instanceof Error ? (error.message ?? "not found error message") : "not found error"} (${endpoint})`
throw newError
}타임아웃, 네트워크, HTTP 응답 오류가 모두 동일한 Error 한 종류로 평탄화되어 호출자가 원인을 구분할 수 없습니다. 원본: PR #265 ·
src/utils.ts
사례 2
export async function getPopularMovies(page) {
try { return await fetchApi(POPULAR_PATH, page); }
catch (error) { throw new Error("영화 데이터를 불러오는 중 오류가 발생했습니다."); }
}원본
error인자를 사용하지 않고 동일 문구로 새 Error를 만들기 때문에 status code와 message가 모두 사라집니다. 원본: PR #275 ·src/features/movieModel.ts
사례 3
} catch (error) {
if (error instanceof Error && error.name === "AbortError") {
throw new Error(TIMEOUT_ERROR_MESSAGE);
}
throw error;
}AbortError를 새 Error로 감쌀 때
cause옵션을 쓰지 않아 원본 스택과 메시지가 끊깁니다. 원본: PR #287 ·src/API/api.ts
스스로 진단해보기
해설을 펼치기 전에 다음 질문에 답한다.
- 이 catch 블록에서 사라지는 정보가 무엇인지 한 줄로 적는다.
- 사용자에게 보여 줄 메시지와 개발자가 콘솔에서 확인할 정보를 같은 데이터로 합쳐도 되는지 판단한다.
- 이 함수의 호출자가 네트워크 오류와 4xx 응답을 구분해서 처리해야 할 상황을 한 가지 떠올린다.
해설
해설 보기
catch에서 잡은 error는 원인을 식별할 수 있는 거의 모든 정보를 담고 있는 객체입니다. 여기에는 원본 메시지, 스택 트레이스, 발생 위치, 그리고 fetch 실패라면 TypeError·AbortError 같은 구체적인 타입 정보가 포함됩니다. 고정 문자열로 새 Error를 만들어 다시 던지는 순간 이 정보들은 모두 버려지고, 호출자는 “무언가 잘못되었다”라는 사실 외에는 아무것도 알 수 없게 됩니다.
이 패턴은 세 가지 층에서 문제를 일으킵니다. 첫째, 디버깅 비용이 올라갑니다. 프로덕션에서 에러 리포트를 받아도 스택 트레이스가 throw new Error(...)가 위치한 줄에서 끊겨 있어 원인을 추적할 수 없습니다. 둘째, 호출자의 분기 처리가 불가능해집니다. 네트워크 단절과 404 응답은 사용자에게 다르게 안내해야 하지만, 문자열 하나로 좁혀지면 구분할 근거가 사라집니다. 셋째, 재시도 정책을 세울 수 없습니다. 일시적 네트워크 오류만 재시도해야 하는 상황에서 모든 에러가 동일하게 보이면 무차별 재시도나 무차별 포기 중 하나를 선택할 수밖에 없습니다.
해결의 핵심 개념은 “정보 보존”입니다. ES2022부터 Error 생성자의 cause 옵션을 사용하면 원본 에러를 체인으로 연결할 수 있습니다. 또는 커스텀 에러 클래스 를 만들어 status code와 원인 타입을 구조화된 필드로 남기는 방법도 있습니다. 어느 쪽이든 “상위 계층에 try...catch로 맥락을 덧붙이되 원본은 버리지 않는다”는 원칙은 동일합니다.
개선 방향
Before
} catch (error) {
throw new Error("영화 데이터를 불러오는 중 오류가 발생했습니다.");
}After
} catch (error) {
throw new Error(`영화 데이터를 불러오는 중 오류가 발생했습니다. (${endpoint})`, {
cause: error,
});
}핵심 변화는 Error 생성자의 두 번째 인자 { cause }로 원본 에러를 체인에 남겼다는 점입니다. 사용자에게 보여 줄 메시지는 그대로 유지하면서도 개발자 도구와 로그 수집기에서는 원본 스택과 타입을 그대로 추적할 수 있습니다.




