JavaScript 에러 처리와 디버깅 완전 정복|try-catch, throw, 디버깅 실전 방법
🐛 JavaScript 에러 처리와 디버깅 완전 정복|try-catch, throw, 디버깅 실전 방법
안녕하세요, 퍼블리셔 노미입니다!
프론트엔드 개발을 하다 보면 가장 자주 마주치는 것이 있습니다.
바로 **"에러(Error)"**입니다.
에러를 무서워할 필요는 없습니다.
오히려 에러는 우리가 더 나은 코드를 짜도록 도와주는 힌트이기도 해요.
오늘은 자바스크립트 개발자가 반드시 알아야 할 에러 처리와 디버깅 방법을 아주 상세히 정리해드릴게요.
📌 에러(Error)란 무엇인가?
에러란 프로그램 실행 중 문제가 발생해서 정상적인 흐름이 중단되는 상황을 의미합니다.
자바스크립트에서는 에러가 발생하면 기본적으로 프로그램이 중단되거나 catch되지 않은 에러라면 콘솔에 빨간색 오류 메시지가 뜹니다.
대표적인 에러 상황
- 존재하지 않는 변수를 참조했을 때
- 네트워크 요청 실패
- 타입이 잘못됐을 때
- 함수 호출 방법이 잘못됐을 때
⚡ JavaScript 기본 에러 종류
에러 종류 | 설명 |
---|---|
ReferenceError | 정의되지 않은 변수를 참조할 때 발생 |
TypeError | 타입이 맞지 않는 작업을 할 때 발생 |
SyntaxError | 코드 문법이 잘못됐을 때 발생 |
RangeError | 허용 범위를 초과할 때 발생 |
URIError | URI 관련 함수 사용이 잘못됐을 때 발생 |
ReferenceError 예시
console.log(x); // ReferenceError: x is not defined
TypeError 예시
null.f(); // TypeError: Cannot read properties of null
🛠 try-catch 문법
try-catch는 에러가 발생할 수 있는 코드를 안전하게 감싸서 프로그램이 중단되지 않게 만드는 방법입니다.
기본 구조
try {
// 에러가 발생할 수 있는 코드
} catch (error) {
// 에러를 처리하는 코드
}
예시
try {
const result = riskyFunction();
console.log(result);
} catch (e) {
console.error('에러 발생:', e.message);
}
- try 블록: 문제가 발생할 가능성이 있는 코드
- catch 블록: 에러를 잡아서 처리
→ try-catch를 사용하면 에러가 발생해도 프로그램이 죽지 않고 계속 실행됩니다!
🔗 finally 블록
finally는 에러 발생 여부와 상관없이 항상 실행되는 블록입니다.
자원 해제, 연결 끊기, 로딩 종료 등 마무리 작업에 자주 사용합니다.
구조
try {
// 시도
} catch (e) {
// 에러 발생
} finally {
// 무조건 실행
}
예시
try {
console.log('시도합니다');
throw new Error('문제가 생겼어요!');
} catch (e) {
console.error('에러 잡음:', e.message);
} finally {
console.log('항상 실행됩니다.');
}
🚀 throw로 사용자 정의 에러 만들기
throw 키워드를 사용하면 개발자가 직접 에러를 발생시킬 수 있습니다.
필요할 때 명시적으로 에러를 던질 수 있어요.
throw 사용 예시
function divide(a, b) {
if (b === 0) {
throw new Error('0으로 나눌 수 없습니다!');
}
return a / b;
}
try {
console.log(divide(4, 0));
} catch (e) {
console.error(e.message);
}
- throw new Error('메시지')
- throw '문자열' (비추천)
→ 명확한 에러 처리 흐름을 만들 수 있습니다!
🧩 브라우저 개발자 도구로 디버깅하기
에러가 발생했을 때 무작정 console.log만 찍는 것은 좋은 디버깅 방법이 아닙니다.
브라우저 개발자 도구(DevTools)를 제대로 활용하면 빠르고 정확하게 문제를 찾을 수 있어요.
DevTools 열기
- 크롬(Chrome): F12 또는 Ctrl + Shift + I
- 사파리(Safari): Cmd + Option + I
DevTools 주요 탭
- Console: 에러 메시지, 로그 출력
- Sources: 코드 열람 및 디버깅 (브레이크포인트 설정)
- Network: 네트워크 요청 추적 (fetch, API 호출)
- Application: 로컬스토리지, 세션스토리지, 쿠키 확인
📜 콜스택(Call Stack) 분석하기
에러 메시지가 발생했을 때 가장 먼저 볼 것은 콜스택(Call Stack)입니다.
콜스택은 에러가 어디서 발생했는지, 어떤 함수 호출 흐름을 따라왔는지 보여줍니다.
콜스택 예시
function a() {
b();
}
function b() {
c();
}
function c() {
throw new Error('문제 발생!');
}
a();
이 경우, 에러 메시지에는 c -> b -> a
순으로 콜스택이 쌓여있을 겁니다.
- 콜스택은 가장 마지막 호출부터 거꾸로 쌓인다
- 어떤 함수가 에러를 일으켰는지 정확히 추적할 수 있다
→ 콜스택을 읽을 수 있으면 에러 디버깅 속도가 확 올라갑니다!
🛑 Breakpoint 설정하고 디버깅하기
Breakpoints를 설정하면 코드가 실행되다가 특정 지점에서 일시 정지하게 할 수 있습니다.
그 순간의 변수 상태, 호출 흐름을 모두 확인할 수 있어요.
브레이크포인트 설정 방법
- DevTools의 Sources 탭 열기
- 문제를 의심하는 코드 줄 번호 클릭 (파란색 점 생김)
- 페이지 새로고침 or 코드 재실행
- 브레이크포인트에 도달하면 코드 실행 멈춤
정지 상태에서 할 수 있는 것
- 변수 값 실시간 확인
- Call Stack 확인
- Step Over(다음 줄로 이동), Step Into(함수 안으로 들어가기)
- Scope 안에 있는 변수 조회
→ 디버깅 정확도와 속도가 엄청나게 올라갑니다!
🧠 실전에서 자주 하는 실수와 디버깅 예시
1. 오타(misspelling)
const userName = '노미';
console.log(username); // ReferenceError
→ 콘솔 에러를 보면 'username is not defined' 라고 알려줍니다.
2. null 또는 undefined 접근
const user = null;
console.log(user.name); // TypeError
→ 'Cannot read properties of null' 에러 발생!
예방 방법
if (user) {
console.log(user.name);
}
3. 비동기 처리 실수 (await 누락)
async function fetchUser() {
const res = fetch('/api/user'); // await 누락
const data = await res.json();
}
→ res가 Promise 객체라서 json()이 안 먹힐 수 있어요.
수정 방법
async function fetchUser() {
const res = await fetch('/api/user');
const data = await res.json();
}
🌟 자주 보는 에러 메시지 해석법
ReferenceError: x is not defined
→ 변수가 선언되지 않음TypeError: Cannot read properties of undefined
→ 잘못된 객체 접근SyntaxError: Unexpected token
→ 문법 오류 (괄호, 중괄호 빠짐 등)RangeError: Maximum call stack size exceeded
→ 무한 재귀 호출
→ 에러 메시지를 읽는 연습을 많이 할수록 디버깅이 빨라집니다!
🚀 async/await 디버깅 특화 방법
async/await는 비동기 코드를 깔끔하게 만들어주지만,
에러 디버깅에서는 주의할 점이 있습니다.
await 에러 자동 캐치 방법
async function fetchData() {
try {
const res = await fetch('/api/data');
const data = await res.json();
return data;
} catch (error) {
console.error('에러 발생:', error);
}
}
- await하는 코드는 항상 try-catch로 감싸야 한다.
- fetch 실패 시 자동으로 catch 블록으로 이동한다.
에러가 발생해도 앱이 죽지 않게 만들기
async function safeCall(fn) {
try {
return await fn();
} catch (error) {
console.error('문제 발생:', error);
return null;
}
}
// 사용 예시
const data = await safeCall(() => fetch('/api/data').then(res => res.json()));
→ 안전하게 에러를 관리하면서 비동기 로직을 처리할 수 있습니다!
🌐 네트워크 에러 추적 (Network 탭 활용)
API 호출 실패, 리소스 로딩 실패는 Network 탭을 보면 바로 알 수 있습니다.
Network 탭 보는 법
- DevTools → Network 탭 열기
- 페이지 새로고침 (필터링: XHR)
- 요청 목록 중 실패한 요청을 클릭
- Status Code, Request Headers, Response Body 확인
주요 상태 코드(Status Code)
- 200 OK → 정상
- 400 Bad Request → 잘못된 요청
- 401 Unauthorized → 인증 오류
- 403 Forbidden → 권한 없음
- 404 Not Found → URL 잘못됨
- 500 Internal Server Error → 서버 문제
→ 네트워크 문제는 코드 디버깅이 아니라 API/서버 문제일 수도 있습니다!
🛡️ 종합 디버깅 전략 (Debugging Checklist)
1. 에러 메시지를 끝까지 읽자
- 어디서 에러가 났는지, 무슨 에러인지 정확히 확인
2. 콜스택(Call Stack) 살펴보기
- 에러까지의 함수 호출 경로를 추적
3. 브레이크포인트 사용
- 문제 코드 근처에 일시정지 걸고 변수 값 확인
4. console.log는 전략적으로
- 필요한 곳만 찍고, 문제를 좁혀가기
5. 가정 검증하기
- 내가 믿는 값(user가 항상 있을 것 등)을 의심하기
6. 작게 쪼개서 재현해보기
- 문제를 단순화해서 작은 테스트 코드로 확인
7. 네트워크 요청 체크하기
- API 응답 상태, 본문을 꼼꼼히 확인
8. 문서와 에러 메시지 검색하기
- MDN, StackOverflow 활용 (거의 모든 에러는 이미 누군가 겪었다!)
🧠 좋은 디버깅 마인드셋
- 에러를 두려워하지 말자 (에러는 성장의 기회!)
- 가능한 모든 가정을 의심하자
- 급하게 수정하지 말고 원인을 먼저 찾자
- 한 번에 하나씩, 문제를 좁혀나가자
- 문제를 고친 후에도 "왜?"를 정확히 이해하자
→ 디버깅은 단순한 에러 수정이 아니라, 문제 해결 능력을 키우는 과정입니다!
📚 마무리하며
개발자는 "코드 작성"보다 "문제 해결"에 더 많은 시간을 씁니다.
이번 글을 통해 에러 종류 이해 → try-catch 사용 → throw 커스텀 에러 → 브라우저 디버깅 → 네트워크 문제 추적까지 모두 정복했다면, 앞으로 어떤 문제도 침착하게 해결할 수 있을 거예요.
다음 글에서는 에러 로깅 시스템 구축 (Sentry, 로그 서버 연동) 방법까지 이어서 다룰 예정입니다. 계속 함께 성장해요! 🚀
#JavaScript에러처리 #디버깅가이드 #콜스택분석 #trycatch #throwError #네트워크에러 #브라우저디버깅 #프론트엔드디버깅 #퍼블리셔노미 #문제해결능력