동기적 처리
비동기적 처리
function increase (number, callback){
setTimeout(()=>{
const result=number+10;
if (callback){
callback(result)
}
},1000)}
increase(0, result=>{
console.log(result);
});
console.log("시작!!")
setTimeout이 사용되는 시점에서 중지 X
1. 우선 코드 전체가 호출됨
2. 1초 뒤에 주어진 인자 값에 10 더해 반환하는 함수가 실행됨
3. 더해진 값이 result에 담기자마자 출력됨
function increase (number){
const promise = new Promise((resolve, reject)=>{
//resolve : 성공, reject : 실패
setTimeout(()=>{
const result = number +10;
if (result>50){
//50보다 높으면 에러 발생시키기
const e = new Error("Number Too Big");
return reject(e)
}
resolve(result); // number 값에 10 더하고 성공 처리
},100)
});
return promise;
}
async function runTask(){
try{
let result = await increase(0);
console.log(result);
let result = await increase(result);
console.log(result);
let result = await increase(result);
console.log(result);
let result = await increase(result);
console.log(result);
}
catch(e){
console.log(e);
}
}
axios : 자바스크립트 HTTP 클라이언트로 HTTP 요청을 Promise 기반으로 처리
npm create-react-app {프로젝트명}
cd {프로젝트명}
yarn add axios
const App=()=>{
const [data,setData]=useState(null);
const onClick =()=>{
axios.get("https://jsonplaceholder.typicode.com/todos/")
.then(response=>{
//console.log(response.data)
setData(response.data);
});
};
return(
<div>
<div>
<button onClick={onClick}>불러오기</button>
</div>
{data && <textarea rows={7} value={JSON.stringify(data,null,2)}
readOnly={true}/>}
</div>
);
};
onClick
함수 실행onClick
내에서 axios.get
사용해 파라미터로 전달된 주소에 GET 요청.then
으로 결과 비동적으로 확인async () => {} 형식 사용
const [data,setData]=useState(null);
const onClick =async()=>{
try{
const response=await axios.get("https://jsonplaceholder.typicode.com/todos/");
setData(response.data);
}
catch(e){
console.log(e);
}
};
전체 뉴스 불러오기
GET https://newsapi.org/v2/top-headlines?country=kr&apiKey=API_KEY
특정 카테고리 뉴스 불러오기
GET https://newsapi.org/v2/top-headlines?country=kr&category=business&apiKey=API_KEY
카테고리 : business, entertainment, health, science, sports, technology
1.NewsItem 컴포넌트 구성
title, description, url, urlToImage 가진 JSON 객체
1. 프로젝트 디렉토리 안에 설치
npm install styled-components
2. 프로젝트 폴더에 import 하기
import styled from 'styled-components';
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;
import React from 'react';
import styled from "styled-components";
import NewsItem from './NewsItem';
const sampleArticle={
title:"제목",
description:"내용",
url:"https://google.com",
urlToImage:"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTWyeaxdZJy6ROj5lGScMD7ou9WmWwzjjWyEdORt8J-672adFtCM0vBF8KSEkoxv10RGsk&usqp=CAU",
};
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=()=>{
return (
<NewsListBlock>
<NewsItem article={sampleArticle}/>
<NewsItem article={sampleArticle}/>
<NewsItem article={sampleArticle}/>
<NewsItem article={sampleArticle}/>
<NewsItem article={sampleArticle}/>
</NewsListBlock>
);
};
export default NewsList;
!articles
로 조회 ‼import React, { useEffect, useState } 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=()=>{
const [articles,setArticles]=useState(null);
//loading 사용해 API 요청 대기 여부 판별 - default 요청 없으니 false
const [loading, setLoading]=useState(false);
useEffect(()=>{
//async 사용하는 함수 따로 선언
const fetchData=async()=>{
setLoading(true); //요청 받아 대기 중이니 true
try{
//axios, await로 API로 받아오기
const response=await axios.get("https://newsapi.org/v2/top-headlines?country=kr&apiKey=4f17bd32831b40dfb56ac62971b30743")
//받아온 article 데이터 세팅
setArticles(response.data.articles);
}
catch(e){
console.log(e);
}
//받은 요청 모두 완료하여 대기 없으니 loading을 false
setLoading(false);
};
fetchData();//article 데이터 받아오는 fetchData 함수 실행
},[]);
//대기중 : (loading = true)
if (loading){
return <NewsListBlock>...대기중...</NewsListBlock>
}
//아직 articles 값이 설정되지 않은 경우
if (!articles){
return null;
}
//articles 값이 유효함 - 모두 load된 경우
return (
<NewsListBlock>
{articles.map(article=>(
<NewsItem key={article.url} article={article}/>
))}
</NewsListBlock>
);
};
export default NewsList;
요청 대기 중 (loading = true)
요청 완료 (loading = false) && article 올바르게 설정됨
name=(API 내) 실체 카테고리 명
, text=랜더링용 한글 카테고리
import React from 'react';
import styled from 'styled-components';
const categories=[
{
name:"all",
text:'전체보기'
},
{
name : "business",
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.div`
font-size : 1.125rem;
font-weight : 1000;
cursor: pointer;
white-space:pre;
text-decoration: none;
color:inherit;
paddig-bottom:0.25rem;
&:hover{
color:#495057;
}
&+&{
margin-left:1rem;
}
`;
const Categories=()=>{
return (
<CategoriesBlock>
{categories.map(c=>(
<Category key={c.name}>{c.text}</Category>
))}
</CategoriesBlock>
);
};
export default Categories;
const App=()=>{
return(
<>
<Categories/>
<NewsList/>
</>
);
};
카테고리 목록 상단 랜더링 결과
import React, {useState,useCallback} from "react";
import axios from "axios";
import NewsList from './components/NewsList';
import Categories from './components/Categories';
/*API_KEY="4f17bd32831b40dfb56ac62971b30743" */
const App=()=>{
const [category,setCategory]=useState("all");
const onSelect=useCallback(category=>setCategory(category),[]);
return(
<>
<Categories category={category} onSelect={onSelect}/>
<NewsList category={category}/>
</>
);
};
export default App;
import React from 'react';
import styled,{css} from 'styled-components';
const categories=[...]
const CategoriesBlock=styled.div`...`
const Category=styled.div`
font-size:1.125rem;
cursor:pointer;
white-space:pre;
text-decoration:none;
color:inherit;
padding-bottom:0.25rem;
&:hover{
color: #495057;
}
${props=>
props.active && css`
font-weight:600;
border-bottom:2px solid #22b8cf;
color:#22b8cf;
&:hover{
color:#3bc9db;
}
`}
&+&{
margin-left:1rem;
}
`;
const Categories=({onSelect, category})=>{
return (
<CategoriesBlock>
{categories.map(c=>(
<Category
key={c.name}
active={category===c.name}
onClick={()=>onSelect(c.name)}>
{c.text}
</Category>
))}
</CategoriesBlock>
);
};
export default Categories;
카테고리 선택 시 active 스타일 적용
{공백}
, 특정 카테고리 선택 시 &category=ㅋ{카테고리}
요청 주소에 포함함수형 컴포넌트 (useEffect) 사용
1. 컴포넌트 맨 처음 랜더링되는 경우
2. category 값 변경되는 경우
요청 시작되도록 함
import React, { useEffect, useState } 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);
//loading 사용해 API 요청 대기 여부 판별 - default 요청 없으니 false
const [loading, setLoading]=useState(false);
useEffect(()=>{
//async 사용하는 함수 따로 선언
const fetchData=async()=>{
setLoading(true); //요청 받아 대기 중이니 true
try{
const query=category==="all"?"":`&category=${category}`;
console.log(query);
//axios, await로 API로 받아오기
const response=await axios.get(`https://newsapi.org/v2/top-headlines?country=kr${query}&apiKey=4f17bd32831b40dfb56ac62971b30743`)
//받아온 article 데이터 세팅
//console.log(response.data.articles);
setArticles(response.data.articles);
}
catch(e){
console.log(e);
}
//받은 요청 모두 완료하여 대기 없으니 loading을 false
setLoading(false);
};
fetchData();//article 데이터 받아오는 fetchData 함수 실행
},[category]);
//대기중 : (loading = true)
if (loading){
return <NewsListBlock>...대기중...</NewsListBlock>
}
//아직 articles 값이 설정되지 않은 경우
if (!articles){
return null;
}
//articles 값이 유효함 - 모두 load된 경우
return (
<NewsListBlock>
{articles.map(article=>(
<NewsItem key={article.url} article={article}/>
))}
</NewsListBlock>
);
};
export default NewsList;
전체 | 건강 | 기술 |
---|---|---|
npm add react-router-dom@5.3.0
버전 지정 한하면 라우팅 문법에 차이 발생함
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import {BrowserRouter} from "react-router-dom";
ReactDOM.render(
<BrowserRouter>
<App/>
</BrowserRouter>,
document.getElementById("root")
);
import React from 'react';
import Categories from '../components/Categories';
import NewsList from '../components/NewsList';
const NewPages=({match})=>{
//카테고리가 선택되지 않으면 기본값 all로 사용
const category = match.params.category||"all";
return(
<>
<Categories/>
<NewsList category={category}/>
</>
)
};
export default NewPages;
import React from "react";
import {Route} from "react-router-dom";
import NewsPage from "./pages/NewsPage";
const App=()=>{
return (<Route path="/:category?" component={NewsPage}/>)
}
export default App;
styled(컴포넌트이름)``
형식 /카테고리이름
설정/
으로 설정 (/all
아님)