[JavaScript] What - fetch()

하쮸·6일 전

Error, Why, What, How

목록 보기
62/62

비동기 통신의 핵심: Promise와 fetch API


1. Promise

  • Promise(프로미스)

    • 비동기 처리의 상태 관리자.
    • 즉, 비동기 처리가 어떻게 진행되고 있는지를 나타내는 상태(Status)결과값을 함께 보관하는 객체.
      • 콜백 지옥을 해결하고 비동기 작업의 결과를 객체화하여 관리할 수 있게 해줌.
  • Promise는 아직 도착하지 않은 값을 대신해서 대표하는 객체.

    • 즉, 미래에 완료될 작업의 결과를 현재 시점에서 다룰 수 있게 해줌.

1-1. Promise의 3가지 상태.

  • Pending (대기)

    • 비동기 처리가 아직 수행되지 않은 초기 상태.
  • Fulfilled (이행)

    • 비동기 처리가 성공적으로 완료된 상태. (resolve 호출)
  • Rejected (거부)

    • 비동기 처리가 실패한 상태 (reject 호출)

1-1-1. Settled

  • Settled 상태란?
    • Promise가 Fulfilled 또는 Rejected 중 하나로 결과가 확정된 상태를 의미.
  • 한 번 settled 상태가 되면 변경되지 않음.

1-2. 원리.

  • Promise는 상태(state)와 결과값(value 또는 error)을 내부에 보관함.
    • 그래서 비동기 작업이 끝나는 시점을 기다렸다가(then, await) 필요한 시점에 결과를 안전하게 가공할 수 있음..

2. fetch()

  • fetch()는 기존의 XMLHttpRequest보다 간결하며 HTTP 요청을 보내고 그 결과를 Promise로 반환하는 Web API.
    • XMLHttpRequest 보다 현대적인 네트워크 요청 방식.

2-1. 특징

  • 요청 결과를 Promise<Response> 형태로 반환.
    • HTTP 응답 전체를 Response 객체로 감싸서 제공함.
fetch(url) → Promise<Response>
  • 주의할 점.

    • 첫 번째 .then() 또는 await fetch()에서 받는 Response 객체는 헤더 정보와 메타데이터만 포함함.
  • 실제 응답 본문(body)을 사용하려면 반드시 한 번 더 비동기 처리를 해야함.

response.json() // Promise 반환
  • 즉, fetch이중 비동기 구조를 가짐.
    • 1 네트워크 요청 (fetch)
    • 2 응답 본문 파싱 (response.json)

Ex) 택배로 예시

단계코드상황비유상태
1단계fetch(url)택배 기사님이 우리 집 벨을 누름연결 성공
2단계response.json()상자를 집 안으로 들고 들어와서 칼로 뜯음데이터 확보 (내용물 확인 완료)

2-2. fetch 실행 로직

  • 호출

    fetch() 호출 즉시 Pending 상태의 Promise 반환.

  • 수행

    브라우저 백그라운드에서 네트워크 요청 전송.

  • 정착(Settled)

    • 성공 시: Fulfilled → Response 객체 반환
    • 장애 발생 시: Rejected → Error 객체 반환

2-3. 예외 처리.

  • fetch()는 HTTP 상태 코드가 404나 500이어도 통신 자체는 성공으로 간주함.
    • HTTP 에러 ≠ Promise reject
      • 즉, fetch는 HTTP 에러를 실패(reject)로 보지 않음.
    • 네트워크 장애, 요청 중단만 reject
fetch(url)
  .then(response => {
      console.log(response.ok); 	// false 가능
});
상황fetch 결과
네트워크 정상 + 200fulfilled (ok === true)
네트워크 정상 + 404fulfilled (ok === false)
네트워크 정상 + 500fulfilled (ok === false)
네트워크 장애rejected
  • 반드시 response.ok (200~299)를 직접 확인하고 필요 시 수동으로 에러를 발생(throw)시켜야 함.
fetch(url)
  .then(response => {
      if (!response.ok) {
          throw new Error(`HTTP 에러: ${response.status}`);
      }
      return response.json();
  })
  .then(data => console.log(data))
  .catch(err => console.error(err));

3. .then() vs async / await

  • 두 방식은 동작 방식은 완전히 동일하고 차이점은 표현 방식과 가독성.
    • async / await는 비동기 처리를 동기화하는 것이 아니라 Promise 체이닝을 문법적으로 풀어 쓴 표현 방식.

.then() 체이닝

  • 비동기 작업의 결과를 메서드 체인으로 연결하는 전통적인 방식입니다.
function load() {
    fetch(url)							// 첫 번째 Promise 반환.
        .then(response => {
            if (!response.ok) throw new Error('HTTP 에러');
            return response.json(); 	// 두 번째 Promise 반환
        })
        .then(data => console.log(data))
        .catch(e => console.error(e)); // 모든 단계의 에러 포착
}

async / await

  • 비동기 코드를 동기 코드처럼 위에서 아래로 읽히게 만드는 방식.
async function load() { 						// async 키워드 필수
    try {
        const response = await fetch(url); 		// 첫 번째 비동기
        if (!response.ok) throw new Error('HTTP 에러');

        const data = await response.json(); 	// 두 번째 비동기
        console.log(data);
    } catch (e) {
        					// try 블록 내의 모든 에러 처리
        console.error(e);
    }
}
  • fetch(url)가 실행되는 순간 첫 번째 프로미스가 생성.
    • await는 이 첫 번째 프로미스가 서버로부터 응답 헤더를 다 받아올 때까지(Fulfilled 또는 Rejected 될때 까지) load 함수의 실행을 잠시 멈춤.
    • 응답이 와서 fulfilled 되면 내부의 resolve 값인 response 객체를 꺼내어 response 변수에 담음.
  • response.json()이 호출되는 순간 두 번째 프로미스가 생성.
    • await는 이번에도 이 두 번째 프로미스가 본문 데이터를 다 읽고 JSON으로 변환할 때까지 기다렸다가 파싱이 완료되면 내부의 resolve 값, 즉 최종 결과물인 실제 데이터(JS 객체)만 꺼내어 data 변수에 담음.

4. 참고.

profile
Every cloud has a silver lining.

0개의 댓글