1. Ajax란?
- AJAX ( Asynchronous Javascript and XML) 은 웹 페이지의 이동없이 필요한 데이터만 전송하는 기술입니다.
- 웹 어플리케이션에서 데이터를 가져올 때 서버쪽 데이터가 필요할 때 ajax기법을 사용하게 됩니다.
- 이 작업은 시간이 오래 걸릴 수도 있기 때문에 비동기적으로 처리하게 됩니다.
- 비동기(asynchronous)는 요청이 끝날 때 까지 기다리는 것이 아니라, 동시에 여러 작업을 수행하게 됩니다.
- 나중에 react에서는 다른 서버의 REST API와 통신을 이용하여 데이터베이스 데이터를 가져올 수 있습니다.
2. ES6의 fetch를 이용해서 리액트에서 데이터 처리하기
- fetch를 하면 promise라는 객체를 반환함. ( promise.then(콜백 함수) )
Promise = fetch(요청주소)
1) 이벤트 클릭 시 처리하기
- 데이터를 가져와서 useState()에 저장하는 작업입니다.
- 극단적으로 표현해 데이터 통신에 100초가 소요되면 useState는 100초 간 undefined 상태가 됩니다.
- 렌더링 시에 에러를 나타내기 때문에, undefined에 관한 처리를 동시에 진행합니다.
import { useEffect, useState } from "react";
const App = () => {
let [raw, setRaw] = useState();
const handleClick = () => {
fetch('https://raw.githubusercontent.com/yopy0817/data_example/master/hi.json')
.then( response => response.json() )
.then( data => {
//console.log(data)
setRaw(data)
})
}
return (
<div>
<h3>클릭시에 fetch로 데이터 가져오기</h3>
<button type="button" onClick={handleClick}>데이터로드</button>
{raw !== undefined ?
<div>
{raw.userId}<br />
{raw.userPw}<br />
{raw.userName}<br />
</div>
: undefined
}
</div>
)
}
export default App;
2) 화면 렌더링 완료시 데이터 처리하기 useEffect() 훅 사용
- 비동기 작업을 컴포넌트에 바로 쓰고 state를 변경하면, 무한루프에 빠지게 됩니다.
- 그래서 useEffect() 훅을 이용하여 첫번째 렌더링 완료시만 데이터만 가져오도록 처리합니다.
import { useEffect, useState } from "react";
const App = () => {
let [data, setData] = useState()
//렌더링 완료시 데이터 로드
useEffect( () => {
fetch('https://raw.githubusercontent.com/yopy0817/data_example/master/hi.json')
.then( response => response.json() )
.then( data => {
setData(data)
})
}, [])
return (
<div>
<h3>로딩시에 fetch로 데이터 가져오기(같은표현)</h3>
{data && <div>
{data.userId}<br />
{data.userPw}<br />
{data.userName}<br />
</div>
}
</div>
)
}
export default App;
* 예제
import { Fragment, useEffect, useState } from "react";
const App = () => {
/*
Ajax를 이용해서 외부데이터 가져오기
1. Promise = fetch()
*/
//클릭해서 데이터가져오기
const [raw, setRaw] = useState();
const handleClick = () => {
fetch("https://raw.githubusercontent.com/yopy0817/data_example/master/hi.json")
.then( response => response.json() )
.then( data => setRaw(data) )
}
//화면이 mount이후 데이터가져오기 - useEffect()
const [data, setData] = useState();
useEffect(() => {
fetch("https://raw.githubusercontent.com/yopy0817/data_example/master/hi.json")
.then(response => response.json() )
.then(data => setData(data) )
}, []);
return (
<Fragment>
<button onClick={handleClick}>데이터가져오기</button>
{
raw === undefined ?
<div>
데이터준비중
</div>
:
<div>
아이디:{raw.userId}<br/>
PW: {raw.userPw}<br/>
이름: {raw.userName}<br/>
</div>
}
<h3>mount이후 데이터 가져오기</h3>
{
data && <div>
아이디:{data.userId}<br/>
PW:{data.userPw}<br/>
이름:{data.userName}<br/>
</div>
}
</Fragment>
)
}
export default App;
3. Axios로 데이터 처리하기
- 엑시오스는 비동기를 더 편하게 처리하는 라이브러리 입니다.
- 엑시오스 설치
npm add axios
- 엑시오스
Promise = axios.get(요청주소)
1) 이벤트 클릭시 처리하기 (fetch와 거의 같음 😊)
import axios from "axios";
import { useState } from "react";
const App = () => {
const handleClick = () => {
axios.get('https://raw.githubusercontent.com/yopy0817/data_example/master/hi.json')
.then(response => {
setData( response.data );
})
}
return (
<div>
<h3>엑시오스데이터</h3>
<button type="button" onClick={handleClick}>데이터로드</button>
{data !== undefined ?
<div>
{data.userId}<br />
{data.userPw}<br />
{data.userName}<br />
</div>
:
undefined
}
</div>
)
}
export default App;
4. async, await 적용하기
- ES6의 문법입니다. 비동기 코드를 간결하게 작성할 수 있게 합니다.
* async, await규칙
1) 어싱크 함수 안에서 어웨잇을 사용한다.
2) function 앞에 async 키워드를 추가 함수는 언제나 프라미스를 반환합니다.
3) 리턴이 프로미스라면 await을 적용하고 then절을 없앨 수 있다.
- 비동기에서 3번의 데이터 통신을 한다고 가정합시다.
- 1,2,3,4,5,6의 순서로 출력될 것 같지만 아닙니다.
- 순서를 보장하지 않습니다.
...생략
const handleClick = () => {
axios.get('https://raw.githubusercontent.com/yopy0817/data_example/master/hi.json')
.then(response => {
console.log(response.data);
console.log(1)
})
console.log(2)
axios.get('https://raw.githubusercontent.com/yopy0817/data_example/master/hello.json')
.then(response => {
console.log(response.data);
console.log(3)
})
console.log(4)
axios.get('https://raw.githubusercontent.com/yopy0817/data_example/master/by.json')
.then(response => {
console.log(response.data);
console.log(5)
})
console.log(6)
}
* 예제
import axios from "axios";
import { Fragment, useState } from "react";
const App = () => {
/*
-Axios는 비동기를 편하게 처리하는 라이브러리 입니다 (fetch로 사용해도 무방합니다)
-설치 npm add axios
-Axios는 get(), post()함수를 제공하고, 사용했을때 리턴은 Promise
*/
const [data, setData] = useState();
//순서를 보장받고 싶다면?
const handleClick = () => {
axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/hi.json")
.then(response => {
console.log(response.data)
console.log(1);
axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/by.json")
.then(response => {
console.log(response.data)
console.log(3);
axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/hello.json")
.then(response => {
console.log(response.data)
console.log(5);
});
});
});
}
//순서를 보장하지 않음
/*
const handleClick = () => {
axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/hi.json")
.then(response => {
console.log(response.data)
console.log(1);
});
console.log(2);
axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/by.json")
.then(response => {
console.log(response.data)
console.log(3);
});
console.log(4);
axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/hello.json")
.then(response => {
console.log(response.data)
console.log(5);
});
console.log(6);
}
*/
return (
<Fragment>
<h3>엑시오스로 데이터 가져오기</h3>
<button onClick={handleClick}>데이터가져오기</button>
{
data && <div>
아이디: {data.userId}<br/>
PW: {data.userPw}<br/>
이름: {data.userName}
</div>
}
</Fragment>
)
}
export default App;
- Axios는 이미 Promise를 반환합니다.
- Axios앞에 await을 사용할 수 있고, then() 절을 생략 할 수 있습니다.
- Axios를 호출하는 부모함수에는 await을 반드시 달아줍니다.
* async, await의 장점
- 코드의 간결성
- 어싱크, 어웨잇은 동기적 방식(순서를) 보장한다
* async, await 적용하여 변경하기
const handleClick = async () => {
let response = await axios.get('https://raw.githubusercontent.com/yopy0817/data_example/master/hi.json')
console.log(response.data);
setData( response.data );
console.log(1);
let response2 = await axios.get('https://raw.githubusercontent.com/yopy0817/data_example/master/hello.json')
console.log(response2.data);
console.log(2);
let response3 = await axios.get('https://raw.githubusercontent.com/yopy0817/data_example/master/by.json')
console.log(response3.data);
console.log(3);
}
5. JS ES6 문법 Promise, async, await
- 프로미스는 자바스크립에 내장된 내장 객체입니다.
- 프로미스 객체에서는 2가지 형태를 지닙니다.
1) 상태(state)
- pending: 수행 중
- fullfilled: 성공적 완료
- rejected: 실패
2) 프로듀서 - 정보를 제공하는 제공자(즉, promise), 컨슈머 - 사용자(즉, 호출하는 사람)
* 프로미스 생성
- executor콜백함수를 전달해야 하며 executor콜백함수는 다시 resolve함수와 reject함수를 받습니다.
//매개변수는 컨슈머가 분명 콜백함수를 전달할거야!(그 콜백함수로 리턴해줄게!)
let promise = new Promise( (success, fail) => {
//producer 입장에서 성공했을 때 성공을 알리는 함수
success("success");
//producer 입장에서 실패했을 때 실패를 알리는 함수
//fail(new Error("fail"));
});
console.log(promise)
* 프로미스의 사용(then(), catch())
- then은 promise가 정상적으로 실행 된다면, 결과를 실행시켜주는 콜백함수이다.
- catch는 promise가 실패했을 때, 결과를 실행시켜주는 콜백함수이다.
//....생략
promise.then( data => console.log(data) )
.catch( error => console.log(error) )
* 프로미스의 적용
- 결과가 언제 돌아갈지 모르지만, 내가 이것을 완료되면 Promise를 전달해 줄테니, 너는 then함수만 이용해서 결과를 받아서 처리해!
- ajax fetch()의 내부모습 예시
//자바스크립트로 구현되어있는 fetch라고 가정
function myfetch(req) {
//비동기적 실행.. 10초..
return new Promise( (success, fail) => {
success("data....");
});
}
//fetch와 같은 실행구조를 보인다.
myfetch("http://localhost:5502/~~~~~").then( response => console.log(response) );
* 예제
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//프로미스 객체 - 자바스크립트 내장객체 (특별한 패턴을 제공해줍니다.)
//excutor라는 콜백함수를 전달받고 excutor첫번째는 성공시킬 함수, 실패일 때 실행시켜줄 함수
var promise = new Promise(function(success, fail) {
//성공
// setTimeout(function() {
// success("success"); //3초 이후 실행
// }, 3000);
//실패
fail(new Error("에러남"));
});
// console.log(promise);
promise.then(function(response) {
console.log(response);
}).catch(function(err) {
console.log(err);
})
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//예시 ) myFetch
/*
function myFetch(url) {
//....url을 가지고 처리
return new Promise(function(success, fail) {
success("data....");
});
}
//var result = myFetch("http://~~~~~~");
//console.log(result); //promise
myFetch("http://~~~~~~")
.then(response => response)
.then(data => data)
.then(data => console.log(data));
*/
//async(이거비동기야) - function앞에 선언하게 되면 return값을 자동으로 Promise변형해줍니다.
async function myFetch(url) {
return "data..";
}
//var result = myFetch("http://.....");
//console.log(result);
//myFetch("http://.....")
//.then(response => console.log(response));
//await(기다려) - 리턴이 Promise라면 await을 적용할 수 있고 then절을 생략할 수 있습니다. (단, async함수 안에서만 사용 할 수 있음)
(async () => {
let result = await myFetch("http://.....");
console.log(result);
})();
</script>
</body>
</html>
* Promise를 더 쉽게 사용할 수 있게 해주는 async(이거 비동기야)
- async란 function앞에 선언하며, return값을 자동으로 Promise로 변경해주는 역할을 한다.
- 즉, 비동기 함수의 실행 형태로 변경해준다.
* async, await규칙
1) 어싱크 함수 안에서 어웨잇을 사용한다
2) function 앞에 async 키워드를 추가 함수는 언제나 프라미스를 반환합니다.
3) 리턴이 프로미스라면 await을 적용하고 then절을 없앨 수 있다.
* Promise를 더 쉽게 사용할 수 있게 해주는 await(기다려)
- 리턴이 프로미스라면 await을 적용하고 then절을 없앨 수 있다. (단 async함수 안에서만 사용 가능합니다)
(async function() {
let data = await myfetch();
console.log(data)
})();
* async, await의 장점
- 코드의 간결성
- 어싱크, 어웨잇은 동기적 방식(순서)을 보장한다.
* 동기적 콜백 vs 비동기적 콜백 예제
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//동기적 콜백 vs 비동기적 콜백
//동기적 콜백
function aaa(func) {
func("success");
}
//비동기적 콜백
function bbb(func) {
setTimeout(function() {
func("success");
}, 2000);
}
console.log(1);
//동기적 콜백의 반환
aaa(function(result) {
console.log(result); //success
console.log(2);
})
console.log(3);
//비동기적 콜백의 반환
bbb(function(result) {
console.log(result); //success
console.log(4);
})
console.log(5);
</script>
</body>
</html>
* app.js
import axios from "axios";
import { Fragment, useState } from "react";
const App = () => {
/*
-Axios는 비동기를 편하게 처리하는 라이브러리 입니다 (fetch로 사용해도 무방합니다)
-설치 npm add axios
-Axios는 get(), post()함수를 제공하고, 사용했을때 리턴은 Promise
*/
const [data, setData] = useState();
//async await의 장점
//1. 코드가 간결해집니다.
//2. 동기적인 호출 방식을 보장해줍니다.
const handleClick = async () => {
let result = await axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/hi.json")
console.log(result.data);
console.log(1);
let result2 = await axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/by.json")
console.log(result2.data);
console.log(2);
let result3 = await axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/hello.json")
console.log(result3.data);
console.log(3);
}
/*
//순서를 보장받고 싶다면?
const handleClick = () => {
axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/hi.json")
.then(response => {
console.log(response.data)
console.log(1);
axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/by.json")
.then(response => {
console.log(response.data)
console.log(3);
axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/hello.json")
.then(response => {
console.log(response.data)
console.log(5);
});
});
});
}
*/
//순서를 보장하지 않음
/*
const handleClick = () => {
axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/hi.json")
.then(response => {
console.log(response.data)
console.log(1);
});
console.log(2);
axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/by.json")
.then(response => {
console.log(response.data)
console.log(3);
});
console.log(4);
axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/hello.json")
.then(response => {
console.log(response.data)
console.log(5);
});
console.log(6);
}
*/
return (
<Fragment>
<h3>엑시오스로 데이터 가져오기</h3>
<button onClick={handleClick}>데이터가져오기</button>
{
data && <div>
아이디: {data.userId}<br/>
PW: {data.userPw}<br/>
이름: {data.userName}
</div>
}
</Fragment>
)
}
export default App;
* 뉴스 API키로 한국 뉴스 데이터 불러와서 NewsList 작성
(category 누를 때마다 주제에 맞는 기사 불러와짐)
- App.js, NewsHome.js, NewsList.js, NewsArticle.js, NewsCategory.js, NewsList.module.css
import { Route, Routes } from "react-router-dom";
import NewsHome from "./component2/NewsHome";
import Header from './layout/Header';
const App = () => {
return (
<Routes>
<Route element={<Header/>}>
<Route path="/" element={<NewsHome/> }/>
<Route path="/:category" element={<NewsHome/> }/>
</Route>
</Routes>
)
}
export default App;
import { Fragment } from "react";
import NewsCategory from "./NewsCategory";
import NewsList from "./NewsList";
const NewsHome = () => {
return (
<Fragment>
<NewsCategory/>
<NewsList/>
</Fragment>
)
}
export default NewsHome;
import axios from "axios";
import { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import NewsArticle from "./NewsArticle";
import styled from './NewsList.module.css'; //css
const NewsList = () => {
//1. API 가져오기
//news API 개인 key
//5. 라우터로 들어오는 값에 처리
const {category} = useParams();
//category 없거나 undefinded이면 all로 할당
const query = (category || 'all') === 'all' ? '' : `&category=${category}`;
//2. useEffect에서 화면로딩시 데이터처리
const [data, setData] = useState();
useEffect( () => {
(async () => {
const url = `https://newsapi.org/v2/top-headlines?country=kr${query}&apiKey=위에 입력한 개인키`;
console.log(url);
let {data: {articles}} = await axios.get(url);
setData(articles);
setLoading(true); //로딩완료
})();
}, [query]) //6. 변화가 일어날 때 마다 재실행할 변수
//3. 데이터 로딩처리 (데이터가 오기 전에 state는 undefined)
const [loading, setLoading] = useState(false);
if(loading === false) {
return <div>로딩중</div>
}
//4. li태그를 컴포넌트로 변경
return (
<div className={styled.news_container}>
<h3>오늘의 헤드라인</h3>
<ul className={styled.news_wrap}>
{
data.map( (item, index) => <NewsArticle key={index+1} item={item} /> )
}
</ul>
</div>
)
}
export default NewsList;
//NewsList에 data.map((item, index) => <NewsArticle key={index+1} item={item}/>)
// 그래서 const NewsArticle = ( {item} ) => 안에 넣어주고 props로 넘겨줌.
//원래 {item.url} 이었는데 {url}로 변경
const NewsArticle = ({ item }) => {
/* 1. url, urlToImage, title, author, description, publishedAt */
const { url, urlToImage, title, author, description, publishedAt } = item;
// console.log(props);
let date = new Date(publishedAt);
let year = date.getFullYear();
let month = date.getMonth() + 1;
let day = date.getDate();
//props로 넘겨줌.
return (
<li>
<a href={url}>
<img src={urlToImage} alt={title} />
<div>
<p>{author === null? "기자 없음" : author}</p>
{/* <p>{publishedAt}</p> */}
<p>{`${year}년 ${month}월 ${day}일`}</p>
<p>{title}</p>
<p>{description}</p>
</div>
</a>
</li>
)
}
export default NewsArticle;
import { NavLink } from "react-router-dom";
const NewsCategory = () => {
/*
business
entertainment
general
health
science
sports
technology
1. 카테고리 맵회전
2. 라우터설정
3. NewsList에서는 라우터값을 처리
*/
const category = [
{name: "all", topic: "전체"},
{name: "business", topic: "비지니스"},
{name: "entertainment", topic: "연예"},
{name: "general", topic: "일반"},
{name: "health", topic: "건강"},
{name: "science", topic: "과학"},
{name: "sports", topic: "스포츠"},
{name: "technology", topic: "기술"},
]
const myStyle = {
color : "pink",
borderBottom: "3px solid red"
}
return (
<ul>
{
category.map( (item, index) => <li key={index}>
<NavLink to={ item.name ==='all' ? '/' : `/${item.name}`} style={({isActive}) => isActive ? myStyle : undefined }>{item.topic}</NavLink>
</li>)
}
</ul>
)
}
export default NewsCategory;
.news_container {
padding: 50px 40px;
}
.news_wrap {
display: flex;
flex-direction: column;
}
.news_wrap li {
position: relative;
min-height: 150px;
padding-left: 200px;
list-style: none;
}
.news_wrap li img {
position: absolute;
top: 0;
left: 0;
width: 150px;
height: 100px;
}
.news_wrap li a {
color: #000;
text-decoration: none;
}
'TIL > React' 카테고리의 다른 글
day72-react (0) | 2023.01.25 |
---|---|
day70-react (0) | 2023.01.19 |
day69-react (0) | 2023.01.18 |
day68-react (0) | 2023.01.18 |
day67-react (0) | 2023.01.17 |