728x90
๋ฆฌ์กํธ๋ฅผ ๋ค๋ฃจ๋ ๊ธฐ์ - 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