티스토리 뷰

728x90

 

2020/11/16 - [React] - [React] 08. React SPA와 Router 사용하기 01

2020/11/24 - [React] - [React] 09. Router 사용하기 02

 

 

이전에 공부했던 Router를 사용하여 뉴스 뷰어를 만들어보려고 합니다.

메뉴바에서 선택된 카테고리를 url 파라미터로 전송해 해당 카테고리별로 뉴스 데이터를 보여줄 것입니다.

결과 화면

 

 

 

 src/components/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;

 

 

 

 

 src/components/NewsList.js 

 

NewsList컴포넌트에서 API를 사용하여 뉴스 데이트를 불러올 것이기 때문에  axios 라이브러리를 설치해주어야 합니다.

newsapi.org/register를 통해 뉴스 API를 발급받을 수 있습니다.

++ axios

node.js와 브라우저를 위한 http통신 javascript 라이브러리로 axios를 사용하면 GET, PUT, POST, DELETE 등의 메서드로 API 요청을 할 수 있습니다. 아래 명령어를 사용하여 설치 할 수 있습니다.

npm install axios
// API를 요청하고 뉴스 데이터를 불러옴
// 각 뉴스의 정보를 보여주는 컴포넌트

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

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 NewsList = ({ category }) => {
  const [articles, setArticles] = useState(null);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
  
    const fetchData = async () => {
      setLoading(true);
      try {
        console.log("category : ", category);
        const query = category === "all" ? "" : `&category=${category}`;
        const response = await axios.get(
          `https://newsapi.org/v2/top-headlines?country=kr${query}&apiKey=a0f944e0583e4ab392808ecea22709f4`
        );
        setArticles(response.data.articles);
      } catch (e) {
        console.log(e);
      }
      setLoading(false);
    };
    fetchData().then((r) => console.log("ok"));
  }, [category]);

  // 대기 중 상태
  if (loading) {
    return <NewsListBlock>대기 중…</NewsListBlock>;
  }
  // 값이 설정되지 않은 상태
  if (!articles) {
    return null;
  }

  // articles 값이 있다면
  return (
    <NewsListBlock>
      {articles.map((article) => (
        <NewsItem key={article.url} article={article} />
      ))}
    </NewsListBlock>
  );
};

export default NewsList;

 

 

 

 

 src/components/Categories.js 

++ NavLink

NavLink란, 현재 경로와 Link에서 사용하는 경로가 일치하는 경우 특정 스타일 혹은 CSS 클래스를 적용할 수 있는 컴포넌트입니다.

NavLink에서 링크가 활성화되었을 때 CSS 클래스를 적용할 때 activeClassName 값을 props로 넣어 사용합니다!

import React from "react";
import styled, { css } 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}
          activeClassName="active"
          exact={c.name === 'all'}
          to = {c.name === 'all' ? '/' : c.name}
        >

        {c.text}
        </Category>
      ))}
    </CategoriesBlock>
  );
};

export default Categories;

 

 

 

 

 src/page/NewsPage.js 

 

NewsPage 컴포넌트에서는 match객체를 사용해 파라미터 값을 확인합니다. 값이 존재한다면 그 값을 category 변수에 넣어주고 없다면 'all'이라는 값을 넣어줍니다. 그리고 <NewsList> 컴포넌트에 props로 category를 전달해줍니다.

import React from 'react';
import Categories from '../Componentsend/Categories';
import NewsList from '../Componentsend/NewsList';


const NewsPage = ({ match }) => {

    const category = match.params.category || 'all';
    return (
        <>
            <Categories />
            <NewsList category={category} />
        </>
    );
};


export default NewsPage;

 

 

 

 src/App.js 

 

그리고 마지막으로 App 컴포넌트에 <Route>를 추가해줍니다.

path에는 / :category?를 설정해주었는데요! url에 파라미터를 사용할 때에는  를 변수 앞에 붙여주고 사용합니다.

변수 뒤에 붙은 는 파리미터가 생략되어도 매칭이 가능하다는 의미입니다.

import { Route } from "react-router-dom";
import NewsPage from "./Page/NewsPage";
const App = () => {
  return <Route path="/:category?" component={NewsPage} />;
};

export default App;

 

 

코드를 실행해보면!

http://localhost:3000/science

728x90
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크