JS LAB
JS 5.6 · 5부. 비동기 프로그래밍

에러 처리와 로딩 상태

실패했을 때와 기다리는 동안의 화면을 만듭니다.

예상 학습 시간 16분 실행 단추가 있는 예제는 고쳐서 실행해 볼 수 있습니다 난이도 중급
개념 설명

기다리는 동안과 실패했을 때

앞 단원의 예제는 출력 칸에 결과만 냈습니다. 실제 화면은 세 가지 상태를 모두 보여 주어야 합니다.

  • 기다리는 중 — 요청을 보냈고 아직 오지 않았습니다.
  • 받았음 — 데이터를 화면에 그립니다.
  • 실패 — 무엇이 잘못됐는지 알리고, 다시 해 볼 길을 줍니다.

이 셋을 빠뜨리면 사용자는 빈 화면을 보고 고장 났다고 여깁니다. 4부의 DOM 다루기와 5부의 비동기를 여기서 합칩니다.

최소 예제 · 편집 가능

세 상태를 화면에 내기

실행한 뒤 미리보기 탭을 열어 보세요. 상태에 따라 모양이 다릅니다.

script.js
const box = document.querySelector("#box");

async function load(id) {
	box.textContent = "불러오는 중...";
	box.className = "loading";

	try {
		const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${id}`);

		if (!response.ok) {
			throw new Error(`서버가 ${response.status} 를 돌려주었습니다`);
		}

		const todo = await response.json();

		box.textContent = todo.title;
		box.className = "done";
	} catch (error) {
		box.textContent = `불러오지 못했습니다: ${error.message}`;
		box.className = "error";
	}
}

await load(1);

console.log(box.className);
console.log(box.textContent);
출력
done delectus aut autem

load(99999) 로 고쳐 실행하면 실패 화면을 볼 수 있습니다.

상세 사용법

실패한 경우

script.js
const box = document.querySelector("#box");

try {
	const response = await fetch("https://jsonplaceholder.typicode.com/todos/99999");

	if (!response.ok) {
		throw new Error(`서버가 ${response.status} 를 돌려주었습니다`);
	}

	box.textContent = "받았습니다";
	box.className = "done";
} catch (error) {
	box.textContent = `불러오지 못했습니다: ${error.message}`;
	box.className = "error";
}

console.log(box.className);
console.log(box.textContent);
출력
error 불러오지 못했습니다: 서버가 404 를 돌려주었습니다

오류 메시지를 그대로 보여 주지 마세요. 사용자는 TypeError: Failed to fetch 를 읽을 수 없습니다. 무엇을 해야 하는지 알려 주는 문구로 바꿔 적습니다.

상세 사용법

끝나면 반드시 하는 일

성공이든 실패든 로딩 표시는 꺼야 합니다. 두 자리에 똑같이 적는 대신 finally 에 한 번만 적습니다(5.3 · 5.4).

script.js
const spinner = document.querySelector("#spinner");
const box = document.querySelector("#box");

try {
	const response = await fetch("https://jsonplaceholder.typicode.com/todos/2");
	const todo = await response.json();

	box.textContent = todo.title;
} catch (error) {
	box.textContent = "잠시 뒤에 다시 시도해 주세요";
} finally {
	spinner.className = "off";
}

console.log(spinner.className);
console.log(box.textContent);
출력
off quis ut nam facilis et officia qui
상세 사용법

여러 요청을 함께

script.js
const [first, second] = await Promise.all([
	fetch("https://jsonplaceholder.typicode.com/todos/1").then(r => r.json()),
	fetch("https://jsonplaceholder.typicode.com/todos/2").then(r => r.json())
]);

console.log(first.title);
console.log(second.title);
출력
delectus aut autem quis ut nam facilis et officia qui

Promise.all하나라도 실패하면 통째로 실패합니다. 일부가 실패해도 나머지를 쓰고 싶다면 Promise.allSettled 를 사용합니다.

흔한 실수

처음에 자주 걸리는 것

1. 로딩 표시를 끄지 않습니다

실패한 자리에서 return 하면 끄는 줄을 건너뜁니다. finally 에 두면 그럴 일이 없습니다.

2. 오류 메시지를 그대로 보여 줍니다

개발자에게 필요한 말이지 사용자에게 필요한 말이 아닙니다. 콘솔에는 그대로 남기고 화면에는 다시 해 볼 길을 적으세요.

3. 두 번 눌린 것을 막지 않습니다

요청이 끝나기 전에 또 누르면 요청이 겹칩니다. 시작할 때 단추를 잠그고 finally 에서 풉니다.

실습 문제

파트 5 미니 실습 — API 조회 화면

5부에서 배운 것을 모아 봅니다 난이도 상

번호를 입력하고 조회 단추를 누르면 그 할 일을 가져와 보여 주는 화면을 만드세요. 아래를 모두 지켜야 합니다.

  • 누르면 먼저 불러오는 중을 보여 줍니다.
  • 받으면 제목을 그립니다.
  • 없는 번호(예: 99999)면 사람이 읽을 수 있는 문구로 알립니다.
  • 끝나면 성공이든 실패든 단추를 다시 누를 수 있게 합니다.
단추 잠그기와 풀기를 try 앞과 finally 에 나눠 두면 빠뜨릴 일이 없습니다.
const input = document.querySelector("#id"); const btn = document.querySelector("#load"); const box = document.querySelector("#box"); btn.addEventListener("click", async () => { btn.disabled = true; box.textContent = "불러오는 중..."; box.className = "loading"; try { const id = input.value.trim(); const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${id}`); if (!response.ok) { throw new Error("없는 번호입니다"); } const todo = await response.json(); box.textContent = todo.title; box.className = "done"; } catch (error) { box.textContent = `불러오지 못했습니다. ${error.message}`; box.className = "error"; } finally { btn.disabled = false; } });
script.js
// 위 정답을 여기에 옮겨 적고 실행해 보세요.
// 실행한 뒤 미리보기 탭에서 번호를 바꿔 가며 눌러 볼 수 있습니다.
console.log("여기에 작성하세요");
출력
여기에 작성하세요

여기까지 하면 파트 5 가 끝납니다. 다음 파트에서는 코드를 클래스와 모듈로 나눕니다.

요약

이 단원의 정리

  • 기다리는 중 · 받았음 · 실패 세 상태를 모두 화면에 보여 줍니다.
  • 로딩 표시를 끄는 일은 finally 에 한 번만 적습니다.
  • 오류 메시지를 그대로 보여 주지 말고 다시 해 볼 길을 적습니다.
  • 요청 중에는 단추를 잠가 겹치지 않게 합니다.
  • Promise.all 은 하나라도 실패하면 통째로 실패합니다.