본문으로 건너뛰기

단원 1~11 섞어 풀기

자바스크립트 목차비동기문제 12개

하루 지나 풀면 더 오래 남습니다. 1~11단원을 마친 다음 날 풀어 보세요. 1~11단원에서 골고루 12문제를 섞었고, 어느 레슨의 문제인지는 정답을 펼쳐야 보입니다. 출력을 쓰는 문제는 빈칸과 줄바꿈까지 종이에 끝까지 쓴 뒤 정답을 펼치세요. 줄 수를 알려 주지 않는 문제는 마지막 줄까지 다 씁니다. 막히면 정답 앞의 힌트를 먼저 펼칩니다. 점수는 매기지 않습니다.

한 번에 다 풀 필요는 없습니다. 문제 1~11은 폰으로도 풀 수 있고(약 30~40분), 끝의 만들어 보기 하나는 컴퓨터에서 Node로 직접 돌려 봅니다.

마지막 문제는 비동기 응답에서 올바른 JSON만 골라 합계를 구하는 함수를 빈 데서 만들어 봅니다. 컴퓨터가 있으면 저장해 돌려 맞춰 보고, 폰으로만 본다면 문제에 적힌 값으로 내 코드를 한 줄씩 따라가 맞춰 봅니다. 실행 결과는 모두 Node 26으로 돌린 것입니다.

문제 1

무엇이 찍힐까요? 세 줄을 글자 그대로 쓰세요.

const fee = 0;
console.log('50' + 10);
console.log(fee || 3000);
console.log(fee ?? 3000);
힌트

+는 한쪽이 문자열이면 이어 붙이고, ||는 0을 거짓 같은 값으로 보지만 ??는 null과 undefined만 비었다고 봅니다.

정답
실행 결과
5010
3000
0
  • 2번 줄: '50'이 문자열이므로 '5010'이 됩니다.
  • 3번 줄: 숫자 0은 거짓 같은 값이라 ||는 오른쪽 3000을 돌려줍니다.
  • 4번 줄: ??는 0을 그대로 살려 0을 돌려줍니다.

다시 볼 레슨: 글자를 수로: Number와 NaN · 참 같은 값과 ??

문제 2

배열에서 짝수만 골라 더한 값을 돌려주는 함수를 부릅니다. 출력되는 두 줄을 쓰세요.

function addEven(nums) {
  let sum = 0;
  for (const n of nums) {
    if (n % 2 === 0) {
      sum += n;
    }
  }
  return sum;
}
const a = addEven([3, 4, 7, 6]);
console.log(a);
console.log(addEven([1, 5]));
힌트

첫 호출에서는 4와 6이 더해지고, 둘째 호출에서는 조건이 참인 값이 없어 초기값 0이 그대로 반환됩니다.

정답
실행 결과
10
0
  • [3, 4, 7, 6]에서는 짝수 4와 6만 더해져 10을 돌려줍니다.
  • [1, 5]에서는 짝수가 없으므로 0을 돌려줍니다.

다시 볼 레슨: for...of로 배열 돌기 · 돌려받은 값 쓰기

문제 3

객체에 이름표를 하나 더 붙인 것과 ...로 펼쳐 만든 새 객체를 비교합니다. 출력되는 한 줄을 쓰세요.

const a = { q: 2 };
const b = a;
const c = { ...a };
b.q = 5;
c.q = 9;
console.log(a.q, b.q, c.q);
힌트

const b = a;는 같은 객체를 가리키고, const c = { ...a };는 속성을 복사한 새 객체를 만듭니다(규칙 3).

정답
실행 결과
5 5 9
  • b는 a와 같은 객체를 가리키므로 b.q = 5로 바꾸면 a.q도 5가 됩니다.
  • c는 펼쳐서 새로 만든 별도의 객체이므로 c.q = 9는 c만 바꿉니다.

다시 볼 레슨: 객체를 넘기고 복사하기 · 배열 바꾸기와 복사 · 단축 속성과 나머지 모으기

문제 4

숫자 배열을 정렬하고 20보다 큰 값만 고릅니다. 출력되는 두 줄을 쓰세요.

const nums = [30, 5, 100];
const s1 = nums.toSorted();
const s2 = nums.toSorted(
  (a, b) => a - b,
);
const big = s2.filter(
  (n) => n > 20,
);
console.log(s1);
console.log(big);
힌트

비교 함수 없는 toSorted()는 글자 순서로 정렬하고, (a, b) => a - b를 넘긴 toSorted는 숫자 크기 순서로 정렬합니다.

정답
실행 결과
[ 100, 30, 5 ]
[ 30, 100 ]
  • s1은 글자 첫 자 '1', '3', '5' 순서로 정렬되어 [ 100, 30, 5 ]가 됩니다.
  • s2는 숫자 크기순 [ 5, 30, 100 ]이고, 여기서 20보다 큰 값만 거르면 [ 30, 100 ]이 됩니다.

다시 볼 레슨: 정렬: sort와 toSorted · map·filter·find

문제 5

구조 분해 할당의 기본값을 씁니다. 출력되는 두 줄을 쓰세요.

function calc({ p, c = 500 }) {
  return p - c;
}
console.log(calc({ p: 4500 }));
console.log(
  calc({ p: 4500, c: 1000 }),
);
힌트

c 속성이 없을(undefined) 때만 기본값 500이 쓰입니다.

정답
실행 결과
4000
3500
  • 첫 호출은 c가 없어 기본값 500을 써서 4000이 찍힙니다.
  • 둘째 호출은 c: 1000이 넘어와 3500이 찍힙니다.

다시 볼 레슨: 구조 분해로 꺼내기 · 기본값 매개변수

문제 6

클로저 두 개를 만들고 바깥 변수 step을 바꾼 뒤 차례로 부릅니다. 출력되는 두 줄을 쓰세요.

let step = 1;
function makeUp(start) {
  let n = start;
  return () => {
    n += step;
    return n;
  };
}
const up = makeUp(10);
console.log(up());
step = 5;
console.log(up());
힌트

안쪽 함수는 up이 가진 n 칸과 전역의 step 이름표를 기억합니다(규칙 5).

정답
실행 결과
11
16
  • 첫 번째 up()은 10 + 1로 n을 11로 바꾸고 11을 찍습니다.
  • step = 5로 바뀐 뒤 두 번째 up()은 11 + 5로 n을 16으로 바꾸고 16을 찍습니다.

다시 볼 레슨: 클로저: 만들어진 칸을 기억하는 함수

문제 7

부모 클래스 Card를 상속받은 VipCard의 메서드를 부릅니다. 출력되는 두 줄을 쓰세요.

class Card {
  constructor(owner) {
    this.owner = owner;
    this.pt = 0;
  }
  add(n) {
    this.pt += n;
  }
}
class VipCard extends Card {
  add(n) {
    this.pt += n * 2;
  }
}

이어서 두 카드를 만들어 점수를 올립니다.

const c = new Card('민지');
const v = new VipCard('준호');
c.add(10);
v.add(10);
console.log(c.owner, c.pt);
console.log(v.owner, v.pt);
힌트

c.add(10)은 Card의 add를, v.add(10)은 VipCard에서 다시 정의한 add를 실행합니다.

정답
실행 결과
민지 10
준호 20
  • c는 일반 Card라 10이 더해지고, v는 VipCard라 10 * 2인 20이 더해집니다.

다시 볼 레슨: 클래스와 new · 상속: extends와 super

문제 8

JSON 문자열 배열을 돌며 읽고, 수량이 0 이하이면 오류를 던집니다. 출력되는 세 줄을 쓰세요.

const raw = [
  '{"q":2}', '{bad}',
  '{"q":0}',
];
for (const s of raw) {
  try {
    const o = JSON.parse(s);
    if (o.q <= 0) {
      throw new Error('0 이하');
    }
    console.log('통과:', o.q);
  } catch (e) {
    console.log('실패:', e.name);
  }
}
힌트

'{bad}'는 JSON.parse에서 SyntaxError가 나고, '{"q":0}'은 throw new Error(...)에서 Error가 납니다. catch는 e.name을 찍고 있습니다.

정답
실행 결과
통과: 2
실패: SyntaxError
실패: Error
  • 첫 바퀴는 통과: 2가 찍힙니다.
  • 둘째 바퀴는 JSON.parse가 던진 오류의 이름인 실패: SyntaxError가 찍힙니다.
  • 셋째 바퀴는 new Error('수량 오류')의 이름(e.name)인 실패: Error가 찍힙니다.

다시 볼 레슨: try와 catch · throw로 오류 알리기

문제 9

다음 코드를 실행했을 때 출력되는 줄을 마지막 줄까지 순서대로 쓰세요.

console.log(1);
setTimeout(() => {
  console.log(2);
}, 0);
Promise.resolve()
  .then(() => {
    console.log(3);
  })
  .then(() => {
    console.log(4);
  });
console.log(5);
힌트

지금 도는 코드(1, 5) → Promise 줄(3, 4) → 할 일 줄(2) 순서로 실행됩니다(규칙 8).

정답
실행 결과
1
5
3
4
2
  • 동기 코드인 1과 5가 먼저 찍힙니다.
  • 이어서 Promise 줄의 3과 4가 모두 찍힌 뒤, 할 일 줄의 2가 마지막에 찍힙니다.

다시 볼 레슨: 타이머와 Promise, 누가 먼저

문제 10

async 함수 안에서 응답 본문을 꺼내 출력합니다. 출력되는 세 줄을 순서대로 쓰세요.

async function show() {
  console.log('요청');
  const res = Response.json({
    item: '쿠키',
  });
  const d = await res.json();
  console.log('도착:', d.item);
}
show();
console.log('대기');
힌트

show() 안에서 await 앞의 '요청'은 바로 찍히고, await res.json()에서 show 함수가 멈춘 동안 바깥의 '대기'가 먼저 찍힙니다.

정답
실행 결과
요청
대기
도착: 쿠키
  • show()를 부르면 '요청'이 먼저 찍히고, await res.json()에서 잠시 멈춥니다.
  • 바깥의 '대기'가 찍힌 뒤 show가 다시 이어져 '도착: 쿠키'를 찍습니다.

다시 볼 레슨: async와 await · fetch로 데이터 받아 오기

문제 11

아이디가 0일 때 던진 '없음' 오류를 catch에서 잡아 '없음' 한 줄이 찍혀야 하는데, 아래 코드는 catch가 잡지 못하고 멈췄습니다. 몇 번 줄을 어떻게 고쳐야 할까요? 한 줄만 고쳐, 고친 줄을 쓰세요.

async function findUser(id) {
  if (id === 0) {
    throw new Error('없음');
  }
  return '민지';
}
try {
  findUser(0);
} catch (e) {
  console.log(e.message);
}
실행 결과
main.js:3    throw new Error('없음');          ^ Error: 없음    at findUser (main.js:3:11)    at main.js:8:3
힌트

8번 줄에서 findUser(0)이 돌려준 거부된 Promise를 try 안에서 오류로 풀려면 앞에 무엇을 붙여야 하는지 보세요.

정답

8번 줄을 await findUser(0);으로 고칩니다.

async function findUser(id) {
  if (id === 0) {
    throw new Error('없음');
  }
  return '민지';
}
try {
  await findUser(0);
} catch (e) {
  console.log(e.message);
}
실행 결과
없음
  • try 안에서 await를 붙여야 거부된 Promise가 오류로 던져져 catch (e)에 잡힙니다.

다시 볼 레슨: 기다리는 일의 오류

문제 12 (만들어 보기)

이번에는 1~11단원에서 배운 것을 한데 모아 빈 데서 함수를 만들어 봅니다. JSON 문자열을 담은 Promise들의 배열 tasks를 받아:

  1. await Promise.all(tasks)⁠로 모든 문자열 배열을 한꺼번에 꺼냅니다.
  2. 배열의 문자열을 하나씩 돌며 try / catch와 JSON.parse로 읽습니다.
  3. 올바른 JSON이면 객체의 p 속성을 합계에 더하고, 깨진 JSON이면 건너뛰어 최종 합계를 돌려주는 async 함수 sumOk(tasks)를 작성하세요.

이렇게 나와야 합니다.

7500

시험 배열을 [Promise.resolve('{bad}')] 하나로 바꾸면 0이 나와야 합니다.

0

아래 칸의 위쪽에 sumOk 함수를 쓰세요.

// async 함수 sumOk 쓰기

const tasks = [
  Promise.resolve('{"p":4500}'),
  Promise.resolve('{bad}'),
  Promise.resolve('{"p":3000}'),
];
console.log(await sumOk(tasks));
힌트

async function sumOk(tasks) 안에서 await Promise.all(tasks)⁠로 문자열 배열을 받은 뒤, for...of와 try / catch로 JSON.parse(s)를 실행해 o.p를 더하고 합계를 돌려줍니다.

정답

모범 답입니다.

async function sumOk(tasks) {
  const list =
    await Promise.all(tasks);
  let sum = 0;
  for (const s of list) {
    try {
      const o =
        JSON.parse(s);
      sum += o.p;
    } catch (e) {
      sum += 0;
    }
  }
  return sum;
}

이어서 시험 데이터를 넣어 확인합니다.

const t1 = '{"p":4500}';
const t2 = '{bad}';
const t3 = '{"p":3000}';
const tasks = [
  Promise.resolve(t1),
  Promise.resolve(t2),
  Promise.resolve(t3),
];
const res =
  await sumOk(tasks);
console.log(res);
실행 결과
7500

다시 볼 레슨: fetch로 데이터 받아 오기 · try와 catch · JSON: 객체와 글 사이


틀린 문제는 그 레슨으로 돌아가 모션 그림과 '자주 틀리는 곳'을 보고, 하루쯤 지난 뒤 그 문제만 다시 풀어 보세요. 그날 바로 다시 풀면 방금 본 정답이 기억나 맞힌 것인지 알기 어렵습니다.

끝까지 내려오면 저절로 표시됩니다. 이 기기에만 저장합니다.

자바스크립트 목차로