39. Promise

Promise를 통한 비동기 흐름의 구조화, async/await에 의한 가독성 개선, 그리고 fetch를 통한 HTTP 통신과 에러 판단의 명확한 책임 분리


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는 무엇을 보장하는가

  1. 비동기 작업은 반드시 한 번 끝난다
  2. 성공과 실패가 명확히 구분된다
  3. 결과는 한 번만 결정된다 (불변성)

🧐 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. 헷갈릴만 한 포인트, 기다렸다가 실행하기에 동기라고 봐야하지 않나요?

Photo of JayTak

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는 과도한 추상화이기 때문
  • 개발자가 의도에 맞게 분기할 수 있게 하기 위함



reference: 모던자바스크립트 Deep Dive 45장. Promise