본문 바로가기

React

[React] API 연동

백엔드를 무작정 코딩하면서 공부해보려고 했는데

 

엄두가 안나서 기본 개념부터 배워보려고 합니다.

 

오늘은 API 연동에 대해서 공부해 보려고 하는데

 

알고 있어야 할 개념이 있습니다.

 

(벨로퍼트님의 강의를 참고하였습니다.)

 

비동기 처리 함수라는 것이 있는데

 

비동기라는 말은 작업을 동시에 처리하는 것을 의미합니다.

 

만약 서버쪽에서 데이터를 받와아야 할 때는, 요청을 하고 서버에서

 

응답을 할 때 까지 대기를 해야 되기 때문에 작업을 비동기적으로 처리합니다.

 

비동기 처리는 보통 Promise나 async/await함수를 사용합니다.

const myPromise = new Promise((resolve, reject) => {
  // 구현..
})

 

Promise함수의 형태입니다.

 

성공할 때는 resolve를 호출하고 실패할 때는 reject를 호출합니다.

 

myPromise.then(n => {
  console.log(n);
});

 

함수가 resolve 된 후 다음 작업을 해야할 때는 .then을 붙여서 사용합니다.

 

.catch(error => {
    console.log(error);
  });

 

함수가 reject 된 후 다음 작업을 해야할 때는 .catch를 붙여서 사용합니다.

 

reject함수는 사용되지 않으면 생략된다고도 합니다.

 

function increaseAndPrint(n) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const value = n + 1;
      if (value === 5) {
        const error = new Error();
        error.name = 'ValueIsFiveError';
        reject(error);
        return;
      }
      console.log(value);
      resolve(value);
    }, 1000);
  });
}

increaseAndPrint(0)
  .then(increaseAndPrint)
  .then(increaseAndPrint)
  .then(increaseAndPrint)
  .then(increaseAndPrint)
  .then(increaseAndPrint)
  .catch(e => {
    console.error(e);
  });

 

작업이 이어질 때 보통은 call back 함수를 사용해서 코드가 길어지는데

 

Promise의 then을 사용해서 코드의 양을 대폭 감소시킬 수 있습니다.

 

하지만 에러를 잡기 어렵고,

 

특정 값을 공유해가면서 작업을 처리하기도 까다롭습니다. 

 

async/await 함수를 사용하면 이런 문제점을 해결할 수 있습니다.

 

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function process() {
  console.log('안녕하세요!');
  await sleep(1000); // 1초쉬고
  console.log('반갑습니다!');
}

process();

 

async/await 함수의 기본 형태입니다.

 

함수 앞에 async를 붙이고 Promise 부분에 await를 붙이면

 

Promise 작업이 끝나고 코드를 실행합니다.

 

여기서 Promise 부분은

  await sleep(1000);

 

sleep의 단위는 ms로 1000ms = 1초입니다.

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function makeError() {
  await sleep(1000);
  const error = new Error();
  throw error;
}

async function process() {
  try {
    await makeError();
  } catch (e) {
    console.error(e);
  }
}

process();

 

에러가 발생하면 throw함수와 catch 함수를 사용한다고 합니다.

 

throw연산자는 에러를 생성합니다.

 

에러가 없다면 catch문을 점프하고 try를 끝까지 실행하고

 

에러가 발생하면 catch를 실행합니다.

 

위 코드에서는 throw문으로 인해 catch문이 실행되겠네요.

 

 

 

여기까지가 비동기 함수에 대한 개념이었고,

 

API 연동에 대해서 알아봅시다.

 

API를 연동할 때는 axios라는 라이브러리를 사용합니다.

 

API 요청에는 4가지 형식이 있는데

 

  • GET: 데이터 조회
  • POST: 데이터 등록
  • PUT: 데이터 수정
  • DELETE: 데이터 제거

이것을 Representational State Transfer API, 줄여서 Rest API라고 부릅니다.

 

import axios from 'axios';

axios.get('/users/1');

 

데이터를 조회하는 GET의 형식입니다.

 

파라미터에는 API주소가 들어갑니다.

 

axios.post('/users', {
  username: 'blabla',
  name: 'blabla'
});

 

데이터를 등록하는 POST의 형식입니다.

 

첫 번째 파라미터에는 API주소가 들어가고

 

두 번째 파라미터에는 등록하고자하는 정보가 들어갑니다.

 

 

 

다음은 실습을 해보겠습니다.

 

 JSONPlaceholder 에 있는 연습용 API 를 사용해서 진행하겠습니다.

 

https://jsonplaceholder.typicode.com/users

 

API 주소이고 형식은 이렇습니다.

 

[
  {
    "id": 1,
    "name": "Leanne Graham",
    "username": "Bret",
    "email": "Sincere@april.biz",
    "address": {
      "street": "Kulas Light",
      "suite": "Apt. 556",
      "city": "Gwenborough",
      "zipcode": "92998-3874",
      "geo": {
        "lat": "-37.3159",
        "lng": "81.1496"
      }
    },
    "phone": "1-770-736-8031 x56442",
    "website": "hildegard.org",
    "company": {
      "name": "Romaguera-Crona",
      "catchPhrase": "Multi-layered client-server neural-net",
      "bs": "harness real-time e-markets"
    }
  },
  {
    "id": 2,
    "name": "Ervin Howell",
    "username": "Antonette",
    "email": "Shanna@melissa.tv",
    "address": {
      "street": "Victor Plains",
      "suite": "Suite 879",
      "city": "Wisokyburgh",
      "zipcode": "90566-7771",
      "geo": {
        "lat": "-43.9509",
        "lng": "-34.4618"
      }
    },
    "phone": "010-692-6593 x09125",
    "website": "anastasia.net",
    "company": {
      "name": "Deckow-Crist",
      "catchPhrase": "Proactive didactic contingency",
      "bs": "synergize scalable supply-chains"
    }
  },
  (...)
]

 

VSC를 열고 새 프로젝트를 만들어 줍시다.

 

 

데이터 요청에 대한 상태를 관리 할 때에는 다음과 같이 총 3가지 상태를 관리해주어야합니다.

  1. 요청의 결과
  2. 로딩 상태
  3. 에러
import React, { useState, useEffect } from 'react';
import axios from 'axios';

function Users() {
  const [users, setUsers] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchUsers = async () => {
      try {
        // 요청이 시작 할 때에는 error 와 users 를 초기화하고
        setError(null);
        setUsers(null);
        // loading 상태를 true 로 바꿉니다.
        setLoading(true);
        const response = await axios.get(
          'https://jsonplaceholder.typicode.com/users'
        );
        setUsers(response.data); // 데이터는 response.data 안에 들어있습니다.
      } catch (e) {
        setError(e);
      }
      setLoading(false);
    };

    fetchUsers();
  }, []);

  if (loading) return <div>로딩중..</div>;
  if (error) return <div>에러가 발생했습니다</div>;
  if (!users) return null;
  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>
          {user.username} ({user.name})
        </li>
      ))}
    </ul>
  );
}

export default Users;

 

useEffect를 통해 컴포넌트가 렌더링되는 시점에 요청을 시작합니다.

 

이때 주의할 점은 async함수를 useEffect 파라미터로 할 수 없기 때문에

 

내부에 따로 선언해 준 후 사용해야 합니다.

 

setLoading을 true로 바꾸고 

 

주어진 api에 get요청을 하여 가져온 데이터를

 

setUsers를 통해서 등록해줍니다.

 

데이터의 get요청이 끝나면 

 

loading을 false로 바꿔줍니다.

 

이때 get요청이 진행되는 동안 div태그롤 이용해서  '로딩중.. '이란 문구를 보여줍니다.

 

에러가 나면 '에러가 발생했습니다'를 출력합니다.

 

데이터를 잘 가져와 출력

 

 

 

데이터를 가져오는 동안은

 

로딩중

 

로딩중이라 표시됩니다.

 

 

이때 API주소를 바꿔보면

 

에러 메세지

 

 

에러 처리가 된 것을 볼 수 있습니다.

 

다음 시간에는 useReducer에 대해서 복습해보도록 하겠습니다.

 

감사합니다!