๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ–ฅ๏ธ/Frontend

[๋ฆฌ์•กํŠธ๋ฅผ ๋‹ค๋ฃจ๋Š” ๊ธฐ์ˆ ] ์™ธ๋ถ€ API๋ฅผ ์—ฐ๋™ํ•˜์—ฌ ๋‰ด์Šค ๋ทฐ์–ด ๋งŒ๋“ค

by HanaV 2023. 6. 28.
728x90

https://www.yes24.com/Product/Goods/62597469?pid=123487&cosemkid=go15325014723890344&gclid=CjwKCAjwkeqkBhAnEiwA5U-uM-TEoCVlWDSzJA2vBdVWRKQI_NXK8EnAMIKXCLzsxNLJmdQc_gpd8RoCtQYQAvD_BwE 

 

๋ฆฌ์•กํŠธ๋ฅผ ๋‹ค๋ฃจ๋Š” ๊ธฐ์ˆ  - YES24

๋ฆฌ์•กํŠธ, ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•˜๋Š๋ƒ๊ฐ€ ์ค‘์š”ํ•˜๋‹ค!๊ธฐ๋ณธ๊ธฐ๋ฅผ ๊ผผ๊ผผํ•˜๊ฒŒ! ํšจ๊ณผ์ ์œผ๋กœ ํ™œ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๊นŒ์ง€ ๋‹ค์–‘ํ•˜๊ฒŒ ๋ฐฐ์šฐ์ž!๋ฆฌ์•กํŠธ๋ฅผ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•œ ํ•ต์‹ฌ ๊ฐœ๋…์€ ๋ฌผ๋ก ์ด๊ณ  ์–ด๋–ค ์ƒํ™ฉ์—์„œ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•ด์•ผ ํ•˜

www.yes24.com

 

์†Œ์Šค์ฝ”๋“œ๋Š” ํ•˜๋‹จ์— ์žˆ์Šต๋‹ˆ๋‹ค

 

 

 


์ „์ฒด ์†Œ์Šค์ฝ”๋“œ

Index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { BrowserRouter } from 'react-router-dom';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

 

App.js

import { Route, Routes } from '../node_modules/react-router-dom/dist/index';
import NewsPage from './pages/NewsPage';

function App() {
  //const [category, setCategory] = useState('all');
  //const onSelect = useCallback(category => setCategory(category), []);
  return (
    <Routes>
      <Route path='/' element={<NewsPage />} />
      <Route path='/:category' element={<NewsPage />} />
    </Routes>
  )
}

export default App;

 

Categories.js

import styled from "styled-components";
import { NavLink } from "../../node_modules/react-router-dom/dist/index";

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: #d374c3;
    }

    &.active {
        font-weight: 600;
        border-bottom: 2px solid #ee93df;
        color: #ee93df;
        &:hover {
            color: #d374c3;
        }
    }

    & + & {
        margin-left: 1rem;
    }
`;

const Categories = () => {
    return (
        <CategoriesBlock>
            {categories.map(c => (
                <Category 
                    key={c.name}
                    className={({isActive}) => (isActive ? 'active' : undefined)}
                    to={c.name === 'all' ? '/' : `/${c.name}`}
                >
                    {c.text}
                </Category>
            ))}
        </CategoriesBlock>
    )
}

export default Categories;

 

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

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;

 

NewsList.js

import styled from "styled-components";
import NewsItem from "./NewsItem";
import axios from "axios";
import usePromise from "../lib/usePromise";

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 [loading, resolved, error] = usePromise(() => {
        const query = category === 'all' ? '' : `&category=${category}`;
        return axios.get(`https://newsapi.org/v2/top-headlines?country=kr${query}&apiKey=460a690efc5748c7979f8762eea705fe`);
    }, [category]);

    // loading
    if(loading) {
        return <NewsListBlock>๋Œ€๊ธฐ ์ค‘...</NewsListBlock>
    }

    // resolve๊ฐ’์ด ์—†์œผ๋ฉด
    if(!resolved) {
        return null;
    }

    // error
    if(error) {
       return <NewsListBlock>์—๋Ÿฌ ๋ฐœ์ƒ!</NewsListBlock>
    }

    // resolved ๊ฐ’์ด ์žˆ์œผ๋ฉด
    const { articles } = resolved.data;
    return (
        <NewsListBlock>
            {articles.map(article => (
                <NewsItem key={article.url} article={article} />
            ))}
        </NewsListBlock>
    )

}

export default NewsList;

 

usePromise.js

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 react-hooks/exhaustive-deps */
    }, deps);
    return [loading, resolved, error];
}

 

NewsPage.js

import { useParams } from "react-router-dom";
import Categories from "../components/Categories";
import NewsList from "../components/NewsList";

const NewsPage = () => {
    const params = useParams();
    const category = params.category || 'all';

    return(
        <>
            <Categories />
            <NewsList category={category} />
        </>
    )
}

export default NewsPage;
728x90

"); wcs_do();