어제까지는 코드가 정해진 순서대로 흘러갔지만, 밖에서 들어온 JSON 문자열이 깨져 있거나 파일이 없으면 실행 중에 오류가 나며 프로그램이 그 자리에서 멈춥니다. 이 단원에서는 실행 중 오류를 받아 멈추지 않게 이어 가는 try · catch · finally, 잘못된 값이 들어왔을 때 직접 오류를 알리는 throw new Error(...), 그리고 길어진 코드를 여러 파일로 나누는 export와 import를 배웁니다. 이 단원을 이미 안다면 10단원 연습 문제의 다섯 문제를 먼저 풀어 보세요.
파이썬을 배웠다면 — 이 단원에서 다른 곳
- 오류 잡기: 파이썬은
except ValueError as err:처럼 잡을 오류 종류를 골라 적지만, 자바스크립트의catch (err)는 괄호에 오류 종류를 적지 않고 모든 실행 중 오류를 한꺼번에 받습니다. 어떤 오류인지는err.name과err.message속성으로 확인합니다. - 오류 던지기: 파이썬의
raise ValueError('품절')자리에 자바스크립트는throw new Error('품절')을 씁니다. - 오류 위치(
at줄) 읽는 방향: 파이썬Traceback은 가장 최근에 멈춘 함수가 맨 아래에 찍히지만, Node의at줄은 오류가 난 가장 안쪽 함수가 맨 위에 찍히고 아래로 내려갈수록 처음 부른 자리로 거슬러 올라갑니다. - 모듈 가져오기: 파이썬은
from cafe import latte처럼 확장자 없이 파일 이름을 쓰지만, Node에서는from './cafe.js'처럼 상대 경로(./)와 확장자(.js)까지 다 적어야 하며 폴더의package.json에"type": "module"이 들어 있어야 합니다.
먼저 JSON: 객체와 글 사이에서 본 JSON.parse에 따옴표가 빠진 깨진 문자열 '{bad}'가 들어오면 어떻게 되는지 봅니다.
const raw = '{bad}';
const data = JSON.parse(raw);
console.log('다음 주문 처리');
<anonymous_script>:1{bad} ^ SyntaxError: Expected property name or '}' in JSON at position 1 (line 1 column 2) at JSON.parse (<anonymous>) at main.js:2:192번 줄의 JSON.parse(raw)에서 SyntaxError가 나면서 프로그램이 멈추고, 3번 줄의 '다음 주문 처리'는 찍히지 못합니다. 손님 한 명의 주문 문자열이 깨졌다고 카페 주문 프로그램 전체가 꺼지면 곤란합니다.
try · catch · finally의 흐름
오류가 날 수 있는 코드를 try { ... } 블록에 감싸 두면, 그 안에서 실행 중 오류가 났을 때 프로그램을 멈추지 않고 catch (err) { ... } 블록으로 건너뜁니다. 끝에 finally { ... } 블록을 덧붙이면 오류가 나든 안 나든 마지막에 늘 실행됩니다.
const raw = '{bad}';
try {
const data = JSON.parse(raw);
console.log(data.menu);
} catch (err) {
console.log('실패:', err.name);
} finally {
console.log('마무리');
}
실패: SyntaxError
마무리3번 줄에서 오류가 나는 순간 4번 줄을 어떻게 건너뛰고 catch와 finally로 이어지는지 한 걸음씩 따라가 보세요.
try블록: 먼저 실행해 보는 구간입니다. 3번 줄JSON.parse(raw)에서 오류가 나는 순간, 대입은 끝나지 않고try안의 남은 4번 줄은 건너뜁니다.catch (err)블록:try안에서 오류가 났을 때만 실행됩니다. 괄호 안의err이름표에는 방금 난 오류 객체가 붙습니다.finally블록: 오류가 나서catch를 거쳤든, 오류가 없이try를 끝까지 돌았든 마무리할 때 늘 실행됩니다.
이번에는 올바른 JSON 문자열 '{"menu":"라테"}'가 들어왔을 때의 흐름을 비교해 봅니다.
const raw = '{"menu":"라테"}';
try {
const data = JSON.parse(raw);
console.log(data.menu);
} catch (err) {
console.log('실패:', err.name);
} finally {
console.log('마무리');
}
라테
마무리try 안의 3~4번 줄이 오류 없이 끝까지 돌아 라테를 찍고, catch 블록은 통째로 건너뛴 뒤 finally 블록의 마무리가 찍힙니다.
오류 객체의 name과 message
catch (err)가 받는 오류 객체에는 오류 종류를 담은 err.name과 설명 글을 담은 err.message 속성이 들어 있습니다.
try {
const x = null;
console.log(x.price);
} catch (err) {
console.log('종류:', err.name);
const m = err.message;
const ok = m.includes('null');
console.log('설명 포함:', ok);
}
종류: TypeError
설명 포함: truenull에서 .price 속성을 읽으려 해 TypeError가 났고, catch (err)가 그 오류 객체를 받아 err.name으로 'TypeError'를 확인했습니다.
자주 틀리는 곳
1. 빈 catch로 모든 오류를 삼켜 원인을 숨김
catch 안을 비워 두거나 '오류 남' 한 줄만 찍으면, 데이터가 깨진 것이 아니라 내가 변수 이름을 잘못 적은 ReferenceError까지 조용히 삼켜 버려 왜 안 돌아가는지 찾기 어려워집니다.
const raw = '{"menu":"라테"}';
try {
const data = JSON.parse(raw);
console.log(dtaa.menu);
} catch (err) {
console.log('읽기 실패');
}
읽기 실패JSON 문자열은 멀쩡했지만 4번 줄에서 data를 dtaa로 잘못 적어 ReferenceError가 났는데도 '읽기 실패'만 찍혀 JSON이 깨진 줄로 착각하게 됩니다. try 안에는 꼭 오류가 날 수 있는 줄만 짧게 넣고, catch에서는 err.name이나 err.message를 함께 확인하세요.
2. try 안에서 오류가 나기 앞 줄은 이미 실행됨
try 블록은 오류가 난 줄의 뒤쪽만 건너뛸 뿐, 오류가 나기 전에 이미 실행한 앞 줄을 취소하거나 되돌리지 않습니다.
let count = 0;
try {
count += 1;
JSON.parse('{bad}');
count += 10;
} catch (err) {
console.log('count:', count);
}
count: 14번 줄에서 오류가 나기 전에 실행된 3번 줄의 count += 1은 그대로 남고, 오류 뒤인 5번 줄의 count += 10만 건너뛰어 count: 1이 찍힙니다.
3. 코드 자체의 문법 오류는 try로 잡을 수 없음
JSON.parse('{bad}')가 내는 SyntaxError는 프로그램이 도는 도중에 문자열을 분석하다가 난 실행 중 오류라 catch로 잡을 수 있습니다. 반면 자바스크립트 코드 자체의 괄호나 따옴표를 빼먹은 문법 오류는 실행을 시작하기 전에 파일을 읽는 단계에서 막히므로 try로 감싸도 잡히지 않습니다.
요약
try와catch (err):try안에서 실행 중 오류가 나면 남은 줄을 건너뛰고catch로 흐름을 옮긴다. 오류가 없으면catch를 건너뛴다.finally { ... }: 오류가 나든 안 나든 마지막에 늘 실행된다.err.name과err.message: 잡은 오류 객체에서 오류 종류('SyntaxError','TypeError'등)와 설명 문자열을 읽는다.try안에서 오류가 나기 앞 줄은 그대로 실행되며, 빈catch로 오류를 삼키면 오타(ReferenceError)까지 숨겨지므로err.name을 확인한다.
연습 문제
문제 1
다음 코드를 실행했을 때 출력되는 세 줄을 순서대로 쓰세요.
const list = [
'{"qty":2}',
'{깨진글자}',
];
for (const s of list) {
try {
const o = JSON.parse(s);
console.log('수량:', o.qty);
} catch (e) {
console.log('실패:', e.name);
}
}
console.log('완료');
힌트
첫 번째 문자열 '{"qty":2}'는 올바른 JSON이라 try가 끝까지 돌고, 두 번째 문자열 '{깨진글자}'는 JSON.parse에서 SyntaxError가 나 catch로 넘어갑니다.
정답
수량: 2
실패: SyntaxError
완료- 첫 바퀴에서는
JSON.parse('{"qty":2}')가 성공해수량: 2를 찍고catch를 건너뜁니다. - 둘째 바퀴에서는
JSON.parse('{깨진글자}')에서 오류가 나 8번 줄을 건너뛰고catch에서실패: SyntaxError를 찍습니다. - 오류가 잡혔으므로 반복이 멈추지 않고 끝나 마지막 줄의
완료가 찍힙니다.
문제 2
다음 코드는 JSON.parse(s)가 성공해 4500을 찍으려 했지만, 4번 줄에서 변수 이름을 obj 대신 ob로 잘못 적어 오류가 났습니다. 그런데 catch가 아무 정보도 찍지 않아 왜 실패했는지 알 수 없습니다. 6번 줄 한 줄만 고쳐 잡힌 오류의 종류 이름(err.name)이 찍히게 만드세요.
const s = '{"price":4500}';
try {
const obj = JSON.parse(s);
console.log(ob.price);
} catch (err) {
console.log('오류');
}
힌트
catch (err)가 받은 오류 객체의 종류 이름은 err.name에 들어 있습니다.
정답
- 고친 줄:
console.log(err.name);
const s = '{"price":4500}';
try {
const obj = JSON.parse(s);
console.log(ob.price);
} catch (err) {
console.log(err.name);
}ReferenceErrorerr.name을 찍어 보면SyntaxError가 아니라ReferenceError가 찍히므로, JSON 문자열이 아니라 변수 이름ob가 잘못되었다는 것을 바로 알 수 있습니다.
문제 3
문자열 s를 JSON.parse(s)로 읽어 o.price를 찍되, 오류가 나면 '기본가: 4000'을 찍고, 오류가 나든 안 나든 마지막에 finally로 '조회 끝'을 찍는 코드를 완성하세요.
기본가: 4000
조회 끝
const s = '깨진JSON';
// 여기에 try / catch를 쓰세요
힌트
try 안에서 const o = JSON.parse(s);와 console.log(o.price);를 실행하고, catch (err)에서 '기본가: 4000'을, finally에서 '조회 끝'을 찍습니다.
정답
const s = '깨진JSON';
try {
const o = JSON.parse(s);
console.log(o.price);
} catch (err) {
console.log('기본가: 4000');
} finally {
console.log('조회 끝');
}기본가: 4000
조회 끝