본문 바로가기
Frontend/ReactJS(완)

React - 14장 : 비동기작업 처리와 Axios 패턴을 이용한 예제 프로젝트

by VictorMeredith 2023. 2. 22.

<리액트를 다루는 기술>

React - 1장 : React 이해

React - 2장 : JSX

React - 3장 : 컴포넌트

React - 4장 : 이벤트 핸들링

React - 5장 : ref. DOM에 이름 달기 

React - 6장 : 컴포넌트 반복
React - 7장 : 컴포넌트의 LifeCycle
React - 8장 : React Hooks 총정리

React - 9장 : 컴포넌트의 스타일링

React - 10장 : 빠르게 TODO앱 실습

React - 11장 : 컴포넌트 성능 최적화

React - 12장 :  immer를 사용하여 더 쉽게 불변성 유지하기

React - 13장 : 리액트 라우터로 SPA 개발하기

React - 14장 : 뉴스 뷰어 예제프로젝트(현재)

 

오늘의 예제 실습 순서

비동기작업의 이해 ->

axios로 API호출 ->

newsapi API 키 발급받기 ->

뉴스 뷰어 UI만들기 ->

데이터연동 ->

카테고리 구현

14-1. 비동기 작업의 이해

- 비동기작업은 시간이 오래걸리는 작업을 하면서 다른 것도 할 수 있도록 미뤄두거나 동시에 처리하는 것이다.

- 동기작업은 반대로 하나씩 하면서 그 작업에 집중하고 다른 것은 일절 하지 못하는 순차처리방식이다.

- 서버 데이터가 필요해 Ajax로 서버API를 호출하여 데이터를 수신하는 경우 시간이 오래걸려 비동기로 작업을 처리하게 된다.

- 하지만 응답된 데이터를 토대로 그 이후 작업을 진행해야 할 경우에는 동기적으로 처리해야할 때가 생긴다.

- 이를 위해 콜백함수패턴(꼰대들이 쓰던거), Promise(mz세대가 쓰는거)패턴, async/await 패턴(얼리어답터가 쓰는거 사실 2017 es8에서 나오긴 했음) 이 쓰인다. 

- setTimeout도 비동기로 동작한다. 

- 비동기 함수는 webAPI로 보내지고, 큐에 쌓이게된다. 사실 큐도 여러종류이고 큐에 따라 순서가 있긴한데 마이크로 콜백큐 이런거 있음 그건 알아서 찾아보는걸로 하고 어쨌든 이벤트루프라는 넘이 콜스택을 보고있다가 비워지면 그때 큐에서 콜스택으로 보내서 실행하게 된다. 자세한 다이어그램은 알아서 찾아보면 자세히 나와있음

 

 

이렇게 생김

14-1.1 콜백함수

- 콜백함수는 함수 안의 함수를 말하며, 전통적인 비동기처리(비동기함수의 비동기적 작동 방식을 동기적(순차적)으로 처리하기 위한 방법/패턴이다.) 방법이다. 

- 구려서 이제 안쓴다

콜백지옥

14-1.2 Promise 패턴

- ES6에 도입된 기능으로, Promise 객체를 이용해 .then() 과 .catch() 로 체이닝(chaining)하여 연쇄적으로 코드를 짤 수 있도록 도와주는 비동기처리에 좋은 패턴이다.

- 생성자함수 new Promise() 를 이용해서 만들면 되고, 안에 파라미터를 넣어 성공과 실패를 판별하는 콜백을 만들어 promise객체를 반환시킨다.

Promise

- .then().then().then().then() 이렇게 이어서 체이닝으로 동기적처리를 할 수 있다.

14-1.3 async/await 패턴

- Promise를 더 쉽게 사용할 수 있도록 해주는 ES2017(ES8) 문법

- 함수의 앞부분에 async 키워드를 추가하고 해당 함수 내부에서 Promise의 앞부분에 await 키워드를 사용하면 Promise가 끝날 때까지 기다리고 결과값을 담아서 사용할 수 있다.

- try-catch 구문으로 에러를 캐치해줘야 한다. try안에서 에러가 생기면 종료하고 catch를 실행한다.

async/await 패턴

14-2 axios 로 API 호출해서 데이터 받아오기

- axios는 자바스크립트 HTTP 클라이언트이다. 라이브러리임. Promise 기반

- 리액트 하려면 최소한 axios랑 redux는 무조건 해야된다고 봐야한다. 꼰대들이 이거밖에 할줄 몰라서 그럼.

- 예제프로젝트 시작하기 : npx create-react-app news-viewer

 

App.js (axios에 Promise.then으로 처리한 예제)

import './App.css';
import axios from 'axios';
import {useState} from 'react';

function App() {

  const [data, setData] = useState(null);
  const getNewsFromServer = ()=>{
    axios.get('https://jsonplaceholder.typicode.com/todos/1')
    .then((res)=>{
      setData(res.data)
    })
  }
  return (
    <div>
      <div>
        <button onClick={getNewsFromServer}>불러오기</button>
        {data && <textarea rows={7} value={JSON.stringify(data,null,2)} readonly={true}></textarea>}
      </div>
    </div>
  );
}

export default App;

 

App.js (axios에 async/await 으로 처리한 예제)

import './App.css';
import axios from 'axios';
import {useState} from 'react';

function App() {

  const [data, setData] = useState(null);
  const getNewsFromServer = async ()=>{
    try{
      let response = await axios.get('https://jsonplaceholder.typicode.com/todos/1')
      setData(response)
    }
    catch(e){
      console.log(e+'에러당')
    }
  }
  return (
    <div>
      <div>
        <button onClick={getNewsFromServer}>불러오기</button>
        {data && <textarea rows={7} value={JSON.stringify(data,null,2)} readOnly={true}></textarea>}
      </div>
    </div>
  );
}

export default App;

14-3 newsapi API 키 발급받기

- 이번 프로젝트에서 newsapi에서 제공하는 API를 사용하여 뉴스를 불러온 후 보여준다.

- https://newsapi.org/register 가입

https://newsapi.org/v2/top-headlines?country=kr&apiKey=발급받은api키

- 이런식으로 사용한다.

 

App.js

import './App.css';
import axios from 'axios';
import {useState, useEffect} from 'react';
import NewsList from './components/NewsList';
import Categories from './components/Categories';

function App() {


  const [data, setData] = useState(null);
  const [category, setCategory] = useState('all')

  const getNewsFromServer = async ()=>{ 
      try{
        const 쿼리 = category === 'all' ? '' : `&category=${category}`
        let response = await axios.get(`https://newsapi.org/v2/top-headlines?country=kr${쿼리}&apiKey=a037fa82f06f4ca9836a89ea40f58a33`)
        setData(response.data.articles);
        console.log(response.data)
      }
      catch(e){
        console.log(e+'에러당')
      }
  }

  useEffect(()=>{ 
    getNewsFromServer();
  },[category])

  //useEffect는 Axios를 사용하여 API 엔드포인트에서 데이터를 가져오는 데 일반적으로 사용되며, 
  //useMemo는 요청 결과를 캐시하고 Axios 요청을 최적화하는 데 사용됩니다. 

  const onSelect = (e)=>{
    setCategory(e)
  }

  return (
    <div>
      <Categories onSelect={onSelect} category={category}></Categories>
      {data ? <NewsList data={data}></NewsList> : <div className='loadingUI'>로딩중이다!</div>}
    </div>
  );
}

export default App;

 

Categories.jsx

const categories = [
    {
        name: 'all',
        text: '전체보기'
    },
    {
        name: 'business',
        text: '비즈니스'
    },
    {
        name: 'entertainment',
        text: '엔터테인먼트'
    },
    {
        name: 'health',
        text: '건강'
    },
    {
        name: 'science',
        text: '과학'
    },
]

const Categories = ({onSelect, category})=>{
    return (
        <ul className="header">
            {
                categories.map((e,idx)=>{
                    return <li key={e.name} className={category === e.name ? 'active' : ''}><button onClick={()=>{onSelect(e.name)}}>{e.text}</button></li>
                })
            }
        </ul>
    )
}

export default Categories;

 

NewsList.jsx

import List from './List';

const NewsList = ({data})=>{
    return(
        <div>
            <ul className='lists'>
                {
                    data.map((e,idx)=>{
                        return <List data={e} key={e.title}></List>
                    })
                }
            </ul>
        </div>
    )
}

export default NewsList;

 

List.jsx

const List = ({data})=>{
    return(
        <li className="list">
            <a href={data.url}>
                <img src={data.urlToImage ? data.urlToImage : null} alt={data.urlToImage ? '기사이미지' : '이미지가없는데요'} />
                <h4 className="title">
                    {data.title}
                </h4>
            </a>
            <p>내용 : {data.description}</p>
            <p>published at : { data.publishedAt}</p>
        </li>
    )
}

export default List;

 

App.css

.loadingUI{background:rebeccapurple; color:#fff; width:700px; height: 500px; font-size:100px; display:flex; justify-content: center; align-items: center; position: absolute; left:50%; top: 50%; transform:translate(-50%,-50%);}
li{list-style: none;}
ul{margin:0; padding:0;}
a{text-decoration: none;}

.header{display:flex; justify-content: center; align-items: center; background:#e4e4e4; height:60px;}
.header li{margin-right:20px;}
.list{border:1px solid #e4e4e4; padding:20px;}
.list a{display:flex;}
.list img{width:100px; height:auto; margin-right:20px;}
.list .title{margin-right:20px; text-decoration: none; font-size:18px; color:#101010}
.list p{text-decoration: none; color:#000; display: flex; align-items: center;}
.active button{font-weight:700; color:blueviolet}

- 라우터도 적용해보자 안어렵다.

댓글