- 뉴스 API (newsapi.org)의 데이터를 불러와서 화면에 렌더링.
 - 카테고리별로 분류하여 조회 가능.
 - 클릭시 해당 기사로 넘어감.
 
reactjavaScript├── public
└── src
    ├── lib
    │     └── usePromise
    ├── components
    │   ├── pages
    │   │     └── NewsPage
    │   ├── Categories
    │   ├── NewsItem
    │   └── NewsList
    ├── App
    └── index
create-react-app"dependencies": {
    "@testing-library/jest-dom": "^5.14.1",
    "@testing-library/react": "^13.0.0",
    "@testing-library/user-event": "^13.2.1",
    "axios": "^0.27.2",
    "react": "^18.1.0",
    "react-dom": "^18.1.0",
    "react-router-dom": "^6.3.0",
    "react-scripts": "5.0.1",
    "styled-components": "^5.3.5",
    "web-vitals": "^2.1.0"
  },
$ git clone https://github.com/thisisyjin/news-viewer.git
$ npm install
$ npm start || yarn start
내 개발 블로그 에 따로 포스팅해두었다.
- HTTP 요청을 Promise 기반으로 처리.
 
const App = () => {
  const [data, setData] = useState(null);
  const onClickBtn = () => {
    axios.get('https://jsonplaceholder.typicode.com/todos/1').then(response => {
      setData(response.data);
    });
  };
 ...
axios.get(url)로 데이터를 가져온다.response.data를 받아온다.const App = () => {
  const [data, setData] = useState(null);
  const onClickBtn = async () => {
    try {
      const response = await axios.get(
        'https://jsonplaceholder.typicode.com/todos/1'
      );
      setData(response.data);
    } catch (e) {
      console.log(e);
    }
  };
async () => 와 같이 나타냄.response라는 변수에 저장한 후,state)를 response.data로 변경함.
await은 Promise를 기다리기 위해 사용된다.- await은 async function 내부에서만 사용할 수 있다.
 
try {요청} catch (e) {에러시} 로 구성된다.#root에 render()articles와 loading이 있음.async함수를 이용하여 API를 불러옴.response = await axios.get(url)articles state에 데이터를 저장함.
if(loading) 이면 '대기중'을 렌더링하고,
if(!articles) 면 아래 return에서 map함수에서 오류가 나지 않도록 필터링 해준다.
articles(state)는 배열이므로, 배열 메서드인 map을 이용하여 각각의 요소들을
NewsItem 컴포넌트로 렌더링해줌.
NewsItem 컴포넌트에 article이라는 Props를 넘겨줌. (각각의 요소 자신)
props를 전달받음.
NavLink는 react-router의 내장 컴포넌트 중 하나로,
현재 라우터와 일치하면 스타일 or 클래스를 부여하는 컴포넌트이다.
참고 - NavLink의
isActive// 1️⃣ 클래스 적용시 className={isActive => "nav-link" + (!isActive ? " unselected" : "") } // 또는 activeClassName="selected" // 2️⃣ 스타일 적용시 style={isActive => ({ color: isActive ? "green" : "blue" }) }
Category 컴포넌트가 active 라는 클래스를 가지면,
&.active {
        font-weight: 600;
        border-bottom: 2px solid #22b8cf;
        color: #22b8cf;
        &:hover {
            color: #3bc9db;
        }
    }
위처럼 클래스 선택자에 의해 스타일이 적용된다.
 to={c.name === 'all' ? '/' : `/${c.name}`
또한, to 프로퍼티에는 c.name이 'all'이라면 경로는 /가 되게 하고,
아니라면 /c.name이 되도록 설정한다.
useParams 사용.const params = useParams();
const category = params.category || 'all';
<></>const query = category === 'all' ? '' : `&category=${category}`;
NewsPage로부터 props로 받은 category로 query를 결정함.
category가 'all'이라면 ''로, 아니라면 category로 결정.
-> 참고로, category는 useParams로 받아온 params의 값이다.
(params=''인 경우에는 category='all'로 기본값.)
await axios.get(url)에서 url 부분을 수정해준다.
-> ES6 태그드 템플릿 리터럴로 작성함.
`https://newsapi.org/v2/top-headlines?country=kr${query}&apiKey=(인증키)`
country=kr 뒤부분에 바로 query를 넣어줌.
-> 이 url은 동적으로 바뀌는 url이 됨.
1) params 값에 따라서 -> 2) category 값이 설정 -> props로 넘겨줌
-> 3) query값이 설정 -> 4) axios.get을 요청할 url이 동적 설정.
import { useState, useEffect } from 'react';
export default function usePromise(promiseCreator, deps) {
    const [loading, setLoading] = useState(false);
    const [resolved, setResolved] = useState(null);
    const [error, setError] = useState(null);
    useEffect(() => {
        const process = async () => {
            setLoading(true);
            try {
                const resolved = await promiseCreator();
                setResolved(resolved);
            } catch (e) {
                setError(e);
            }
            setLoading(false);
        };
        process();
    // eslint-disable-next-line react-hooks/exhaustive-deps
    }, deps);
    return [loading, resolved, error];
}
state는 loading, resolved, error 총 세가지를 가짐.
-> usePromise 함수가 return 하는 값은 모두 state들임.
process 라는 함수를 useEffect 내부에서 정의함.
-> async 함수이므로 선언한 후 사용해야 함.
try(fetch부분) catch(에러시) 구조로 이루어지며,
try-catch문 이전에는 setLoading(true)
try-catch문 이후에는 setLoading(false)를 해줌.
resolved = await promiseCreator()을 해줌.
여기서 promiseCreator는 usePromise의 첫번째 인자로 받는 함수이다.
-> axios.get(url)을 하면 된다.
const [loading, response, error] = usePromise(() => {
        const query = category === 'all' ? '' : `${category}`;
        return axios.get(
            `https://newsapi.org/v2/top-headlines?country=kr${query}&apiKey=(인증키)`
        );
    }, [category]);
resolved는 state가 아닌 변수이다.