All Articles

fetch() 함수 사용법

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

참고로 Web API는 클라이언트 측에서 사용할 수 있는 자바스크립트 내장함수라고 생각하시면 됩니다. 실무에서는 여러 이유로 axios를 많이 사용하지만, fetch 함수로도 웬만한 기능을 충분히 구현할 수 있습니다. 이걸 읽고 계신 >wecode 분들도 4주차 과제까지, 그리고 1차 프로젝트 까지는 fetch() 함수를 사용하기를 권장 드립니다.

axios 사용법도 굉장히 쉽습니다. fetch를 쓰다가 axios를 사용하는 것만으로는 사실 학습 의미가 없습니다. 어느 라이브러리를 더 써보았는지가 중요한 것이 아니라 http 통신의 요청와 응답에 대한 이해, Promise 개념 공부가 더 중요합니다. 이 글을 읽고 fetch()를 사용하여 코드를 구현해봤다면, Promise 에도 관심을 갖고 공부를 시작해 보세요.

아래의 모든 실습은 본인이 postman을 통해 api 호출 테스트에 성공했다고 가정하고 진행하셔야 합니다. 코드부터 작성하지 말고, api가 올바로 작동하는지 확인하고 개발하세요!

fetch() 함수 기본

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

여러분! 위의 형태로 기억하라는 말을 몇 번 했는데, 각각의 단계가 무엇을 뜻하는지 명확히 알아야 합니다. 그리고 화살표 함수로 몸통을 확 줄인 위의 코드도 ES5의 함수 선언식으로 바꿨을 때 어떤 모양인지 아셔야 더 복잡한 fetch를 구현할 수 있습니다.

바로 다음으로 넘어가지 마시고 위 코드에서 보이는 화살표 함수를 ES5의 함수 선언식으로 직접 바꿔 보시고 넘어가세요.

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

네 본인이 바꿔본 코드랑 똑같으신가요?

첫 번째 코드에서 res를 보며 드는 생각은 무엇이었나요? 3개의 res가 모두 똑같은 변수라고 생각했으면 변수의 scope(스코프) 부터 다시 공부하고 와야 합니다. 위 코드에서 변수의 scope는 각 함수이므로 첫 번째 then와 두 번째 then 안에 있는 res는 서로 다른 것. 당연히 아시죠?

단지 둘다 응답이다 보니 response의 줄임말인 res를 사용했을 뿐입니다. 평소 fetch()를 질문 받을 때 이 것을 헷갈리는 분이 너무 많아서 언급했습니다.

fetch() 함수 - method가 get인 경우

fetch() 함수에서 default method는 get입니다. 그래서 위의 코드는 get으로 호출한 것입니다. 하지만 한 번만 더 연습해보고 넘어가죠!

아래와 같은 api 명세를 보고 어떻게 fetch()를 사용하면 되는지 작성해보세요.

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

네 호출하려면 아래와 같이 하면 됩니다.

fetch('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, { Component } from 'react';

class User extends Component {
  componentDidMount() {
    // user id가 props를 통해 넘어온다고 가정
    const { userId } = this.props;

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

fetch() 함수 - method가 post인 경우

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

아! 호출해야할 api가 get인지, post인지 모른다구요? 당연히 api를 개발한 백앤드 개발자에게 물어보셔야 합니다. 이 또한 가끔씩 멘토한테 와서 api 정보를 몰라서 호출을 못하겠다! 하시는데, api 정보를 아는 것은 오로지 api를 만든 개발자 뿐입니다.

네, 이번에는 아래와 같은 api 명세를 받았다고 합시다.

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

응답 body:
    {
        "success": boolean
    }

아래와 같이 구현합니다.

fetch('https://api.google.com/user', {
    method: 'post',
    body: JSON.stringify({
        name: "yeri",
        batch: 1
    })
  })
  .then(res => res.json())
  .then(res => {
    if (res.success) {
        alert("저장 완료");
    }
  })

넵.. get method로 호출하는 것 보다 많이 복잡해보이네요.

  1. 두 번째 인자에 method와 body를 보내주어야 합니다.
  2. method는 post
  3. body는 JSON형태로 보내기 위해 JSON.stringfy() 함수에 객체를 인자로 전달하여 JSON형태로 변환했습니다.

post로 데이터를 보낼 때 JSON.stringfy를 항상 하다보니 axios는 굳이 감싸주지 않고 객체만 작성해도 되는 편리한 점이 있습니다. 이렇듯 axios는 소소하게 편한한 설정을 제공해주고, 요청과 응답에 대한 확장성 있는 기능을 만들 수 있습니다.

fetch() 함수 - 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')
  .then(res => res.json())
  .then(res => {
    if (res.success) {
        console.log(`${res.user.name}` 님 환영합니다);
    }
  });

fetch() 함수 - res.json()의 의미

잠시 post 예제를 다시 보겠습니다. 모든 코드에 then이 두 번 있고, 첫 번째 then에서 res => res.json() 이 도대체 뭘까 궁금하지 않으셨나요?

fetch('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("저장 완료");
    }
  })

자세히 설명하자면 promise 개념을 알아야 하는데, 이 포스팅에서는 promise 얘기는 하지 않으려고 합니다. 어떤 뜻인지 내용만 집고 넘어가겠습니다.

첫 번째 then의 res가 어떤 값이 들어오길래 res.json()을 리턴하는가?? 궁금하면 어떻게 하라고요? 네! console.log를 찍어봐야죠.

위의 화살표 함수에서 console.log를 찍어보려면 어떻게 해야 하나요?? 네 바로 return 하는 화살표 함수에 바디를 다시 만들어줘야 합니다. 그래서 코드 가장 처음에 ES5의 함수 선언식으로 바꿔보라고 한 것입니다. 함수 선언식으로 바꾸지는 않지만 바디가 추가된 화살표 함수로 수정해보겠습니다.

fetch('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의 데이터를 받을 수 있습니다.

fetch() 함수 - 첫 번째 then 함수에 추가되는 로직

위의 내용까지 읽으면 fetch().then().then() 형태만 기억하면 될 것 같은데 왜 저렇게 설명했냐구요? 바로 백앤드에서 응답 body를 안 주는 경우도 많기 때문입니다!

응답 body로 JSON 데이터를 주지 않는데 프론트에서 Response Object의 json()을 호출하면 에러가 납니다.

다음과 같은 상황을 생각해보면 됩니다.

설명: 유저를 저장한다.
base url: 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', {
    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);
  })

어때요! 이제는 왜 then이 두 개 이고, 각각 어떤 역할을 하는지 잘 이해 되셨나요? 오늘도 공부하시느라 수고 많으셨습니다! 이제부터는 Promise를 파봅시다!!! 화이팅

Reference