[리액트를 다루는 기술] news.api.org에서 뉴스 데이터 받아와서 화면에 출력(feat. 반응형) & a태그 target과 rel

쿼카쿼카·2022년 10월 21일
0

NewsItem.js

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;
  }
`;

function NewsItem({article}) {
  const {title, description, url, urlToImage} = article;
  return (
    <NewsItemBlock>
      {urlToImage && (
        <div className="thumbnail">
          {/* target은 연결 문서 여는 방법 */}
          {/* 기본 값은 _self(현재 창에서 열기) */}
          {/* _blank는 새 창에서 열기 */}
          <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.js

import { useEffect, useState } from "react";
import styled from "styled-components";
import axios from "../../node_modules/axios/index";
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',
};

function NewsList() {
  const [articles, setArticles] = useState(null);
  const [loading, setLoading] = useState(false);
  
  useEffect(() => {
    // async를 사용하는 함수 따로 선언
    (async function fetchData() {
      setLoading(true);
      try {
        const res = await axios.get(`https://newsapi.org/v2/top-headlines?country=kr&apiKey=${process.env.REACT_APP_API_KEY}`);
        setArticles(res.data.articles);
      }
      catch(e) {
        console.log(e);
      }
      setLoading(false);
    })();
  }, [])

  // 대기 중일 때
  if(loading) {
    return <NewsListBlock>대기 중...</NewsListBlock>
  }

  // 아직 articles 값이 설정되 않았을 때
  if(!articles) {
    return null;
  }

  // articles 값이 유효할 때
  return (
    <NewsListBlock>
      {articles.map(article => (
        <NewsItem key={article.url} article={article} />
      ))}
    </NewsListBlock>
  )
}

export default NewsList;
  • useEffect 내부에서 async 즉시 실행함수를 작동
  • Loading 값에 따라 대기 중... 뜰 수 있도록 return
  • articles 값이 없으면 빈 화면 출력

a 태그의 target과 rel

target: 링크 클릭 시 창을 어떻게 띄울지 결정

  • _self(기본값): 현재 창에서 이동
  • _blank: 새 창에서 이동

rel: 현재 창과 링크의 관계

object-fit

img를 박스에 맞게 사이즈 조절

https://developer.mozilla.org/ko/docs/Web/CSS/object-fit

white-space

글자 공백 조정 방법

https://developer.mozilla.org/ko/docs/Web/CSS/white-space

.env

  • .env를 수정하면 서버를 재실행해야 작동함
  • React에서는 env 작성시 앞에 'REACTAPP' 붙여주기

결과화면

profile
쿼카에요

0개의 댓글