[리액트를 다루는 기술] 14장 외부 API를 연동하여 뉴스 뷰어 만들기
-
https://newsapi.org/ 에서 news API 가져오기
-
9장의 style-componenets 활용하여 프로젝트 스타일링하기
-
실습진행
비동기작업의 이헤 - axios로 API호출해서 데이터 받기 - newssapi API 키 발급받기 - 뉴스 뷰어 ui 만들기 - 데이터 연동하기 - 카테고리 기능 구현 - 리액트 라우터적용
14.1 비동기 작업의 이해
- 서버 API 호출하면 송수신 과정에 시간이 오래걸림 -> 비동기처리로 진행
- 비동기 작업시 주로 -> 콜백함수(인자로 전달되는 함수), setTimeout() 함수 사용
14.1.1 콜백 함수
function increase(number, callback) {
setTimeout(() => {
const result = number + 10;
if (callback) {
callback(result);
}
}, 1000);
}
increase(0, (result) => {
console.log(result);
});
- 하지만 콜백지옥에 빠질수 있음(중첩 또 중첩..) -> 가독성이 나쁨으로 지양하셈
14.1.2 Promise
- 콜백지옥같은 코드형성 방지용으로 ES6에 도입됨
function increase(number) {
const promise = new Promise((resolve, reject) => {
// resolve는 성공, reject는 실패
setTimeout(() => {
const result = number + 10;
if (result > 50) {
// 50보다 높으면 에러 발생시키기
const e = new Error("NumberTooBig");
return reject(e);
}
resolve(result); // number 값에 +10 후 성공 처리
}, 1000);
});
return promise;
}
increase(0)
.then((number) => {
// Promise에서 resolve된 값은 .then을 통해 받아 올 수 있음
console.log(number);
return increase(number); // Promise를 리턴하면
})
.then((number) => {
// 또 .then으로 처리 가능
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) => {
// 도중에 에러가 발생한다면 .catch를 통해 알 수 있음
console.log(e);
});
function increase(number) {
const promise = new Promise((resolve, reject) => {
// resolve는 성공, reject는 실패
setTimeout(() => {
const result = number + 10;
if (result > 50) {
// 50보다 높으면 에러 발생시키기
const e = new Error("NumberTooBig");
return reject(e);
}
resolve(result); // number 값에 +10 후 성공 처리
}, 1000);
});
return promise;
}
increase(0)
.then((number) => {
// Promise에서 resolve된 값은 .then을 통해 받아 올 수 있음
console.log(number);
return increase(number); // Promise를 리턴하면
})
.then((number) => {
// 또 .then으로 처리 가능
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) => {
// 도중에 에러가 발생한다면 .catch를 통해 알 수 있음
console.log(e);
});
- 중첩하지 않고 해결할수 있음
14.1.3 async/await
-
async/await는 Promise를 더 쉽게 사용할 수 있도록 해 주는 ES2017(ES8) 문법
-
문법을 사용을 위해..
- 함수의 앞부분에 async 키워드를 추가
- 해당 함수 내부 Promise의 앞부분에 await 키워드를 사용
function increase(number) {
const promise = new Promise((resolve, reject) => {
// resolve는 성공, reject는 실패
setTimeout(() => {
const result = number + 10;
if (result > 50) {
// 50보다 높으면 에러 발생시키기
const e = new Error("NumberTooBig");
return reject(e);
}
resolve(result); // number 값에 +10 후 성공 처리
}, 1000);
});
return promise;
}
async function runTasks() {
try {
// try/catch 구문을 사용하여 에러를 처리합니다.
let result = await increment(0);
console.log(result);
result = await increment(result);
console.log(result);
result = await increment(result);
console.log(result);
result = await increment(result);
console.log(result);
result = await increment(result);
console.log(result);
result = await increment(result);
console.log(result);
} catch (e) {
console.log(e);
}
}
14.2 axios로 API 호출해서 데이터 받아 오기
- axios 란?
현재 가장 많이 사용되고 있는 자바스크립트 HTTP 클라이언트
라이브러리의 특징은 HTTP 요청을 Promise 기반으로 처리한다는 점
다음과 같이 설치
npm create react-app news-viewer
cd news-viewer
npm add axios
- .prettierrc (자동정렬설정)
{
"singleQuote": true,
"semi": true,
"useTabs": false,
"tabWidth": 2,
"trailingComma": "all",
"printWidth": 80
}
- jsconfig.json (파일자동불러오기)
{
"compilerOptions": {
"target": "es6"
}
}
- App.js 다음과 같이 작성
import React, { useState } from 'react';
import axios from 'axios';
const App = () => {
const [data, setData] = useState(null);
const onClick = () => {
axios
.get('https://jsonplaceholder.typicode.com/todos/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;
-
onClick 함수에서 axios.get 함수를 사용 -> 전달주소에 GET 요청을 해줌
-
.then을 통해 비동기적으로 확인 가능
-
async 적용하기
import React, { 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/todos/1',
);
setData(response.data);
} catch (e) {
console.log(e);
} // async 를 적용
};
return (
<div>
<div>
<button onClick={onClick}>불러오기</button>
</div>
{data && (
<textarea
rows={7}
value={JSON.stringify(data, null, 2)}
readOnly={true}
/>
)}
</div>
);
};
export default App;
14.3 newsapi API 키 발급받기
-
API키 발급받기 위해선 https://newsapi.org/register 가입
-
사용할 두가지 API
- 전체 뉴스 불러오기
GET https://newsapi.org/v2/top-headlines?country=kr&apiKey=API 키를 입력
- 특정 카테고리 뉴스 불러오기
GET https://newsapi.org/v2/top-headlines?country=kr&category=business&apiKey=API 키를 입력
- business, entertainment, health, science, sports, technology 중에 골라서 사용
- 전체 뉴스 불러오기
-
apiKey 값에는 앞에서 각자 발급받았던 API 키를 입력
-
기존 App.js 수정하기 -> 현재 불러온 API로 대체
14.4 뉴스 뷰어 UI 만들기
- styled components 설치
npm add styled-components
- src 디렉토리에 두개의 파일 생성
- NewsItem.js : 뉴스정보를 보여주는 컴포넌트
- NewsList.js : API 요청 -> 컴포넌트 배열로 변환 및 렌더링
14.4.1 NewsItem 만들기
- 뉴스데이터는 -> JSON 객체
- 다음과 같이 만들어보자 !
• title: 제목
• description: 내용
• url: 링크
• urlToImage: 뉴스 이미지
- NewsItem.js 작성
import React from 'react';
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;
14.4.2 NewsList 만들기
- NewsList.js -> sampleArticle (가짜데이터)를 넣음
import React from 'react';
import styled from 'styled-components';
import NewsItem from './NewsItem';
const NewsListBlock = 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 (
<NewsListBlock>
<NewsItem article={sampleArticle} />
<NewsItem article={sampleArticle} />
<NewsItem article={sampleArticle} />
<NewsItem article={sampleArticle} />
<NewsItem article={sampleArticle} />
<NewsItem article={sampleArticle} />
</NewsListBlock>
);
};
export default NewsList;
- App.js 수정
import React from 'react';
import NewsList from './components/NewsList';
const App = () => {
return <NewsList />;
};
export default App;
- 오류발생(추후 수정예정)
14.5 데이터 연동하기
- API 호출하기 -> useEffect 사용 -> useEffect 등록함수에 async를 붙이면 안됨(뒷정리함수라)
- map 함수 사용하여 컴포넌트 배열로 변환전 !article 을 조회하여 null이 아닌지 검사 -> map함수가 없기때문에 렌더링 오류걸릴수 있음
14.6 카테고리 기능 구현하기
- 아래와 같은 카테고리 구현
• business(비즈니스)
• entertainment(연예)
• health(건강)
• science(과학)
• sports(스포츠)
• technology(기술)
14.6.1 카테고리 선택 UI 만들기
- Categories.js 생성
import React from 'react';
import styled from 'styled-components';
import { NavLink } from 'react-router-dom';
const categories = [
{
name: 'all',
text: '전체보기',
},
{
name: 'business',
text: '비즈니스',
},
{
name: 'entertainment',
text: '엔터테인먼트',
},
{
name: 'health',
text: '건강',
},
{
name: 'science',
text: '과학',
},
{
name: 'sports',
text: '스포츠',
},
{
name: 'technology',
text: '기술',
},
];
const CategoriesBlock = styled.div`
display: flex;
padding: 1rem;
width: 768px;
margin: 0 auto;
@media screen and (max-width: 768px) {
width: 100%;
overflow-x: auto;
}
`;
const Category = styled(NavLink)`
font-size: 1.125rem;
cursor: pointer;
white-space: pre;
text-decoration: none;
color: inherit;
padding-bottom: 0.25rem;
&:hover {
color: #495057;
}
&.active {
font-weight: 600;
border-bottom: 2px solid #22b8cf;
color: #22b8cf;
&:hover {
color: #3bc9db;
}
}
& + & {
margin-left: 1rem;
}
`;
const Categories = () => {
return (
<CategoriesBlock>
{categories.map((c) => (
<Category
key={c.name}
className={({ isActive }) => (isActive ? 'active' : undefined)}
to={c.name === 'all' ? '/' : `/${c.name}`}
>
{c.text}
</Category>
))}
</CategoriesBlock>
);
};
export default Categories;
- App.js 렌더링후
- category 상태를 useState로 관리
14.7 리액트 라우터 적용하기
- 기존 카테고리 값을 라우터의 URL 파라미터를 사용하여 관리
14.7.1 리액트 라우터의 설치 및 적용
- 리액트 라우터 설치 및 적용
npm add react-router-dom
14.9 정리
- 사용해야 할 API의 종류가 많아지면 요청을 위한 상태 관리를 하는 것이 번거로워질 수 있음
- 리덕스와 리덕스 미들웨어를 배우면 좀 더 쉽게 요청에 대한 상태를 관리할 수 있음
Author And Source
이 문제에 관하여([리액트를 다루는 기술] 14장 외부 API를 연동하여 뉴스 뷰어 만들기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@uiop01900/리액트를-다루는-기술-14장-외부-API를-연동하여-뉴스-뷰어-만들기저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)