TIL#35 React ) fetch 함수

luneah·2021년 12월 4일
1

React

목록 보기
13/15
post-thumbnail

fetch 함수

백앤드로부터 데이터를 받아오려면 api를 호출하고 데이터를 응답받는다. 이 때 자바스크립트 Web API fetch() 함수를 쓰거나 axios 라이브러리를 사용할 수 있다.

참고로 Web API는 클라이언트 측에서 사용할 수 있는 자바스크립트 내장함수라고 생각하면 된다. 실무에서는 여러 이유로 axios를 많이 사용하지만, fetch 함수로도 웬만한 기능을 충분히 구현할 수 있다.

fetch() 함수 기본

fetch('api 주소')
  .then(res => res.json())
  .then(res => {
    // data를 응답 받은 후의 로직
  });


// 위의 코드에서 화살표 함수를 ES5의 함수 선언식으로 바꿈
fetch('api 주소')
  .then(function(res) {
    return res.json();
  })
  .then(function(res) {
    // data를 응답 받은 후의 로직
  });

위 코드에서 변수의 scope는 각 함수이므로 첫 번째 then와 두 번째 then 안에 있는 res는 서로 다른 것이다. 단지 둘 다 응답이다 보니 response의 줄임말인 res를 사용했을 뿐이다.

method가 GET인 경우

fetch() 함수에서 default method는 get이다. 그렇다면 아래와 같은 api 명세를 보고 어떻게 fetch()를 사용하면 될까?

설명: 유저 정보를 가져온다.
base url: [https://api.google.com](https://api.google.com/)
endpoint: /user/3
method: get
응답형태:
    {
        "success": boolean,
        "user": {
            "name": string,
            "batch": number
        }
    }
  • fetch 함수 사용한 호출 방법
fetch('[https://api.google.com/user/3](https://api.google.com/user/3)')
  .then(res => res.json())
  .then(res => {
    if (res.success) {
        console.log(`${res.user.name}` 님 환영합니다);
    }
  });

그런데 api 주소를 보니 user 뒤에 있는 3이 user id 이다. 고정된 api라면 그냥 자바스크립트 코드에서도 고정해서 사용하면 되는데, 위와 같이 api 주소를 상황에 맞게 유동적으로 바꿔줘야 할 때가 정말 많다.

리액트를 한다고 가정하면, 아래와 같이 구현할 수 있다.

import React, { useEffect } from 'react';

function User(props) {
  useEffect(() => {
      const { userId } = props;
	    fetch(`https://api.google.com/user/${userId}`)
	      .then(res => res.json())
	      .then(res => {
	        if (res.success) {
	            console.log(`${res.user.name}` 님 환영합니다);
	        }
	    });      
    }, []);
  ...
}

method가 POST인 경우

fetch() 함수의 기본값은 get이기 때문에 아무것도 작성하지 않아도 get으로 호출했는데, post인 경우에는 fetch() 함수에 method 정보를 인자로 넘겨주어야 한다.

호출해야 할 api가 get인지, post인지 모를 땐 당연히 api를 개발한 백앤드 개발자에게 물어봐야 한다. api 정보를 아는 것은 오로지 api를 만든 개발자 뿐이다.

그렇다면 아래와 같은 api 명세를 받았다고 할 때 어떻게 fetch()를 사용할까?

설명: 유저를 저장한다.
base url: [https://api.google.com](https://api.google.com/)
endpoint: /user
method: post
요청 body:
    {
        "name": string,
        "batch": number
    }

응답 body:
    {
        "success": boolean
    }
  • fetch 함수 사용한 호출 방법
fetch('[https://api.google.com/user](https://api.google.com/user)', {
    method: 'post',
    body: JSON.stringify({
        name: "yeri",
        batch: 1
    })
  })
  .then(res => res.json())
  .then(res => {
    if (res.success) {
        alert("저장 완료");
    }
  })
  1. 두 번째 인자에 method 와 body 를 보내주어야 한다.
  2. method는 post
  3. body는 JSON형태로 보내기 위해 JSON.stringfy() 함수에 객체를 인자로 전달하여 JSON형태로 변환했다.

fetch 함수는 post로 데이터를 보낼 때 JSON.stringfy를 해주어야 하는 반면, axios는 객체를 그대로 작성해도 되는 편리한 점이 있다. 이렇듯 axios는 소소한 편의성을 제공해주고, 요청과 응답에 대한 확장성 있는 기능을 만들 수도 있다.

method가 get인데 parameter를 전달해야 하는 경우

위의 get 예제에서는 3이라는 user id를 path로 넘겨주었다. 그런데 path 말고 query string으로 넘겨줘야 할 수도 있다.

언제는 path로 언제는 query string이고 할 수 있다는 말은 아니고, 예제이다 보니 같은 api를 사용했다. 데이터를 전달하는 방식 또한 백앤드 개발자에게 물어봐야 한다.

설명: 유저 정보를 가져온다.
base url: https://api.google.com
endpoint: /user
method: get
query string: ?id=아이디
응답형태:
    {
        "success": boolean,
        "user": {
            "name": string,
            "batch": number
        }
    }

api 주소 뒤에 그냥 붙여주면 된다.

fetch('[https://api.google.com/user?id=3](https://api.google.com/user?id=3)')
  .then(res => res.json())
  .then(res => {
    if (res.success) {
        console.log(`${res.user.name}` 님 환영합니다);
    }
  });

res.json()의 의미

post 예제를 다시 보면 모든 코드에 then이 두 번 있고, 첫 번째 then에서 res => res.json() 은 도대체 뭘까?

fetch('[https://api.google.com/user](https://api.google.com/user)', {
    method: 'post',
    body: JSON.stringify({
        name: "yeri",
        batch: 1
    })
  })
  .then(res => res.json())   // 왜 then이 두개고 res.json() 은 뭔지?
  .then(res => {
    if (res.success) {
        alert("저장 완료");
    }
  })

첫 번째 then의 res가 어떤 값이 들어오길래 res.json()을 리턴하는지 궁금하면 console.log를 찍어보면 된다. 그렇다면 위의 화살표 함수에서 console.log를 찍어보려면 어떻게 해야 하는가? 바로 return 하는 화살표 함수에 바디를 다시 만들어줘야 한다. 함수 선언식으로 바꾸지는 않지만 바디가 추가된 화살표 함수로 수정해보겠다.

fetch('[https://api.google.com/user](https://api.google.com/user)', {
    method: 'post',
    body: JSON.stringify({
        name: "yeri",
        batch: 1
    })
  })
  .then(res => {       // 첫 번째 then
    console.log(res);  // 어떤 값이 나오는지 확인해보세요. 실제 잘 작동하는 api 주소가 필요합니다.

    return res.json();
  })
  .then(res => {       // 두 번째 then
    if (res.success) {
        alert("저장 완료");
    }
  })

첫 번째 then 함수에 전달된 인자 res는 http 통신 요청과 응답에서 응답의 정보를 담고 있는 객체이다. Response Object 라고 한다.

그런데 console을 확인해보면 백앤드에서 넘겨주는 응답 body, 즉 실제 데이터는 보이지 않을 것이다. 즉 { success: true } 라는 JSON 데이터는 위의 코드로는 console에 찍히지 않을 것이라는 말이다.

응답으로 받는 JSON 데이터를 사용하기 위해서는 Response Object 의 json 함수를 호출하고, return 해야한다. 그러면 두 번째 then 함수에서 응답 body의 데이터를 받을 수 있다.

첫 번째 then 함수에 추가되는 로직

위의 내용까지 읽으면 fetch().then().then() 형태만 기억하면 될 것 같은데 백앤드에서 응답 body를 안 주는 경우도 많기 때문에 설명이 필요하다.

응답 body로 JSON 데이터를 주지 않는데 프론트에서 Response Object의 json()을 호출하면 에러가 난다. 다음과 같은 상황을 생각해보면 된다.

설명: 유저를 저장한다.
base url: [https://api.google.com](https://api.google.com/)
endpoint: /user
method: post
요청 body:
    {
        "name": string,
        "batch": number
    }

응답 body:
    1. 제대로 저장했으면 status code를 200 전달. 응답 body는 없음
    2. 권한 오류가 생기면 status code를 403으로 전달하고. 응답 body는 아래와 같음
        {
            success: false,
            message: "권한이 없습니다"
        }

위의 상황일 때 어떻게 fetch() 를 구현하면 될까?

fetch('[https://api.google.com/user](https://api.google.com/user)', {
    method: 'post',
    body: JSON.stringify({
        name: "yeri",
        batch: 1
    })
  })
  .then(res => {
    if (res.status === 200) {
        alert("저장 완료");
    } else if (res.status === 403) {
        return res.json();
    }
  })
  .then(res => {
    console.log("에러 메시지 ->", res.message);
  })

Reference

profile
하늘이의 개발 일기

0개의 댓글