자바스크립트에서 직접 throw한 예외의 스택 트레이스를 얻는 방법
1. 문제 정의
자바스크립트에서 예외를 **직접 throw**할 때, 브라우저(Firebug 등) 콘솔에는 단순한 에러 메시지만 출력되고 스택 트레이스(함수가 어디서 어떻게 호출됐는지의 경로)가 보이지 않는 경우가 있습니다.
에러 원문은 다음과 같습니다.
If I throw a JavaScript exception myself (eg,
throw "AArrggg"), how can I get the stack trace (in Firebug or otherwise)? Right now I just get the message.
즉, 아래처럼 재귀적으로 호출하는 함수에서 예외가 발생했을 때, 어떤 경로를 거쳐 이 예외가 던져졌는지를 알고 싶은 상황입니다.
| |
이 코드에서 bar(1)이 호출되는 순간 예외가 던져지지만, 브라우저는 던진 문자열("Oh no! 'n' is too small!")만 보여줄 뿐 foo → bar → bar로 이어진 호출 체인을 함께 보여주지 않습니다.
| 증상 | 설명 |
|---|---|
| 출력 내용 | 에러 메시지 텍스트만 표시 |
| 누락되는 정보 | 호출한 함수 이름, 소스 URL, 호출 체인 (스택 트레이스) |
| 발생 조건 | throw로 직접 예외를 던질 때 |
2. 원인 탐구
예외를 throw할 때 스택 트레이스가 안 보이는 이유는, 직접 던진 예외가 자바스크립트 엔진이 생성한 Error 객체가 아니기 때문입니다.
- 엔진이 내부적으로 발생시킨 예외(예:
TypeError,ReferenceError)는 언어 런타임이 예외를 만들 때 그 시점의 호출 스택을 함께 기록합니다. - 반면
throw "문자열"처럼 문자열을 던지면 해당 문자열은 단순한 값일 뿐, 스택 정보를 담고 있는Error객체가 아닙니다. - 따라서 브라우저는 화면에 표시할 메시지(문자열)는 확인할 수 있지만, 그 문자열에 스택 정보가 없어서 호출 경로를 알려줄 수 없습니다.
여기서 핵심은 “던져진 값에 스택 정보가 있느냐 없느냐"입니다.
3. 근본 원인 분석
근본 원인은 다음과 같이 정리할 수 있습니다.
직접 던진 예외 값에는 스택 트레이스 정보가 기본적으로 포함되어 있지 않다.
throw에 전달된 값(문자열, 숫자 등)은 그 자체로는 호출 스택을 알지 못합니다.- 스택 트레이스는
Error객체가 생성될 때 캡처됩니다. 따라서Error객체(new Error())를 만들거나, 이미 만들어진Error의stack속성을 읽어야 호출 체인을 얻을 수 있습니다. - 또한 콘솔에 출력하려면
console.trace()처럼 호출 시점의 스택을 명시적으로 출력하는 메서드를 호출해야 합니다.
정리하면, “직접 던진 값에는 스택이 없다 → 스택을 얻으려면 스택을 제공하는 객체/메서드를 사용해야 한다"는 것이 곧 근본 원인입니다.
| 비교 대상 | 스택 정보 포함 여부 | 예시 |
|---|---|---|
throw "문자열" | ❌ 없음 | throw "AArrggg" |
new Error().stack | ✅ 있음 | var err = new Error(); return err.stack; |
console.trace() | ✅ 있음 (콘솔 출력) | console.trace(); |
4. 코드 해결책
가장 간단한 두 가지 방법을 소개합니다.
방법 1: console.trace() 호출 (권장, 최신 브라우저)
모던 브라우저에서는 예외를 던지기 직전에 console.trace()를 호출하면 현재 호출 스택이 콘솔에 출력됩니다.
| |
실행 결과 콘솔에 foo → bar → bar 순서의 호출 체인(함수 이름과 라인, URL)이 출력됩니다.
방법 2: new Error().stack 속성 사용
Error 객체는 생성되는 순간 stack 속성에 호출 스택을 기록합니다. 이 속성을 읽으면 함수 이름, URL, 호출 체인을 문자열로 얻을 수 있습니다.
| |
이 함수를 호출하면 다음과 같은 형태의 출력이 생성됩니다.
| |
각 줄은 호출한 함수의 이름과 소스 URL 및 라인 번호, 그리고 그 함수를 호출한 상위 함수로 이어지는 체인을 보여줍니다.
방법 비교
| 방법 | 장점 | 사용 예시 |
|---|---|---|
console.trace() | 한 줄로 즉시 콘솔 출력, 최신 브라우저에서 안정적 | 디버깅 중 스택이 궁금할 때 |
new Error().stack | 문자열로 반환해 로그/전송 등에 활용 가능 | 스택을 변수에 저장하거나 서버로 보낼 때 |
예외 처리(handling)와 함께 사용할 때 참고
예외를 catch한 뒤에도 Error 객체의 stack을 출력할 수 있습니다.
| |
단, throw "문자열"처럼 문자열을 던진 경우에는 e.stack이 정의되어 있지 않을 수 있으므로, 스택을 활용하려면 throw new Error("메시지") 형태로 예외를 던지는 것이 좋습니다.
5. 향후 예방 조치
직접 예외를 던질 때 스택 트레이스를 잃지 않기 위한 예방 조치입니다.
문자열 대신
Error객체를 throw하자.1 2 3 4// 나쁜 예 throw "Oh no! 'n' is too small!" // 좋은 예 throw new Error("Oh no! 'n' is too small!")Error객체는stack속성을 자동으로 갖기 때문에, 나중에 디버깅할 때 호출 경로를 복구할 수 있습니다.console.trace()를 디버깅 지점에 배치하자. 예외 직전 또는 의심스러운 분기점에서 한 줄만 추가하면 호출 체인을 즉시 확인할 수 있습니다.catch블록에서 스택을 로그로 남기자.1 2 3 4 5try { // ... } catch (e) { console.error(e && e.stack ? e.stack : e); }e.stack이 있는지 먼저 확인하고, 없으면 원본 값을 출력하도록 방어적으로 작성합니다.arguments.callee.caller에 의존하지 말자. 과거에는caller속성으로 스택을 재구성하는 방식이 쓰였지만, 엄격 모드(strict mode)를 비롯한 현대 환경에서는 제한적입니다.console.trace()또는Error.stack이 더 안전하고 표준적인 방법입니다.
이렇게 하면 직접 던진 예외에서도 스택 트레이스를 놓치지 않고 효과적으로 디버깅할 수 있습니다.
출처
- StackOverflow 질문: How can I get a JavaScript stack trace when I throw an exception?