10단원 '오류와 모듈'의 세 레슨에서 일곱 문제를 내고, 1~9단원에서 세 문제를 섞었습니다. 마지막 두 문제는 코드를 직접 짜 봅니다.
한 번에 다 풀 필요는 없습니다. 문제 1~10은 폰으로도 풀 수 있고(약 30~40분), 끝의 만들어 보기 둘은 컴퓨터에서 Node로 직접 돌려 봅니다. 이 단원을 건너뛰어도 되는지 보려면 문제 2, 4, 7, 8, 10 다섯 문제만 먼저 풀어 보세요(약 15분). 건너뛸지 볼 때는 힌트를 펴지 않고 풉니다. 다섯 문제를 모두 맞히면 11단원으로 넘어가고, 하나라도 헷갈리면 정답의 '다시 볼 레슨'을 먼저 봅니다. 파이썬을 배웠다면 파이썬과 달라지는 곳이 이 다섯 문제에 들어 있습니다(정답의 '파이썬에서는' 줄). 끝의 만들어 보기 둘은 골라서 풉니다.
끝의 두 문제는 프로그램을 직접 만들어 봅니다. 글만 읽고 넘어가지 말고, 컴퓨터에서 작업 폴더의 main.js에 옮겨 적고 터미널에서 node main.js로 돌려 보세요. 실행 결과는 모두 Node 26으로 돌린 것입니다.
문제 1
올바른 JSON 문자열을 try 안에서 읽습니다. 출력되는 두 줄을 쓰세요.
const s = '{"qty":3}';
try {
const o = JSON.parse(s);
console.log(o.qty);
} catch (e) {
console.log('실패');
} finally {
console.log('마무리');
}
힌트
try 안에서 오류가 나지 않으면 catch 블록은 건너뛰고, finally 블록은 늘 실행됩니다.
정답
3
마무리- 3~4번 줄:
'{"qty":3}'은 올바른 JSON이므로o.qty인3이 찍힙니다. - 5~6번 줄: 오류가 나지 않았으므로
catch블록은 건너뜁니다. - 7~8번 줄:
finally블록은 오류가 없어도 늘 실행되어마무리가 찍힙니다.
다시 볼 레슨: try와 catch
문제 2
깨진 JSON 문자열을 try 안에서 읽습니다. 출력되는 두 줄을 쓰세요.
let step = 1;
try {
step = 2;
JSON.parse('{bad}');
step = 3;
} catch (e) {
console.log(e.name, step);
} finally {
console.log('끝');
}
힌트
4번 줄에서 오류가 나기 전에 실행한 3번 줄의 step = 2는 그대로 남고, 오류 뒤인 5번 줄의 step = 3만 건너뜁니다.
정답
SyntaxError 2
끝- 3번 줄: 오류가 나기 전이므로
step이2로 바뀝니다. - 4~5번 줄:
JSON.parse('{bad}')에서SyntaxError가 나는 순간 5번 줄(step = 3)은 건너뛰고catch (e)로 이동합니다. - 7~9번 줄:
e.name인SyntaxError와step값2가 찍히고, 이어서finally의끝이 찍힙니다.
흔한 오답은 첫 줄을 SyntaxError 1이나 SyntaxError 3으로 쓰는 것입니다. 앞의 것은 try에서 오류가 나면 앞 줄까지 취소된다고 본 것이고, 뒤의 것은 오류 뒤 줄도 마저 실행된다고 본 것입니다.
파이썬에서는 except ValueError as e:처럼 잡을 오류 종류를 골라 적지만 — 자바스크립트의 catch (e)는 모든 실행 중 오류를 한꺼번에 받고 e.name으로 종류를 확인합니다.
다시 볼 레슨: try와 catch
문제 3
배달비 fee를 바꾸기 전과 후에 클로저 함수를 부릅니다. 출력되는 두 줄을 쓰세요.
let fee = 500;
function makePay(base) {
return () => base + fee;
}
const pay = makePay(4000);
console.log(pay());
fee = 1000;
console.log(pay());
힌트
돌려준 화살표 함수는 fee의 옛 값 500을 복사해 두지 않고 전역 칸의 fee 이름표를 기억합니다(규칙 5).
정답
4500
5000- 6번 줄: 첫 번째
pay()를 부를 때fee는500이므로4000 + 500인4500이 찍힙니다. - 7~8번 줄:
fee가1000으로 바뀐 뒤 두 번째pay()를 부르면 바뀐fee를 읽어4000 + 1000인5000이 찍힙니다.
다시 볼 레슨: 클로저: 만들어진 칸을 기억하는 함수
문제 4
수량을 검사해 오류를 던지는 함수 pay를 반복문 안에서 부릅니다. 출력되는 세 줄을 쓰세요.
function pay(qty) {
if (qty <= 0) {
throw new Error('0 이하');
}
return qty * 4500;
}
for (const q of [2, 0, 1]) {
try {
console.log(pay(q));
} catch (e) {
console.log(e.message);
}
}
힌트
q가 0일 때 pay(0) 안에서 throw new Error('0 이하')가 실행되어 5번 줄을 건너뛰고 catch (e)로 넘어갑니다. 오류가 잡혔으므로 다음 바퀴인 q = 1도 계속 돕니다.
정답
9000
0 이하
4500q = 2일 때는2 * 4500인9000이 찍힙니다.q = 0일 때는 3번 줄에서 던진Error객체를 10번 줄의catch (e)가 받아e.message인0 이하를 찍습니다.q = 1일 때는1 * 4500인4500이 찍힙니다.
파이썬에서는 raise ValueError('0 이하')로 오류를 던지지만 — 자바스크립트는 throw new Error('0 이하')로 Error 객체를 만들어 던집니다.
다시 볼 레슨: throw로 오류 알리기
문제 5
Bag을 상속받은 IceBag 인스턴스를 만들고 메서드를 부릅니다. 출력되는 두 줄을 쓰세요.
class Bag {
constructor(item) {
this.item = item;
}
tag() { return this.item; }
}
class IceBag extends Bag {
tag() {
return `${this.item}(보냉)`;
}
}
const b = new IceBag('케이크');
console.log(b.item);
console.log(b.tag());
힌트
자식 클래스에 constructor가 없으면 부모 생성자가 그대로 불려 this.item을 저장하고, b.tag()는 자식 클래스 IceBag에 다시 정의된 메서드를 먼저 찾습니다.
정답
케이크
케이크(보냉)- 14~15번 줄: 부모
Bag의 생성자가this.item에'케이크'를 붙여케이크가 찍힙니다. - 16번 줄:
b.tag()는 자식 클래스IceBag의tag()를 찾아케이크(보냉)을 찍습니다.
다시 볼 레슨: 클래스와 new · 상속: extends와 super
문제 6
다음 코드는 3번 줄에서 오류를 던지고 멈춥니다. 오류 메시지 아래에 찍히는 첫 번째 at 줄과 두 번째 at 줄에 적히는 함수 이름을 차례대로 쓰세요.
function unit(menu) {
if (menu !== '라테') {
throw new Error('품절');
}
return 4500;
}
function bill(menu) {
return unit(menu) + 500;
}
console.log(bill('모카'));
힌트
Node의 at 줄은 실제 throw가 실행되어 멈춘 가장 안쪽 함수부터 맨 위에 적힙니다.
정답
첫 번째 at 줄은 unit, 두 번째 at 줄은 bill입니다.
main.js:3 throw new Error('품절'); ^ Error: 품절 at unit (main.js:3:11) at bill (main.js:8:10) at main.js:10:13- 10번 줄이
bill('모카')를 부르고, 8번 줄이unit('모카')를 부른 뒤,unit안 3번 줄에서 오류가 던져졌습니다. - 따라서 가장 최근에 멈춘
at unit이 첫 줄에, 그 함수를 부른at bill이 둘째 줄에 찍힙니다.
파이썬에서는 Traceback의 맨 아래에 가장 최근 함수(unit)가 찍히지만 — 자바스크립트의 at 줄은 반대로 가장 최근에 멈춘 함수(unit)가 맨 첫 줄에 찍힙니다.
다시 볼 레슨: throw로 오류 알리기
문제 7
같은 폴더에 cafe.js와 main.js가 있고 package.json에 "type": "module"이 들어 있습니다. node main.js를 실행했을 때 출력되는 세 줄을 순서대로 쓰세요.
// cafe.js
console.log('cafe 준비');
export const cup = 300;
아래는 같은 폴더의 main.js입니다.
// main.js (cafe.js 가져옴 …)
console.log('main 시작');
import {
cup,
} from './cafe.js';
console.log('컵값:', cup);
힌트
main.js 안에 import 문장이 있으면 main.js의 첫 줄이 돌기 전에 가져온 파일(cafe.js)이 먼저 끝까지 한 번 실행됩니다.
정답
cafe 준비
main 시작
컵값: 300
node main.js를 실행하면import로 연결된cafe.js가 먼저 실행되어 첫 줄에cafe 준비가 찍히고cup = 300을 내보냅니다.- 그다음
main.js의 본문이 위에서부터 돌아main 시작과컵값: 300이 차례로 찍힙니다.
흔한 오답은 main 시작을 첫 줄에 쓰는 것입니다. console.log('main 시작')이 import 줄보다 위에 적혀 있으니 먼저 실행된다고 본 것입니다.
다시 볼 레슨: 파일 나누기: import와 export
문제 8
같은 폴더에 cafe.js가 있습니다. 아래 main.js를 실행하면 300이 찍히나요, 중간에 오류로 멈추나요? 멈춘다면 까닭을 한 줄로 쓰세요.
import {
cup,
} from './cafe';
힌트
3번 줄의 파일 경로 './cafe' 끝에 무엇이 빠졌는지 보세요.
정답
300을 찍지 못하고 경로 끝에 .js 확장자가 빠져 ERR_MODULE_NOT_FOUND 오류로 멈춥니다.
Error [ERR_MODULE_NOT_FOUND]: Cannot find module 'cafe' imported from main.js { code: 'ERR_MODULE_NOT_FOUND', url: 'cafe'}- Node에서 내 파일을
import할 때는'./cafe.js'처럼.js확장자까지 빠짐없이 적어야 합니다.
파이썬에서는 from cafe import cup처럼 .py 확장자 없이 모듈 이름을 적지만 — Node에서는 from './cafe.js'처럼 상대 경로(./)와 .js 확장자까지 모두 적어야 합니다.
다시 볼 레슨: 파일 나누기: import와 export
문제 9
JSON 문자열을 try 안에서 배열로 바꾼 뒤 4500원 이상인 메뉴 이름만 골라냅니다. 출력되는 한 줄을 쓰세요.
const raw =
'[{"n":"라테","p":4500},' +
'{"n":"홍차","p":4000}]';
try {
const arr = JSON.parse(raw);
const res = arr
.filter((x) => x.p >= 4500)
.map((x) => x.n);
console.log(res);
} catch (e) {
console.log('실패');
}
힌트
JSON.parse(raw)는 객체 두 개가 든 배열을 돌려주고, filter와 map이 p >= 4500인 메뉴의 n만 모은 새 배열을 돌려줍니다.
정답
[ '라테' ]- 5번 줄:
JSON.parse(raw)가 메뉴 객체 두 개가 든 배열을 만듭니다. - 6~8번 줄:
filter가p >= 4500인 라테 객체 하나만 남기고,map이 그 객체의n값만 꺼내[ '라테' ]를 돌려줍니다.
다시 볼 레슨: JSON: 객체와 글 사이 · map·filter·find · try와 catch
문제 10
할인 금액 d가 가격 p보다 클 때 오류를 던져 catch에서 '초과' 한 줄이 찍혀야 하는데, 아래 코드는 undefined가 찍혔습니다. 몇 번 줄을 어떻게 고쳐야 할까요? 한 줄만 고쳐, 고친 줄을 쓰세요.
function cut(p, d) {
if (d > p) {
throw '초과';
}
return p - d;
}
try {
console.log(cut(400, 500));
} catch (e) {
console.log(e.message);
}
undefined힌트
10번 줄은 잡은 값 e에서 e.message 속성을 읽고 있습니다. 3번 줄에서 던진 값이 오류 객체인지, 일반 문자열인지 보세요.
정답
3번 줄을 throw new Error('초과');로 고칩니다.
function cut(p, d) {
if (d > p) {
throw new Error('초과');
}
return p - d;
}
try {
console.log(cut(400, 500));
} catch (e) {
console.log(e.message);
}초과3번 줄에서 throw '초과';처럼 문자열을 그대로 던지면 9번 줄의 e에 문자열 '초과'가 들어갑니다. 문자열에는 .message 속성이 없으므로 10번 줄의 e.message가 undefined를 찍습니다(규칙 1). new Error('초과')로 오류 객체를 만들어 던지면 e.message에 '초과'가 담깁니다.
파이썬에서도 문자열 대신 raise ValueError('초과')처럼 예외 객체를 만들어 던지듯 — 자바스크립트에서는 늘 throw new Error('...')를 씁니다.
다시 볼 레슨: throw로 오류 알리기 · try와 catch
문제 11 (만들어 보기)
여러 주문 JSON 문자열이 담긴 배열 raws를 돌며, 올바른 JSON이면 o.p를 sum에 더하고, 깨진 JSON이면 멈추지 않고 bad 개수를 1씩 올리는 코드를 완성합니다. 마지막 줄 console.log(sum, bad);가 이렇게 찍어야 합니다.
7500 1
raws의 세 값을 '{x}', '{y}' 두 개로 바꾸면 이렇게 나와야 합니다.
0 2
아래 뼈대에서 주석 아래를 try / catch로 채우세요.
const raws = [
'{"p":4500}',
'{bad}',
'{"p":3000}',
];
let sum = 0;
let bad = 0;
for (const s of raws) {
// 여기에 try / catch를 쓰세요
}
console.log(sum, bad);
힌트
try 안에서 const o = JSON.parse(s);와 sum += o.p;를 실행하고, catch (e) 안에서 bad += 1;을 실행합니다.
정답
모범 답입니다.
const raws = [
'{"p":4500}',
'{bad}',
'{"p":3000}',
];
let sum = 0;
let bad = 0;이어서 반복문 안에서 try / catch로 처리합니다.
for (const s of raws) {
try {
const o = JSON.parse(s);
sum += o.p;
} catch (e) {
bad += 1;
}
}
console.log(sum, bad);7500 1- 흔한 오답:
try를for반복문 바깥에 감싸면 두 번째 값'{bad}'에서 오류가 나는 순간 반복문 전체를 빠져나와 세 번째 값'{"p":3000}'을 처리하지 못하고4500 1이 찍힙니다.
다시 볼 레슨: try와 catch · JSON: 객체와 글 사이
문제 12 (만들어 보기)
이번에는 빈 데서 함수를 직접 씁니다. 남은 재고 left와 주문 수량 qty를 받는 함수 take(left, qty)를 만드세요.
qty <= 0이면new Error('0 이하')를 던집니다.qty > left이면new Error('부족')을 던집니다.- 그 밖에는
left - qty를 돌려줍니다.
이렇게 나와야 합니다.
1
0 이하
부족
마지막 반복문의 배열 [2, 0, 5]를 [1]로 바꾸면 2 한 줄이 나와야 합니다.
2
아래 칸의 위쪽에 take 함수를 쓰세요.
// 여기에 take 함수를 쓰세요
for (const q of [2, 0, 5]) {
try {
console.log(take(3, q));
} catch (e) {
console.log(e.message);
}
}
힌트
if (qty <= 0)에서 throw new Error('0 이하');를, if (qty > left)에서 throw new Error('부족');를 실행하고, 끝에 return left - qty;를 씁니다.
정답
모범 답입니다.
function take(left, qty) {
if (qty <= 0) {
throw new Error('0 이하');
}
if (qty > left) {
throw new Error('부족');
}
return left - qty;
}이어서 반복문으로 시험 값을 넣어 확인합니다.
for (const q of [2, 0, 5]) {
try {
console.log(take(3, q));
} catch (e) {
console.log(e.message);
}
}1
0 이하
부족- 흔한 오답:
new Error(...)없이 문자열만 던지면catch에서e.message가undefined로 찍힙니다.
다시 볼 레슨: throw로 오류 알리기 · try와 catch
틀린 문제는 그 레슨으로 돌아가 모션 그림과 '자주 틀리는 곳'을 보고, 하루쯤 지난 뒤 그 문제만 다시 풀어 보세요. 그날 바로 다시 풀면 방금 본 정답이 기억나 맞힌 것인지 알기 어렵습니다.