[번역] javascript의 try-catch가 성능에 영향을 주나요?

I
Inkyu Oh

Front-End2026.01.02

也无风雨也雾晴 - 2025-12-17T14:42:06+00:00


면접 때 이런 질문을 받았습니다: try-catch가 성능에 영향을 주나요?
당시 저는 조금 당황해서 "약간의 영향은 있겠죠"라고 모호하게 대답했습니다. 면접관은 이어서 질문했습니다: 영향이 얼마나 큰가요? 어떤 상황에서 영향이 큰가요? 저는 더 이상 대답하지 못했습니다.
돌아와서 진지하게 연구해 보니, 이 질문에 대한 답이 생각보다 흥미롭다는 것을 발견했습니다.

결론부터 말씀드리면

현대 JavaScript 엔진에서 try-catch 자체는 성능에 거의 영향을 주지 않지만, 예외(Exception)를 던지는(throw) 것은 비용이 많이 드는 작업입니다.
말이 조금 어렵나요? 쉽게 풀어서 설명하면 다음과 같습니다:
  • 코드 겉에 try-catch를 한 겹 씌우는 것 → 기본적으로 영향 없음
  • 코드 내부에서 빈번하게 throw Error를 하는 것 → 성능이 매우 나빠짐
아래에서 데이터를 통해 확인해 보겠습니다.

실측 데이터

간단한 테스트를 작성해 보았습니다:
const iterations = 1000000;

// 테스트 1: try-catch 미사용
console.time('무 try-catch');
for (let i = 0; i < iterations; i++) {
Math.sqrt(i);
}
console.timeEnd('무 try-catch');

// 테스트 2: try-catch 사용, 하지만 예외를 던지지 않음
console.time('유 try-catch, 예외 없음');
for (let i = 0; i < iterations; i++) {
try {
Math.sqrt(i);
} catch (e) {
// 실행되지 않음
}
}
console.timeEnd('유 try-catch, 예외 없음');

// 테스트 3: try-catch 사용, 매번 예외를 던짐 (반복 횟수가 훨씬 적음에 유의)
console.time('유 try-catch, 매번 예외 발생');
for (let i = 0; i < 10000; i++) {
try {
throw new Error('test');
} catch (e) {
// 예외 처리
}
}
console.timeEnd('유 try-catch, 매번 예외 발생');
결과 (Node.js v20, M1 Mac):
시나리오
반복 횟수
소요 시간
무 try-catch
1,000,000
1.8ms
유 try-catch, 예외 없음
1,000,000
1.2ms
유 try-catch, 매번 예외 발생
10,000
13.9ms
흥미로운 점은, try-catch를 추가했을 때 오히려 더 빨라졌다는 것입니다.
네, 이는 V8 엔진의 최적화 효과일 수 있습니다. 하지만 핵심은 예외를 던지지 않는 한, try-catch의 오버헤드(Overhead)는 무시할 수 있는 수준이라는 점입니다.
반면 예외를 던지는 시나리오는 어떨까요? 반복 횟수는 100배 줄었지만, 소요 시간은 10배 더 늘어났습니다. 환산해 보면, 예외 발생은 정상 실행보다 약 1,000배 느립니다.

왜 예외 발생은 이렇게 느린가요?

JavaScript 엔진이 다음 세 가지 작업을 수행해야 하기 때문입니다:
  1. Error 객체 생성 - 이 객체는 오류 메시지를 포함합니다.
  1. 스택 추적(Stack Trace) 캡처 - 호출 스택(Call Stack)을 순회하며 각 계층의 함수 이름, 파일 이름, 행 번호를 기록합니다.
  1. 호출 스택 해제(Unwinding) - 예외가 발생한 지점부터 일치하는 catch 블록을 찾을 때까지 위로 거슬러 올라갑니다.
이 중 2번 단계가 가장 많은 시간을 소모합니다. 호출 스택이 깊을수록 스택 추적을 캡처하는 비용이 커집니다.

언제 try-catch를 사용해야 할까요?

한 가지 원칙만 기억하세요: 예외는 예외적인 상황을 처리하기 위한 것이지, 정상적인 흐름을 제어하기 위한 것이 아닙니다.

올바른 사용법: 진짜 예외 처리

// JSON 파싱은 실패할 수 있습니다.
try {
const data = JSON.parse(userInput);
processData(data);
} catch (e) {
showError('입력 형식이 올바르지 않습니다');
}

// 네트워크 요청은 실패할 수 있습니다.
try {
const response = await fetch('/api/data');
const data = await response.json();
} catch (e) {
showError('네트워크 연결 실패');
}
이러한 시나리오에서 예외는 "예상치 못한 상황"이며, 매번 발생하지 않습니다. 이런 경우 try-catch를 사용하는 것은 전혀 문제가 없습니다.

잘못된 사용법: 예외로 흐름 제어

// 잘못된 예시: 예외를 사용하여 사용자 존재 여부 판단
function findUser(id) {
try {
return database.query(`SELECT * FROM users WHERE id = ${id}`);
} catch (e) {
return null; // "찾을 수 없음"을 반환하기 위해 예외 사용
}
}
대부분의 쿼리에서 사용자를 찾지 못한다면, 매번 예외가 발생하여 성능이 매우 나빠질 것입니다.
올바른 방법은 먼저 확인하고 작업을 수행하는 것입니다:
// 올바른 방법
function findUser(id) {
const result = database.query(`SELECT * FROM users WHERE id = ${id}`);
return result || null;
}

반복문 안에서 try-catch를 어떻게 사용하나요?

이것은 또 다른 흔한 질문입니다. 두 가지 작성 방식을 보겠습니다:
// 방식 1: 반복문 내부의 try-catch
for (const item of items) {
try {
processItem(item);
} catch (e) {
console.error('처리 실패:', item);
}
}

// 방식 2: 반복문 외부의 try-catch
try {
for (const item of items) {
processItem(item);
}
} catch (e) {
console.error('처리 실패:', e);
}
성능상으로는 두 방식이 비슷합니다. 예외를 던지지 않는 한 try-catch 자체의 오버헤드가 거의 없기 때문입니다.
차이점은 오류 처리 전략에 있습니다:
  • 방식 1: 특정 항목이 실패해도 다른 항목을 계속 처리합니다.
  • 방식 2: 특정 항목이 실패하면 전체 반복문이 종료됩니다.
비즈니스 요구사항에 따라 선택하면 되며, 성능 때문에 고민할 필요는 없습니다.

초기 V8 엔진의 문제에 대하여

인터넷의 일부 오래된 글에서는 "try-catch가 V8 최적화를 방해한다"고 말하는데, 이는 초기 버전에서는 사실이었습니다. 하지만 V8 6.0+(Node.js 8.3+, Chrome 60+) 이후로는 이 문제가 해결되었습니다.
따라서 누군가 "try-catch가 함수 최적화를 막는다"고 말한다면, 그 글의 게시 날짜를 확인해 보세요. 2018년 이전의 글이라면 참고할 수 있겠지만, 너무 맹신하지는 마세요.

베스트 프랙티스 요약

  1. 안심하고 try-catch를 사용하세요 - 현대 엔진에서 성능 영향은 무시할 수 있습니다.
  1. 예외는 예외입니다 - 흐름 제어가 아닌, 실제 오류 상황을 처리하는 데 사용하세요.
  1. 먼저 확인하고 작업하세요 - if 문으로 판단할 수 있는 것은 예외 처리를 사용하지 마세요.
  1. catch 블록에서 작업을 수행하세요 - 비어 있는 catch 블록은 코드 스멜(Code Smell)입니다.
  1. 오류에 컨텍스트(Context)를 담으세요 - catch 블록에서 문제 해결에 도움이 되는 충분한 정보를 기록하세요.
// 베스트 프랙티스 예시
async function fetchUserData(userId) {
if (!userId) {
return null; // 먼저 확인, 예외 미사용
}

try {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
// HTTP 오류이지만, 반드시 예외는 아님
console.warn(`사용자 획득 실패: ${response.status}`);
return null;
}
return await response.json();
} catch (e) {
// 실제 예외: 네트워크 단절, JSON 파싱 실패 등
console.error('사용자 데이터 획득 예외 발생:', {
userId,
error: e.message,
stack: e.stack
});
throw e; // 필요에 따라 다시 던질지 결정
}
}

면접에서 어떻게 답할까요?

다음에 이 질문을 받으면 이렇게 대답해 보세요:
try-catch 자체는 현대 JavaScript 엔진에서 성능 오버헤드가 거의 없습니다. 실제로 성능에 영향을 주는 것은 예외의 발생과 포착인데, 이는 Error 객체 생성과 스택 추적 캡처가 필요하기 때문입니다. 따라서 try-catch는 정상적인 프로그램 흐름 제어가 아닌, 실제 예외 상황을 처리하는 데 사용할 것을 권장합니다. 예를 들어 사용자 입력 유효성 검사는 try-catch가 아닌 if 문으로 판단하는 것이 좋습니다.


이 글이 도움이 되었다면 제 GitHub를 팔로우해 주세요. 아래는 제가 진행 중인 오픈 소스 프로젝트들입니다:
Claude Code Skills (온디맨드 로딩, 의도 자동 인식, 토큰 낭비 없음, 소개 글):
  • 5-whys-skill - 5 Whys 근본 원인 분석, "근본 원인 찾기"라고 말하면 자동 활성화
  • first-principles-skill - 제1원리(First Principles) 사고, 아키텍처 설계 및 기술 선정에 적합
풀스택 프로젝트 (현대적인 기술 스택 학습에 적합):
  • prompt-vault - 프롬프트 관리자. 최신 기술 스택을 사용하여 최신 프런트엔드 풀스택 개발 패러다임을 배우기에 적합합니다: Next.js 15 + React 19 + tRPC 11 + Supabase 풀스택 예시. 클론 후 무료 Supabase만 설정하면 바로 실행 가능합니다.
  • chat_edit - 듀얼 모드 AI 애플리케이션 (채팅 + 리치 텍스트 편집), Vue 3.5 + TypeScript + Vite 5 + Quill 2.0 + IndexedDB
0
5

댓글

?

아직 댓글이 없습니다.

첫 번째 댓글을 작성해보세요!

Inkyu Oh님의 다른 글

더보기

유사한 내용의 글