[뉴스피드 개발일지 #2] 우리나라 뉴스 불러오기

김유진·2022년 11월 9일
0

React

목록 보기
47/64
post-custom-banner

https://newsapi.org/s/south-korea-news-api
해당 API를 이용하여서 뉴스피드를 만들어 볼 것이다.
사용하게 될 API는 두가지 종류이다.

인기 많은 탑 뉴스 불러오기

https://newsapi.org/v2/top-headlines?country=kr&apiKey=693f3799981248d6b896d0385ee5a7a6

관련 카테고리의 뉴스 불러오기

https://newsapi.org/v2/top-headlines?country=kr&category=technology&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변수가 존재하는지 존재하지 않는지 검사를 해야한다는 점입니다. 이 작업을 하지 않으면 아직 데이터가 없을 때 nullmap이 적용되는 것이므로, 랜더링 과정에서 예기치 못한 오류가 생길 수 있기 때문입니다! 이런 부분은 꼼꼼히 살펴서 리팩토링을 진행하기!

카테고리 구현하기

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를 작성해줍니다!

후후 수고했습니다!

post-custom-banner

0개의 댓글