프론트엔드 개발/JavaScript

자바스크립트) fetch API를 통해 본 Promise

Ella Seon 2022. 11. 26. 01:23

1. fetch 란 무엇인가?

- fetch cheatsheet : https://devhints.io/js-fetch

- 자바스크립트에서 서버에 네트워크 요청을 보내고 새로운 정보를 받아오는 일을 하는 함수

- fetch API를 통해 다양한 주문 전송, 사용자 정보 읽기, 서버에서 최신 변경분 가져오기 등등 다양한 일을 페이지 새로고침 없이 수행할 수 있다.

- fetch API는 Promise를 기반으로 동작한다.

 

- 즉, CRUD 기능을 구현할 수 있다.

 

🔸CRUD란?

- 대부분의 컴퓨터 소프트웨어가 가지는 기본적인 데이터 처리 기능인 아래 4가지 항목을 묶어서 일컫는 말이다. 

즉, 데이터를 읽고 쓰고 삭제하는..프로그램에서 꼭 필요한 기능들이다. 

이름 fetch method SNS 페이지에서 일어나는 동작 예시
1. Create(생성) POST 회원가입 정보 등록하기
2. Read(읽기) GET 팔로워 리스트 읽어오기
3. Update(갱신) PUT 게시글 수정
4. Delete(삭제) DELETE 글 삭제하기

 

1-1) fetch 기본적인 예제코드

🔸GET

fetch(url, [options])
.then((response) => reponse.json()) 
.then(data => {console.log(data)})
.catch((err) => console.log("err:", err))
코드 목록 해석
fetch(url, [options]) fetch는 url매개변수는 필수이고, 두번째 매개변수는 선택사항이다. option 에 아무것도 넘기지 않으면 요청은 GET 메서드로 진행된다. 

fetch()를 호출하면 브라우저는 네트워크 요청을 보내고 promise가 반환된다.

1)url : 접근하고자 하는 url
2) [option] : 선택 매개변수(method, headers, body,mode,dredentials,cache  사용 가능)


.then((response) => reponse.json())
1.  response(응답) 1단계 
1) 서버에서 응답 헤더를 받자마자 fetch 호출 시 받은 promise 가 내장 클래스 Response의 인스턴스와 함께 이행 상태가된다. 이 단계는 아직 body가 서버에 도착하기 전이지만 개발자는 응답 헤더를 보고 요청이 성공적으로 처리되었는지 아닌지를 확인 할 수 있다.

 네트워크 문제나 존재하지 않는 사이트에 접속 할 경우 같이 HTTP 요청을 보낼 수 없는 상태에서 프로미스는 거부 상태가 된다.

status : HTTP 상태 코드가 200과 299사이일 경우 true
ok - boolean 값, HTTP 상태 코드가 200과 299 사이일 경우 true



2.  response(응답) 2단계  = response.json()
추가 메서드를 호출해 응답 본문을 받는 단계


get이나 post로 불러온 응답 데이터를 JSON으로 파싱해줌

1) JSON 파싱 : json 형식의 문자열을 객체로 변환하는 것

2) 왜 객체로 변환시키는데 ?
서버와 데이터를 주고 받을때는 문자만 주고 받을 수 있다.
array와 object 같은거 전송 불가능하다. JSON은 문자열이기 때문에 서버와 데이터 주고 받기가 가능함.

JSON을 그대로 받아와서 object로 안바꾸면 자스에서 객체 즉, 우리가 원하는 데이터를 끄집어낼수가 없음. 그래서 object로 변환을 시켜야함

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

파싱 뜻 : 다른 형식으로 저장된 데이터를 원하는 형식의 데이터로 변환하겠다.
.then(data => {console.log(data)}) 데이터를 콘솔창에 뿌리겠다~
.catch((err) => console.log("err:", err)) 오류 발생시 오류를 담아서 보여줌

 

🔸실제 json 샘플데이터 url을 사용해보자

fetch('https://jsonplaceholder.typicode.com/comments')
  .then((response) => response.json())
  .then((data) => console.log(data));

 

🔸POST

fetch(url, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
        },
        body: JSON.stringify(data)
    })
    .then(response => response.json())
    .then(data => {
        console.log(data)
     })

 

🔸위에 언급한 옵션에 들어가는 속성들 설명

fetch(url, {
  method : 사용할 메소드를 선택 ('GET', 'POST', 'PUT', 'DELETE' ),
  headers : 헤더에 전달할 값 ( { 'content-Type': 'application/json' } ),
  body : 바디에 전달할 값 ( JSON.stringify(data) ),
  mode : 'cors' 등의 값을 설정 (cors, no-cors, same-origin),
  credentials : 자격 증명을 위한 옵션 설정 (include, same-origin, omit)(default = same-origin),
  cache : 캐쉬 사용 여부 (no-cache, reload, force-cache, only-if-cached),
})
옵션 설명
method 사용할 메소드를 선택 ('GET', 'POST', 'PUT', 'DELETE' )
header :{ 'content-Type': 'application/json' } content-type : 자원의 형식을 명시하기 위해 헤더에 실리는 정보를 뜻한다.
여기서는 json 이라고 표시했으니 json 형식으로 전달하겠다.
body : JSON.stringify(data) 네트워크를 통해 전달할 수 있게 object객체를 JSON문자열로 변환하는 과정은 문자열화(Stringification)이라고 합니다.

2. fetch API 를 통해 알아본 promise

JavaScript 에서 코드가 순차적으로 일어나는 처리를 동기 처리라 하며,

특정 코드가 처리될 때 까지 기다리지 않고 다음 코드를 먼저 처리하는 걸 비동기 처리라 한다.

이러한 비동기 처리에서 콜백 지옥(Callback Hell) 등의 문제를 해소하기 위해 사용하는 객체,

and 실행이 되었는데 결과값을 나중에 쓸 수 있는 것 => 나중에 쓸 수 있기 때문에 코드를 분리할 수 있다.

// case 1 : 콜백 HELL 🔥

get("/step1", (a) => {
  get(`/step2/${a}`, (b) => {
    get(`/step3/${b}`, (c) => {
      get(`/step4/${c}`, (d) => {
        console.log(d);
      });
    });
  });
});

 

※ Promise의 최대 장점을 콜백함수에 빗대어 보자

🔸콜백함수와 Promise 의 차이

- 콜백함수는 코드 분리를 못한다. 아래 방식이 최선이다. (결과를 받고 싶지 않아도 결괏값을 바로 받아야한다)
콜백헬
- setTimeoutPromise 라는게 가상으로 있다라고 치고, Promise 는 아래처럼 코드를 promise 변수에 담고 분리를 할 수가 있다. (결과를 나중에 받을 수 있다)
하지만, Promise.all 은 하나만 실패해도 나머지 다 실패처리해서 catch 로 가버리는 단점이있다. 따라서 성공한것도 다시 다 시도해야함.
Promise.allSettled 는 성공한거 results 에 받고, 실패한것만 catch 로 간다.  

 

 비동기 처리에서 순차적 처리를 행하기 위해 사용되는 객체가 프로미스(Promise)이다.

(순자적 실행을 위해 콜백함수 대신 쓸수있는 코딩패턴)

 

🔸fetch API로 promise를 알아보자

- 언제끝날지 알수없는 서버와의 통신은 비동기적으로 처리한다. 

- 아래 예제로 콘솔창에 출력해보자

console.log(1)
fetch('https://jsonplaceholder.typicode.com/comments')
  .then((response) => response.json())
  .then((data) => console.log(data));
console.log(2)

- 1과 2가 먼저 출력되고, fetch API는 맨 마지막에 출력되는 것을 확인할 수 있다.(비동기처리)

 

🔸fetch API 는 Promise 데이터타입을 리턴한다. Promise 데이터 타입은 객체를 response한다.

- 아래 코드를 적어보면 콘솔창에 Promise 데이터 타입이 리턴된다.

let fetched = fetch('https://jsonplaceholder.typicode.com/comments');
console.log('fetched',fetched);

- 우리가 어떤 함수를 리턴할때, Promise가 리턴된다면 비동기적으로 동작할 가능성이 높다.

- 함수가 리턴한 값은 then과 catch라는 메소드를 사용할 수 있다.

- then과 catch는 콜백함수를 인자로 갖는다.

- then 의 파라미터는 result를 알려주고, catch의 파라미터는 reason을 알려준다.(물론 파라미터 명은 아무렇게나 쓸수있음)

- then은 fetch를 통해 실행한 결과가 성공했을때, then으로 전달된 콜백함수가 호출되도록 약속되어있다. 콜백함수가 호출되면서 결과값이 있다면 첫번째 파라미터로 받을 수 있다. Promise가 호출되는 fetch API가 실패를 한다면, catch안으로 전달된 콜백함수가 실행되고 이유를 알려준다.

let fetched = fetch('https://jsonplaceholder.typicode.com/comments');
console.log('fetched',fetched);
fetched.then(function(result){
    console.log('result',result)
});
fetched.catch(function(reason){
    console.log('reason',reason)
})

 

콘솔창 출력결과 Response 타입의 객체를 볼수있다.

- Fetch의 리턴값은 Promise이고 성공적으로 실행이 되면 Response객체를 준다.

 

fetch url 주소를 일부러 틀리게 해서 콘솔창 출력 결과를 살펴보자

let fetched = fetch('https://jsonplceholder.typicode.com/commts');
console.log('fetched',fetched);
fetched.then(function(result){
    console.log('result',result)
});
fetched.catch(function(reason){
    console.log('reason',reason)
})

실패할때는 catch 메소드가 발동되어 왜 실패했는지 이유가 나와있다.

 

🔸Promise를 사용하는 이유

- 비동기적인 작업을 처리할때, 그 작업이 성공했는지, 실패했는지 표준화된 방식을 이용해서 처리할수있도록 해준다.

- 성공했을때는 then, 실패했을때는 catch 메소드가 발동이 된다.

- response.json() 의 리턴값도 Promise로 나온다. 즉, response.json()메서드가 실행되고 나서, then을 호출할것이다 라는 뜻이다.

- 즉, fetch()를 쓰고나면 Promise가 리턴이 되는데, Promise가 리턴되는 곳은 뒤에 then, catch를 붙여서 Promise체이닝을 할 수가 있다.

 

🔸Promise 의 상태

- 생성된 직후의 프로미스는 기본적으로 pending 상태다. 이후 비동기 처리가 수행되면 비동기 처리 결과에 따라 다음과 같이 프로미스의 상태가 변경된다


3. Promise 만드는 방법

- Promise 생성자 함수를 new 연산자와 함께 호출하면 프로미스(Promise 객체)를 생성한다.

- Promise()를 생성하는 Producer 파트와, Promise를 사용하는 Consumer 파트로 나눌수있다. (이부분이 Promise 의 최대 장점이다. 코드 분리가 가능하다.)

- Promise() 에는 인자로 이 작업이 성공했는지, 실패했는지 알수있는 콜백함수가 들어가야한다. 

- 콜백함수 안에는 2개의 파라미터가 들어간다 ( 성공했을때, 실패했을때 파라미터), 파라미터 명은 아무거나 써도 된다.

그런데 보통은 resolve, reject를 많이 쓴다. 

- 그리고 중괄호 안에 resolve() 함수형태로 작성하면 then 이하의 코드가 실행이된다.

// 1. Producer : 프로듀서 만드는 파트

var 프로미스 = new Promise(function(resolve, reject){
 resolve()
});

//2. Consumers : then,catch 로 프로듀서 사용하는 파트

프로미스.then(function(){
	console.log('성공했을 때 출력되는 코드') //성공했을 때 출력되는 코드
}).catch(function(){
	console.log('실패했을 때 출력되는 코드')
});

 

🔸setTimeout을 이용한 예제

 

- 비동기작업이 처리되고, 그 결과가 끝났을 때 Promise의 콜백으로 전달된 첫번째 파라미터에 담겨있는 함수를 호출하면, 작업이 끝났다라는 사실을 알려줄수있다.

    let 프로미스 = new Promise((resolve,reject)=>{
      setTimeout(()=>{
        resolve('2초뒤에 실행하는 코드')
      },2000)
    })

    프로미스.then((data)=>{
      console.log('data',data) //data 2초뒤에 실행하는 코드
    })

- 그런데 보통은 아래처럼 함수 안에서 Promise를 리턴해준다.

    function 프로미스() {
      return new Promise((resolve, reject) => {
        setTimeout(()=>{
          resolve('성공!')
        }, 2000)
      });
    }

    프로미스().then((data) => {
      console.log('data', data)
    })

- Promise는 중첩해서 사용할때 빛을 발휘한다.

    function 첫번째함수() {
      return new Promise((resolve, reject) => {
        setTimeout(()=>{
          resolve('첫번째 함수 성공!')
        }, 2000)
      });
    }
    function 두번째함수() {
      return new Promise((resolve, reject) => {
        setTimeout(()=>{
          resolve('두번째 함수 성공!')
        }, 2000)
      });
    }

    첫번째함수().then((data)=>{
      console.log('data1',data)
      return 두번째함수(); //두번째함수도 Promise임
    }) .then((data)=>{
      console.log('data2',data)
    })

4. Promise 빌트인 객체가 제공하는 정적 메서드

1️⃣Promise.resolve/ Promise.reject

// 정적 메서드 사용
const resolvedPromise = Promise.resolve([1, 2, 3]);
resolvedPromise.then(console.log); // [1, 2, 3]

위 예제는 다음 예제와 동일하게 동작한다

// 생성자 함수를 통해 프로토타입 메서드 사용
const resolvedPromise = new Promise((resolve) => resolve([1, 2, 3]));
resolvedPromise.then(console.log); // [1, 2, 3]


2️⃣Promise.all (잘 안씀)

- Promise.all 메서드는 여러 개의 비동기 처리를 모두 병렬처리할 때 사용한다.

= 인자로 전달된 프로미스들이 모두 완료될 때까지 기다린 후, 그 결과를 담은 배열을 반환하는 메서드

const requestData1 = () =>
  new Promise((resolve) => setTimeout(() => resolve(1), 3000));
const requestData2 = () =>
  new Promise((resolve) => setTimeout(() => resolve(2), 2000));
const requestData3 = () =>
  new Promise((resolve) => setTimeout(() => resolve(3), 1000));

// 세 개의 비동기 처리를 순차적으로 처리
const res = [];
requestData1()
  .then((data) => {
    res.push(data);
    return requestData2();
  })
  .then((data) => {
    res.push(data);
    return requestData3();
  })
  .then((data) => {
    res.push(data);
    console.log(res); // [1, 2, 3] ⇒ 약 6초 소요
  })
  .catch(console.error);

위 예제는 세 개의 비동기 처리를 순차적으로 처리한다.(then 메서드를 통해)
즉, 앞선 비동기 처리가 완료되면 다음 비동기 처리를 수행한다. 따라서 위 예제는 3 , 2 , 1 초를 다 더한 6초 이상이 소요된다.
따라서 이런 상황에서 Promise.all 메서드를 사용한다면 병렬적으로 처리할 수 있다.

const requestData1 = () =>
  new Promise((resolve) => setTimeout(() => resolve(1), 3000));
const requestData2 = () =>
  new Promise((resolve) => setTimeout(() => resolve(2), 2000));
const requestData3 = () =>
  new Promise((resolve) => setTimeout(() => resolve(3), 1000));

Promise.all([requestData1(), requestData2(), requestData3()])
  .then(console.log) // [ 1, 2, 3 ] ⇒ 약 3초 소요
  .catch(console.error);

Promise.all 메서드는 인수로 전달받은 배열의 모든 프로미스가 fulfilled 상태가 되면 종료한다. 따라서 Promise.all 메서드가 종료하는 데 걸리는 시간은 가장 늦게 fulfilled 상태가 되는 프로미스의 처리 시간보다 조금 더 길다(3초보다 조금 더 소요된다.)
프로미스의 처리 시간에 의해 총 처리 시간이 결정되지만, 순서는 코드의 처리 순서를 보장한다.
첫 번째 프로미스가 가장 나중에 fulfilled 상태가 되어도 Promise.all 메서드는 첫 번째 프로미스가 resolve한 처리 결과부터 차례대로 배열에 저장해 그 배열을 resolve하는 새로운 프로미스를 반환한다.
Promise.all 메서드는 모든 프로미스가 fulfilled 상태가 되면 모든 처리 결과를 배열에 저장해 새로운 프로미스를 반환하므로, 해당 처리중에 rejected가 발생될 경우 에러가 발생한다. fulfilled 상태가 되는 것을 기다리지 않고 즉시 종료한다

 

3️⃣Promise.race

- Promise.race 메서드는 Promise.all 메서드와 동일하게 프로미스를 요소로 갖는 배열 등의 이터러블을 인수로 전달받는다.
- 하지만 Promise.race 메서드는 모든 프로미스가 fulfilled 상태가 되는 것을 기다리는 것이 아니라 가장 먼저 fulfilled 상태가 된 프로미스의 처리 결과를 resolve하는 새로운 프로미스를 반환한다.

Promise.race([
  new Promise((resolve) => setTimeout(() => resolve(1), 3000)), // 1
  new Promise((resolve) => setTimeout(() => resolve(2), 2000)), // 2
  new Promise((resolve) => setTimeout(() => resolve(3), 1000)), // 3
])
  .then(console.log) // 3
  .catch(console.log);


4️⃣Promise.allSettled ✨✨✨✨✨(중요)

- Promise.allSettled 메서드는 프로미스를 요소로 갖는 배열 등의 이터러블을 인수로 전달받는다.

- 그리고 이름과 알 수 있듯이 전달받은 프로미스가 모두 settled 상태(비동기 처리가 수행된 상태, fulfilled / rejected 상태)가 되면 처리 결과를 배열로 반환한다.

Promise.allSettled([
  new Promise((resolve) => setTimeout(() => resolve(1), 2000)),
  new Promise((_, reject) =>
    setTimeout(() => reject(new Error("Error!")), 1000)
  ),
]).then(console.log);
/*
[
  {status: "fulfilled", value: 1},
  {status: "rejected", reason: Error: Error! at <anonymous>:3:54}
]
*/

 

 

출처 

- https://pstudio411.tistory.com/entry/fetch-API

 

fetch API

fetch API AJAX란 무엇일까? AJAX는 (Asynchronous Javascript And XML, 비동기적 자바스크립트와 XML)의 약자로 XMLHttpRequest(XHR)과 Javascript와 DOM을 이용하여 서버에서 추가 정보를 비동기적으로 가져올 수 있게

pstudio411.tistory.com

- json 파싱

https://zeddios.tistory.com/90

 

왕초보를 위한 JSON Parsing - 1 (JSON이란?)

안녕하세요! 오늘은 정말 왕왕초보 쌩초보를 위한 JSON Parsing을 배워볼거에요. (참고로 저도 왕초보임)정말 하나하나 배워가 봅시다. 이 글을 좀 나눠서 올리는게 좋을 것 같아요 ㅎㅎ 이번 부스

zeddios.tistory.com

- Promise 

생활코딩 JavaScript-Promise(then,catch) 유튜브 https://www.youtube.com/watch?v=Sn0ublt7CWM 

 

https://chanhuiseok.github.io/posts/js-6/

 

JavaScript - 자바스크립트 fetch와 async/await

컴퓨터/IT/알고리즘 정리 블로그

chanhuiseok.github.io

https://velog.io/@seoltang/fetch-POST-Request

https://sewonzzang.tistory.com/27

 

[Javascript] fetch

자바스크립트를 사용하면 필요할 대 서버에 네트워크 요청을 보내고 새로운 정보를 받아오는 일을 할 수 있습니다. 네트워크 요청은 다음과 같은 경우에 이뤄집니다. 주문 정보 사용자 정보 읽

sewonzzang.tistory.com