1. Promise를 이해해야 하는 이유
- 자바스크립트에서 비동기 처리는 피할 수 없는 개념이다. 서버통신, 타이머, 파일 읽기등 대부분의 중요한 작업은 나중에 결과가 도착한다. 하지만 기존의 비동기 처리 방식은 여러 문제를 안고 있다.
1.1 기존 비동기 처리 방식의 한계
setTimeout(() => {
console.log('done');
}, 1000);
// 이 방식은 단순한 경우에는 문제가 없지만,비동기 작업이 순차적으로 의존하게 되면 심각한 문제가 발생한다.
getPost(id, post => {
getUser(post.userId, user => {
getComments(user.id, comments => {
console.log(comments);
});
});
});
이 구조는 다음과 같은 문제를 만든다.
- 코드 가독성 저하
- 실행 흐름 파악 어려움
- 에러 처리 분산
- 유지보수 난이도 증가
이러한 구조적 한계를 해결하기 위해 Promise가 등장했다.
2. Promise란 무엇인가
2.1 Promise의 정의
- Promise는 한 문장으로 정의할 수 있다. 즉, Promise는 값 자체가 아니라 ‘미래의 값에 대한 약속’이다.
Promise는 지금은 알 수 없지만, 미래에 성공 또는 실패로 결정될 값을 나타내는 객체다.
2.2 Promise는 무엇을 보장하는가
- 비동기 작업은 반드시 한 번 끝난다
- 성공과 실패가 명확히 구분된다
- 결과는 한 번만 결정된다 (불변성)
🧐 Q. 헷갈릴만 한 포인트, Promise 객체는 역직렬화 대상이 아니다.
Promise {
[[PromiseState]]: "fulfilled",
[[PromiseResult]]: Response
}
- 내부 슬롯
- 직렬화 대상 아님
- JSON.parse로 처리 불가
👉 Promise는 “상태 머신”이지 데이터 포맷이 아님
const jsonString = '{"id":1,"name":"kim"}';
// 역직렬화
const obj = JSON.parse(jsonString);
console.log(obj.id); // 1
console.log(obj.name); // "kim"
3. async / await와 Promise
3.1 async / await의 의미
async / await는 Promise를 더 쉽게 쓰기 위한 문법이다.
async function loadData() {
const data = await fetchData();
console.log(data);
}
async함수는 항상 Promise를 반환한다await는 Promise가 처리될 때까지 해당 함수 실행을 일시 중단한다
비동기가 사라지는 것이 아니라, 보여지는 코드 형태만 동기처럼 바뀐다.
🧐 Q. 헷갈릴만 한 포인트, 기다렸다가 실행하기에 동기라고 봐야하지 않나요?
Synchronous vs Asynchronous Execution (Including async/await)
JayTak
4. fetch
- fetch는 HTTP 요청을 보내고, 서버의 HTTP 응답을 Response 객체로 감싸 Promise 형태로 반환하는 함수다.
4.1 Response 객체란 무엇인가
Response 객체는 HTTP 응답 전체를 표현하는 객체다. 즉, Response는 데이터 상자이지 데이터 그 자체가 아니다.
Response
├─ status (200, 404, 500 등)
├─ headers
└─ body (응답 본문, 아직 안 읽음)
4.2 왜 response.json()이 필요한가
- 서버는 데이터를 문자열(JSON) 형태로 보낸다.
- 문자열 데이터를 자바스크립트 객체로 사용하려면, 역직렬화(deserialization) 가 필요하다.
{"id":1,"name":"kim"}
const jsonString = '{"id":1,"name":"kim"}';
const obj = JSON.parse(jsonString);
5. fetch의 에러 처리 방식
fetch는 네트워크 자체가 실패했을 때만 reject된다. 그렇기 때문에 response.ok를 직접 검사해야 한다.
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
const data = await response.json();
🤓 fetch 설계 철학은 이렇다.
“통신 성공 여부는 내가 판단하고, 응답의 의미는 개발자가 판단해라.”
- fetch는 통신 성공만 판단하기 때문
- HTTP 상태 코드의 의미는 상황마다 다르기 때문
- 자동 throw는 과도한 추상화이기 때문
- 개발자가 의도에 맞게 분기할 수 있게 하기 위함