ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • fetch API
    Node.js 2025. 8. 27. 22:45

    🔍 비동기 데이터 통신

    비동기 요청을 통해서 서버에서 데이터를 가져오는 것은 동적이고 상호작용적인 웹 애플리케이션에서 필수가 되었습니다.

    이렇게 비동기 데이터 통신을 하는 방식으로는 XMLHTTPRequest 객체를 활용하는 것으로 수년 동안 사용한 방법입니다.

    이후 Fetch API가 도입되어 비동기 요청 처리를 위한 더 간단한 접근 방식을 사용하게 되었습니다.

     

    🌐 크롬 네트워크 탭

    크롬의 네트워크 탭을 확인하면xhr, fetch type을 볼 수 있습니다.

    🧩 XMLHttpRequest

    XMLHttpRequest(XHR)은 AJAX 요청을 생성하는 JavaScript API입니다.

    • XHR의 메서드로 브라우저와 서버간의 네트워크 요청을 전송할 수 있습니다.

     

    // XMLHttpRequest 객체 생성
    var xhr = new XMLHttpRequest();
    
    xhr.open("GET", "<https://test.com>", true);
    
    xhr.onload = function () {
        if (xhr.status === 200) {
            var data = JSON.parse(xhr.responseText);
            console.log(data);
        } else {
            console.error("Request failed: " + xhr.status);
        }
    };
    
    xhr.send();

     

    🏄‍♂️ 사용 방법

    1. XMLHttpRequest 객체 생성
    2. open() 메서드를 통해 요청에 필요한 정보를 설정
    3. send() 메서드로 서버에 요청
    4. 응답에 대한 콜백 함수 생성

     

    🧩 AJAX

    비동기 자바스크립트와 XML

    • Asynchronous Javascript and XML

     

    웹 페이지를 전체 새로고침 없이 서버와 데이터를 비동기적으로 주고받아 부분적으로만 업데이트하는 웹 개발 기법입니다.

    • 빠르게 동작하는 동적인 웹 페이지를 만들 수 있습니다.

     

    다양한 형태의 데이터를 주고 받을 수 있습니다.

    • JSON
    • XML
    • HTML
    • 텍스트 파일 등

     

    👋 장점

    • 웹 페이지 전체를 다시 로딩하지 않고도 웹 페이지의 일부분만을 갱신해 속도가 향상되고 코딩의 양이 줄어듭니다.
    • 서버의 처리가 완료될 때까지 기다리지 않고 처리가 가능하다.
    • 다양한 UI를 가능하게 합니다.

     

    ⚠️ 단점

    • 히스토리 관리가 되지 않습니다.
    • AJAX를 쓸 수 없는 브라우저에 대한 문제 이슈 존재
    • HTTP 클라이언트의 기능이 한정
    • XMLHttpRequest를 통해 통신하는 경우 사용자에게 아무런 진행 정보가 주어지지 않습니다.(요청이 완료되지 않았는데 사용자가 페이지를 떠나거나 오작동 가능.)

     

    📌 fetch

    Fetch API는 네트워크 통신을 포함한 리소스 취득을 위한 인터페이스를 제공

    • XMLHttpRequest보다 강력하고 유연하다.

     

    fetch는 거의 모든 웹 브라우저가 지원하고 있습니다.

    fetch를 이용하면 비동기 HTTP 요청을 만드는데 충분합니다.

     

    🆚 fetch 와 XMLHttpRequest 차이 정리

     

    1️⃣ 응답 처리

     

    XHR은 콜백 을 받아 responseText, responseXMl 등을 통해 응답을 처리

    fetch는 promise 를 기반으로 동작하여 response.json(), response.text()를 사용합니다.

    • 프로미스를 반환하기 때문에
    • .then().catch()를 이용한 체이닝이 가능합니다.
    • async/await 문법을 사용해 코드를 더 간결하게 작성할 수 있습니다.

    2️⃣ cors

     

    XHR은 CORS를 위한 추가 설정이 필요하지만 fetch는 CORS를 지원하여 간단하게 설정이 가능합니다

    fetch('url', {
      mode: 'cors',
      credentials: 'same-origin',
      headers: {
        'Content-Type': 'application/json',    
      },
    });

     

    3️⃣ 파일 업로드 및 다운로드

     

    XHR 에서는 progress 이벤트 를 통해 업로드 진행률 데이터를 받을 수 있습니다.

     

    XHR

    • upload.onprogress 이벤트를 사용해 업로드 진행률 추적 가능
    • 다운로드도 onprogress 이벤트를 통해 스트리밍 중간 상태 확인 가능

    fetch

    • 업로드 진행률을 직접 추적하는 기능이 없음 ❌
    • fetch는 스트림(Stream) 기반으로 설계되었기 때문에 전체 진행률을 한 번에 알기 어렵습니다.
    • ReadableStream API를 사용하여 fetch로도 다운로드 진행률을 추적할 수는 있습니다.

     

    4️⃣ 요청 취소

     

    XHR

    • xhr.abort() 메서드로 요청 취소 가능

    fetch

    • 기본적으로 취소 기능이 없었음
    • 현재는 AbortController API로 요청 취소 가능 ✅
    const controller = new AbortController();
    const signal = controller.signal;
    
    fetch("<https://example.com/test>", { signal })
      .then(res => res.json())
      .then(data => console.log(data))
      .catch(err => console.error("❌", err));
    
    // 1초 후 요청 취소
    setTimeout(() => controller.abort(), 1000);
    

     

    📖 사용 방법

    📌 fetch

    fetch 함수를 호출하는 즉시 브라우저는 HTTP 요청을 생성합니다.

     

    fetch(url)
    
    fetch(url, options)
      .then(res => res.text())
      .then(text => ...));
    • URL만 전달하여 함수를 실행할 수 있습니다 (GET 메소드로 요청)
      • fetch는 기본으로 GET METHOD를 사용합니다.

     

    📖 두 번째 인자 - options

    • 헤더, 본문 등 구체적인 요청 정보를 지정해줍니다.
    options 설명
    method 요청 메소드
    headers 요청 헤더
    body 요청 본문
    mode cors 관련

     

     

    🏄‍♂️ 동작 과정

    1. 요청한 url로 설정한 HTTP Method로 요청을 보냅니다.
    2. fetch를 통해 ajax를 호출 시 해당 주소에 요청을 보낸 후 응답 객체를 받습니다.
    3. 첫 번째 then 에서 그 응답을 받아 res.text() 메서드로 파싱한 text 값을 리턴합니다.
    4. 다음 then 에서 리턴받은 text를 받고 원하는 처리를 합니다.

     

    ↗️ Json 업로드

    fetch('/upload', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        }
        body: JSON.stringify({name: 'beomsic'})
    })
    • body 필드에 JSON 문자열을 지정해서 요청 본문을 설정합니다.

     

    ↩️ Response

    fetch 함수의 HTTP 요청은 서버로 전달되어 서버는 HTTP 응답을 다시 되돌려줍니다.

    ⚠️ 이 응답 데이터는 fetch 함수를 실행하면 바로 얻는 것이 아닙니다.

    • 브라우저는 응답 시 실행할 콜백 함수만 등록을 한 후 다음 코드를 실행합니다.
    • fetch 함수는 응답을 바로 반환하는 것이 아니라 Promise 객체를 반환합니다.

    🤖 fetch 함수는 응답 대신 프라미스 객체를 반환하고 프라미스가 이행되면 Response 객체를 얻습니다. 이 Response 객체에는 HTTP 응답 정보가 담겨있습니다.

     

    📌 주요 필드

    필드 설명
    status HTTP 상태 코드 (ex: 200)
    ok HTTP 상태코드가 200~299 사이인지 여부
    body 응답 내용
    headers 응답 헤더

     

     

    📌 응답 본문

    response.text() 응답을 읽고 텍스트를 반환
    response.json() 응답을 JSON 형태로 파싱
    response.formData() 응답을 FormData 객체 형태로 반환
    response.blob() 응답을 Blob(타입이 있는 이진 데이터, ex: 이미지)형태로 반환
    response.arrayBuffer() 응답을 ArrayBuffer(바이너리 데이터를 로우 레벨 형식으로 표현)형태로 반환

     

    ⚠️ 주의

    응답 자료 형태 반환하는 메서드는 한번만 사용가능합니다.

    • response.text() 를 사용해 응답을 얻고 나면 본문의 콘텐츠는 모두 처리가 되어 response.json()을 입력해도 동작하지 않게 됩니다.

     

    🤔 fetch 함수는 왜 프라미스를 두 번 사용할까?

    async () => {
        try{
            const response = await fetch('/test')
            const data = response.json();
        }catch(e) {
            console.error(`⚠️ ERROR: ${e}`);
        }
    }

     

     

    1️⃣ fetch 함수가 반환하는 프로미스

     

    2️⃣ response 객체의 메소드가 반환하는 프라미스

     

    브라우저는 헤더를 모두 받으면 response 객체를 제공해줍니다.

    • fetch 가 반환한 첫 번째 프로미스가 이행될 때

     

    브라우저가 body 데이터를 모두 받으면 데이터를 만듭니다.

    • .json(), .text(), .blob() 같은 본문 조회 메소드가 반환한 두 번째 프로미스가 이행될 때

     

    시간을 지연하면서 본문을 청크 단위로 응답하는 서버

    const http = require('http');
    
    const server = http.createServer(async (req, res) => {
      if (req.url === '/test') {
        // 응답 헤더 전송
        res.writeHead(200, { 'Content-Type': 'text/plain' });
    
        // 청크 단위로 1초 간격 응답
        for (const i of Array.from({ length: 5 }).keys()) {
          res.write(`chunk ${i}\\n`);
          await new Promise((resolve) => setTimeout(resolve, 1000));
        }
    
        // 응답 종료
        res.end();
      } else {
        res.writeHead(404);
        res.end('Not Found');
      }
    });
    
    server.listen(3000, () => {
      console.log('🚀 Server running at <http://localhost:3000>');
    });

     

    🖥️ curl 요청을 통해서 확인해보기

    • 매초 마다 chunk x 가 출력됩니다.

     

    🌐 브라우저에서 fetch 함수는 어떻게 동작??

    const response = await fetch("<http://localhost:3000/test>");
    
    // 브라우저가 모든 헤더를 받았을 때
    console.log("response:", response);
    
    // 브라우저가 body 전체 수신 및 문자열로 변환할 때
    const text = await response.text();
    console.log("text:", text);

    • 서버가 헤더를 실어서 먼저 응답하기 때문에 Response 객체를 먼저 만들고
    • 일정 시간이 지연된 뒤 모든 바디가 수신되면 text 로그가 출력됩니다.

     

    🚀 그래서 두 번의 프로미스가 필요한 이유

     

    1️⃣ 첫 번째 프로미스 (fetch)

    • 네트워크 연결 + 응답 헤더를 받았는지 여부 확인
    • "응답이 왔다!"를 알려줌 (본문은 아직일 수도 있음)

     

    2️⃣ 두 번째 프로미스 (.json() 같은 메서드)

    • 응답 body를 끝까지 다 받고 응답을 받기 위해 지정한 방식(JSON, 텍스트, Blob 등)으로 파싱 완료되었을 때 알려줌

     

    댓글

Designed by Tistory.