https://newsapi.org/s/south-korea-news-api
해당 API를 이용하여서 뉴스피드를 만들어 볼 것이다.
사용하게 될 API는 두가지 종류이다.
https://newsapi.org/v2/top-headlines?country=kr&apiKey=693f3799981248d6b896d0385ee5a7a6
여기서 ?category
는 카테고리에 따라서 바뀌는 값이다.
이제 뉴스를 불러올 수 있는 코드로 작성해보자. 먼저 뉴스 아이템을 담아올 수 있는 Component를 제작해야 하며, props에 객체를 받아와 정보를 활용하게 될것이다.
뉴스아이템을 하나씩 받아오게 될 js파일을 작성해보자.
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>
);
};
먼저 props
로 모든 article 객체를 받아옵니다. 그리고 title
, description
, url
, urlToImage
와 같이 하나씩 뽑아내서 그 값을 변수에 저장해줍니다.
useEffect(() => {
const fetchData = async () => {
setLoading(true);
try {
const response = await axios.get(
'https://newsapi.org/v2/top-headlines?country=kr&apiKey=693f3799981248d6b896d0385ee5a7a6'
);
setArticles(response.data.articles);
}
catch (e) {
console.log(e);
}
setLoading(false);
};
fetchData();
}, []);
이제 API를 통하여 받아온 아이템들을 List에 나열해야 하는데 랜더링 될 때마다 받아와야 하므로 useEffect
를 이용하여 구현할 수 있습니다.
try
- catch
구문을 통하여 API 통신 중 발생할 수 있는 에러를 캐치할 수 있도록 합니다. 그리고 awiat
를 사용하여서 통신이 비동기적으로 이루어질 수 있게끔 하였네요!
그리고 setLoading
이라는 state를 하나 만들어서 페이지를 받아오고 있는 도중이나 받아올 데이터가 없을 때의 상황을 대비할 수 있게끔 합니다.
return (
<NewListBlock>
{articles.map(article => (
<NewsItem key = {article.url} article = {article} />
))}
</NewListBlock>
);
다음으로는 map
함수를 통하여서 컴포넌트 배열을 시행합니다. map을 사용하기 전에 신경 써야 하는 것이 있는데, 그것은 바로 article
변수가 존재하는지 존재하지 않는지 검사를 해야한다는 점입니다. 이 작업을 하지 않으면 아직 데이터가 없을 때 null
에 map
이 적용되는 것이므로, 랜더링 과정에서 예기치 못한 오류가 생길 수 있기 때문입니다! 이런 부분은 꼼꼼히 살펴서 리팩토링을 진행하기!
const categories = [
{
name : 'all',
text : '전체보기'
},
{
name : 'business',
text : '비즈니스'
},
{
name : 'entertainment',
text : '엔터테인먼트'
},
{
name : 'health',
text : '건강',
},
{
name : 'science',
text : '과학'
},
{
name : 'sports',
text : '스포츠'
},
{
name : 'technology',
text : '기술'
}
];
이렇게 카테고리가 될 놈들을 리스트화 시킵니다.
그리고 카테고리 컴포넌트를 만들어줍니다.
const Categories = ({ onSelect, category }) => {
return (
<CategoriesBlock>
{categories.map(c => (
<Category
key = {c.name}
active = {category === c.name}
onClick = {() => onSelect(c.name)}>
{c.text}
</Category>
))}
</CategoriesBlock>
);
};
그리고 당연히 랜더링을 해야 하니 App.js에 적어줍니다.
그런데 우리가 선택한 카테고리가 무엇인지 props로 App.js에서 넘겨주었네? 어떻게 넘겨주었는지 확인해봅시다.
const App = () => {
const [category, setCategory] = useState ('all');
const onSelect = useCallback(category => setCategory(category), []);
return (
<>
<Categories category = {category} onSelect = {onSelect} />
<NewsList category = {category}/>
</>);
};
export default App;
state
로 어떤 것을 선택하였는지 확인할 수 있게 해주었습니다. 그리고 select 이벤트를 발생시킵니다!
그런데 카테고리별로 컴포넌트 새로 만드는 건 오바 아닐까?
그래서 어떻게 해야 하냐면 NewsList
컴포넌트 안에서 새로 받아온 props를 바탕으로 정보를 랜더링하게 하되 카테고리에 따라 API를 지정할 수 있게끔 만들어 주어야 합니다.
useEffect(() => {
const fetchData = async () => {
setLoading(true);
try {
const query = category === 'all'? '' : `&category=${category}`;
const response = await axios.get(
'https://newsapi.org/v2/top-headlines?country=kr&category=${query}&apiKey=693f3799981248d6b896d0385ee5a7a6'
);
setArticles(response.data.articles);
}
catch (e) {
console.log(e);
}
setLoading(false);
};
fetchData();
}, [category]);
이렇게 설정하면 카테고리가 all
값이라면 query
값을 공백으로 설정해주고, 그것이 아니라면 &category=카테고리
형태의 문자열을 만들어 직접 넣어줍니다. 그래서 이 qeury가 주소에 포함될 수 있게끔 합니다!
카테고리 값이 바뀔 때마다 뉴스를 새로 불러와야 하기 때문에 의존성 배열에 category를 작성해줍니다!
후후 수고했습니다!