[리액트를 다루는 기술] 9. 외부 API를 연동하여 뉴스뷰어 만들기

2022. 7. 5. 23:25·BOOK

웹어플리케션을 만들다보면 비동기작접으로 처리해야할때가 필요하다

한가지일만하는것이아닌 여러 작업을 실행하는, 파일 다운로드 기능이라 보면된다

 

이렇게 서버 API를 호출할때 외에도 작업을 비동기적으로 처리할때가있는데

바로 setTimeout 함수를 사용하여 특정작업을 예약할때이다.

 

function printMe() {
	console.log("hello world!");
}
setTimeout(printMe, 3000);
console.log("대기중 ...");

비동기작업을 할때 가장 흔히사용하는 방법은 콜백함수를 사용하는 것이다.

 

 

1. 콜백함수

 

function increase(number, callback) {
  setTimeout(() => {
    const result = number + 10;
    if (callback) {
      callback(result);
    }
  }, 1000);
}

increase(0, (result) => {
  console.log(result);
});

 

2. Promise

promise는 콜백 지옥같은 코드가 생기지않게 도입된기능이다.

여러 작업을 연달아 처리한다고 해서 함수를 여러번감싸는게아니라 then을

사용해서 그다음작업을 설정한다.

import { Component } from 'react';

class Counter extends Component {
  state = {
    number: 0,
    fixedNumber: 0,
  };
  render() {
    const { number, fixedNumber } = this.state; // state를 조회할때는 this.state로 조회한다
    return (
      <div>
        <h1>{number}</h1>
        <h2>바뀌지않는값 {fixedNumber}</h2>
        <button
          //onClick을 통해 버튼이 클릭되었을 때 호출할 함수를 지정합니다.
          onClick={() => {
            this.setState((prevState) => {
              return { number: prevState.number + 1 };
            }); //변하는값만 설정
            this.setState((prevState) => ({ number: prevState.number + 1 }));
          }}
        >
          +1
        </button>
      </div>
    );
  }
}

export default Counter;

function increase(number) {
  const promise = new Promise((resolve, reject) => {
    //resoleve는 성공,  reject는 실패
    setTimeout(() => {
      const result = number + 10;
      if (result > 50) {
        const e = new Error('numbertobig');
        return reject(e);
      }
      resolve(result);
    }, 1000);
  });
  return promise;
}

increase(0)
  .then((number) => {
    console.log(number);
    return increase(number);
  })
  .then((number) => {
    console.log(number);
    return increase(number);
  })
  .then((number) => {
    console.log(number);
    return increase(number);
  })
  .then((number) => {
    console.log(number);
    return increase(number);
  })
  .then((number) => {
    console.log(number);
    return increase(number);
  })
  .catch((e) => {
    //도중에 에러가 발생한다면
    console.log(e);
  });

 

 

3. async / await

async/await는 Promise를 더욱 쉽게 사용할수있도록 해주는  ES8문법이다.

이문법을 사용하려면 함수의 앞부분에 async 키워드를 추가하고 함수 내부에서 Promise의 앞부분에 await 키워드를 사용한다.

 

이렇게하면 Promise가 끝날때까지 기다리고, 결과값을 특정 변수에 담을수 있다.

function increase(number) {
  const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
      const result = number + 10;
      if (result > 50) {
        const e = new Error('numberToobig');
        return reject(e);
      }
      resolve(result);
    }, 1000);
  });
  return promise;
}

async function runTasks() {
  try {
    let result = await increase(0);
    console.log(result);
    result = await increase(0);
    console.log(result);
    result = await increase(result);
    console.log(result);
    result = await increase(result);
    console.log(result);
    result = await increase(result);
    console.log(result);
    result = await increase(result);
    console.log(result);
  } catch (e) {
    console.log(e);
  }
}

 

4.  axios로 API호출해서 데이터 받아오기

axios는 http요청을 promise 기반으로 처리한다

import { useState } from 'react';
import axios from 'axios';

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

그렇다면 만약 async를 적용하면 어떨까??

import { useState } from 'react';
import axios from 'axios';

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

이렇게 async () => { } 와 같은 형식으로 적용한다.

 

5. newsAPI 만들기

그렇다면 큰틀로 newsAPI를 만들어보자 

newsapi에서 API키를 발급받아야한다.

 

https://newsapi.org/account 

 

Login - News API

 

newsapi.org

에서 회원가입후 api키를 받는다.

 

 

 

https://newsapi.org/s/south-korea-news-api

 

South Korea News API - Live top headlines from South Korea

Get live top and breaking news headlines from South Korea with our JSON API. Live example This example demonstrates the HTTP request to make, and the JSON response you will receive, when you use the News API to get live headlines from South Korea. Top head

newsapi.org

에 들어가서 두가지  API 주소를 가져올것이다.

1. 전체뉴스불러오기

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

 

 

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

 

 

 

그후 주소만 기존소스에서 바꿔준다.

import { useState } from 'react';
import axios from 'axios';

const App = () => {
  const [data, setData] = useState(null);
  const onClick = async () => {
    try {
      const response = await axios.get(
        'https://newsapi.org/v2/top-headlines?country=kr&apiKey=발급받은 api키를 입력해라',
      );
      setData(response.data);
    } catch (e) {
      console.log(e);
    }
  };
  return (
    <div>
      <div>
        <button onClick={onClick}>불러오기</button>
      </div>
      {data && (
        <textarea
          rows={7}
          value={JSON.stringify(data, null, 2)}
          readOnly={true}
        />
      )}
    </div>
  );
};
export default App;

 

1. 뉴스 뷰어UI 만들기

 

 yarn add styled-components  

 

명령어로  styled component를 설치한다.

 

우선 NewsItem 파일로 

 

json 데이타의 키값인

title, description, url, urlToImage

를 보여준다. 

 

import styled from 'styled-components';

const NewsItemBlock = styled.div`
  display: flex;
  .thumbnail {
    margin-right: 1rem;
    img {
      display: block;
      width: 160px;
      height: 100px;
      object-fit: cover;
    }
  }
  .contents {
    h2 {
      margin: 0;
      a {
        color: black;
      }
    }
    p {
      margin: 0;
      line-height: 1.5;
      margin-top: 0.5rem;
      white-space: normal;
    }
  }
  & + & {
    margin-top: 3rem;
  }
`;

const NewsItem = ({ article }) => {
  const { title, description, url, urlToImage } = article;
  return (
    <NewsItemBlock>
      {urlToImage && (
        <div className="thumbnail">
          <a href={url} target="_blank" rel="noopener noreferrer">
            <img src={urlToImage} alt="thumbnail" />
          </a>
        </div>
      )}
      <div className="contents">
        <h2>
          <a href={url} target="_blank" rel="noopener noreferrer">
            {title}
          </a>
        </h2>
        <p>{description}</p>
      </div>
    </NewsItemBlock>
  );
};

export default NewsItem;

 

 

NewsList 파일로 보여준다

json 데이타를 자식컴포넌트에 보여줄값들을 임의로 만들어준다. (임시)

import React from 'react';
import NewItem from './NewsItem';
import styled from 'styled-components';

const NewsListBock = styled.div`
  box-sizing: border-box;
  padding-bottom: 3rem;
  width: 768px;
  margin: 0 auto;
  margin-top: 2rem;
  @media screen and (max-width: 768px) {
    width: 100%;
    padding-left: 1rem;
    padding-right: 1rem;
  }
`;

const sampleArticle = {
  title: '제목',
  description: '내용',
  url: 'https://google.com',
  urlToImage: 'Https://via.placeholder.com/160',
};

const NewsList = () => {
  return (
    <NewsListBock>
      <NewItem article={sampleArticle}></NewItem>
      <NewItem article={sampleArticle}></NewItem>
      <NewItem article={sampleArticle}></NewItem>
      <NewItem article={sampleArticle}></NewItem>
      <NewItem article={sampleArticle}></NewItem>
    </NewsListBock>
  );
};

export default NewsList;

 

 

그후 App 폴더에서 NewsList 컴포넌트를 렌더링해준다.

import { useState } from 'react';
import axios from 'axios';
import NewsList from './Components/NewsList';

const App = () => {
  return (
    <div>
      <NewsList></NewsList>
    </div>
  );
};
export default App;

 

 

그러면 인위적인  List가 보일것이다.

이인위적인 데이타를 이제 API를 사용해서 데이터를 연동해보겠다

 

NewsList.js 소스에 axios 를 더해준다

 

import React, { useEffect, useState } from 'react';
import NewItem from './NewsItem';
import styled from 'styled-components';
import axios from 'axios';

const NewsListBock = styled.div`
  box-sizing: border-box;
  padding-bottom: 3rem;
  width: 768px;
  margin: 0 auto;
  margin-top: 2rem;
  @media screen and (max-width: 768px) {
    width: 100%;
    padding-left: 1rem;
    padding-right: 1rem;a
  }
`;

const sampleArticle = {
  title: '제목',
  description: '내용',
  url: 'https://google.com',
  urlToImage: 'Https://via.placeholder.com/160',
};

const NewsList = () => {
  const [articles, setArticles] = useState(null);
  const [loading, setLoding] = useState(false);
  useEffect(() => {
    //async를 사용하는 함수 따로 선언
    const fetchData = async () => {
      try {
        const response = await axios.get(
          'https://newsapi.org/v2/top-headlines?country=kr&apiKey=발급받은 api키',
        );
        setArticles(response.data.articles);
      } catch (e) {
        console.log(e);
      }
      setLoding(false);
    };
    fetchData();
  }, []);

  if (loading) {
    return <NewsListBock>대기중 ...</NewsListBock>;
  }
  if (!articles) {
    return null;
  }

  return (
    <NewsListBock>
      {articles.map((article) => (
        <NewItem key={article.url} article={article} />
      ))}
    </NewsListBock>
  );
};

export default NewsList;

여기서 중요한점은 map 함수를 조회하기전에  !articles를 조회해서 해당값이 null인지 

검사해야한다. 이작업을 않한다면 데이터가없을때 null에 map 함수가 없기때문에 렌더링 과정에서 오류가 발생한다.

 

다음엔 카테고리 기능구현을...진행

'BOOK' 카테고리의 다른 글

[리액트를 다루는 기술] 8. 컴포넌트 스타일링  (0) 2022.07.03
[리액트를 다루는 기술] 7. 컴포넌트의 반복  (0) 2022.07.03
[리액트를 다루는 기술] 6. ref: DOM에 이름달기  (0) 2022.07.03
[리액트를 다루는 기술] 5. 이벤트 핸들링  (0) 2022.07.02
[리액트를 다루는 기술] 4. 컴포넌트  (0) 2022.06.27
'BOOK' 카테고리의 다른 글
  • [리액트를 다루는 기술] 8. 컴포넌트 스타일링
  • [리액트를 다루는 기술] 7. 컴포넌트의 반복
  • [리액트를 다루는 기술] 6. ref: DOM에 이름달기
  • [리액트를 다루는 기술] 5. 이벤트 핸들링
윤랩용
윤랩용
10배의 법칙으로 행동
  • 윤랩용
    yunrap 개발블로그
    윤랩용
  • 전체
    오늘
    어제
    • 분류 전체보기 (82) N
      • 알고리즘 (12) N
        • 알고리즘유형 (5) N
        • 코딩테스트 (2)
      • 네트워크 (2) N
      • 언어 (13)
        • HTML (0)
        • CSS (0)
        • Javascript (9)
        • Java (1)
        • 용어 (3)
      • Backend (1)
        • Spring (1)
      • FrontEnd (10) N
        • React (8) N
        • Next.js (0)
        • LAB(실험실) (1)
      • 자기개발 (2)
        • motivation (1)
      • BOOK (35)
        • 모던자바스크립트 DeepDive (22)
        • 인사이드 자바스크립트 (2)
      • 요즘 FE TREND 뭘까? (0)
  • 공지사항

    • 블로그를 새롭게 활성화시키겠습니다.
  • 최근 글

  • 인기 글

  • 태그

    create react app
    최단거리구하기
    시스템테마
    프로미스체이닝
    콜백패턴
    원시타입
    var키워드
    tailwind 다크모드
    함수프로그래밍
    tcp 프로토콜
    javascript bfs
    물너비구현하기
    접근자프로퍼티
    dark:
    캡슐화
    __proto__
    클로저 js
    즉시실행함수
    리액트 fiber
    클로저
  • 최근 댓글

  • hELLO· Designed By정상우.v4.10.0
윤랩용
[리액트를 다루는 기술] 9. 외부 API를 연동하여 뉴스뷰어 만들기
상단으로

티스토리툴바