728x90
Ajax
- AJAX ( Asynchronous Javascript and XML)는 웹 페이지의 이동없이 필요한 데이터만 전송하는 기술
웹 어플리케이션에서 데이터를 가져올 때 서버쪽 데이터가 필요할 때 ajax기법을 사용
이 작업은 시간이 오래 걸릴 수도 있기 때문에 비동기적으로 처리하게 됨
비동기(asynchronous): 요청이 끝날 때 까지 기다리는 것이 아니라, 동시에 여러 작업을 수행, 순서 보장X
나중에 react에서는 다른 서버의 REST API와 통신을 이용하여 데이터베이스 데이터를 가져올 수 있음
2023.01.06 - [클라이언트/JavaScript] - [JavaScript] AJAX, API, fetch, Promise, then, json, xml
ES6의 fetch를 이용 - 리액트에서 데이터 처리하기
Promise = fetch(요청주소)
1. 이벤트 클릭시 처리
- 데이터를 가져와서 useState()에 저장하는 작업
- 극단적으로 표현해 데이터 통신에 100초 가 소요되면 useState는 100초 간 undefined 상태가 됨
- 렌더링 시에 에러를 나타내기 때문에, undefined에 관한 처리를 동시에 진행
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)) // 클릭 이벤트를 넣지 않고 그냥 하면 계속 실행되서 무한루프 돈다. 절대 하면 안됨
}
return (
<Fragment>
<button onClick={handleClick}>데이터 가져오기</button>
{ //JSX 문법 - 3항 연산자
raw === undefined ? //state는 비어있기 때문에 빈 화면에 대한 처리를 해줘야 함
<div>
데이터준비중
</div>
:
<div>
아이디: {raw.userId},
비밀번호: {raw.userPw},
이름: {raw.userName}
</div>
}
{/*
가져온 데이터: {raw.userId}, {raw.userPw}, {raw.userName}
우선 화면을 바로 뿌려주는데 버튼을 누르지 않으면 state값이 없어서 바로 찍으면 annot read properties of undefined 뜸. 그래서 다른 화면을 보여줘야함
*/}
</Fragment>
)
}
export default App;
- 반복문, 조건문, 중첩된 함수 내에서 Hook 호출하지x
- 리액트에서 data fetching을 훅없이 사용하면 무한루프에 빠짐
state변경 - rerendering - data fetching - (state 값 변경으로 인한) rerenderting - 다시 data fetching 반복 - 무한루프를 막기 위해 3항 연산자를 사용하여 state값이 없는 상태에서의 처리를 해주고, 클릭 이벤트 실행시 데이터를 가져오게끔 만들어줌
2. 화면 렌더링 완료시 데이터 처리 - useEffect()훅 사용
- 비동기 작업을 컴포넌트에 바로 쓰고 state를 변경하면, 무한루프에 빠지게 됨
- 그래서 useEffect() 훅을 이용하여 첫번째 렌더링 완료시만 데이터만 가져오도록 처리
import { Fragment, useEffect, useState } from "react"
const App = () => {
//화면이 mount이후 데이터 가져오기 = 화면 렌더링 완료시 데이터 처리하기
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>
<h3>mount이후 데이터 가져오기</h3>
{
data && <div> {/* 처음에는 state값 없으므로 조건을 줘야함 */}
아이디: {data.userId}
비밀번호: {data.userPw}
이름: {data.userName}
</div>
}
</Fragment>
)
}
export default App;
- 똑같이 무한루프에 빠지게 되므로 이번에는 mount(생성) 이후 데이터를 가져오게 하기위해 useEffect 훅을 이용하여 첫번째 렌더링 이후에만 데이터를 가져오도록 만듦
- 처음에는 state값이 없으므로 undefined 처리를 해줘야 하는데 여기서 && 연산자 이용
data && <div> data값 </div> - 앞의 data 전체가 true이면 뒤의 값도 확인
앞의 data 전체가 false이면 뒤의 값 확인x = undefined이면 false 판별
Axios
- Axios는 비동기를 더 편하게 처리하는 라이브러리
- get(), post() 함수를 제공하고, 사용했을 때 리턴은 Promise
엑시오스 설치
npm add axios
엑시오스 사용
Promise(반환) = axios.get(요청주소).then 사용가능
axios 사용 비동기 코드
import axios from "axios"
import { Fragment, useState } from "react"
const App = () => {
/*
Axios는 비동기를 편하게 처리하는 라이브러리 입니다. (fetch로 사용해도 무방합니다.)
- 설치 npm add axios
- Axios는 get(), post() 함수를 제공하고, 사용했을 때 리턴은 Promise
*/
//비동기는 순서를 보장하지 않음
const handleClick = () => {
axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/hi.json")
// console.log(result); Promise 객체 나옴
.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);
})
}
return (
<Fragment>
<h3>엑시오스로 데이터 가져오기</h3>
<button onClick={handleClick}>데이터 가져오기</button>
{
data && <div>
아이디: {data.userId},
비밀번호: {data.userPw},
이름: {data.userName}
</div>
}
</Fragment>
)
}
export default App;
axios만 사용하여 동기적으로 사용하고 싶다면 함수 안에 함수를 넣어서 사용 가능
🔥🔥🔥🔥 콜백지옥 🔥🔥🔥🔥
//순서를 보장받고 싶다면? 콜백함수의 지옥
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);
});
});
});
}
axios 사용 동기 코드 ▼ async, await
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 = 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);
}
return (
<Fragment>
<h3>엑시오스로 데이터 가져오기</h3>
<button onClick={handleClick}>데이터 가져오기</button>
{
data && <div>
아이디: {data.userId},
비밀번호: {data.userPw},
이름: {data.userName}
</div>
}
</Fragment>
)
}
export default App;
async(이거 비동기임) , await(기다려) 적용
- ES6의 문법으로 비동기 코드를 간결하게 작성 가능
- 비동기 작업을 할 함수 앞에 async를 붙여 비동기 작업을 마치 동기 작업 처럼 작성할 수 있
규칙
- async 함수 안에서 await을 사용
- function 앞에 async 키워드를 추가, 함수는 언제나 Promise를 반환함
- 리턴이 Promise라면 await을 적용하고 then절을 없앨 수 있음
Axios는 이미 Promise를 반환하므로 Axios앞에 await을 사용할 수 있고, then() 절을 생략 할 수 있음
Axios를 호출하는 부모함수에는 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);
}
JS ES6 문법
Promise
- 자바스크립에 내장된 내장객체
- 상태(state) - pending(수행중), fullfilled(성공적 완료), rejected(실패)
- Producer - 정보를 제공하는 제공자 (즉, Promise)
Consumer - 사용자 (즉, 호출하는 사람 = 나)
- Promise 객체에는 executer라는 콜백 함수로 resolve, reject 라는 두개의 함수를 매개변수로 가지고 있음
Promise 생성
- executor콜백함수를 전달해야 하며 executor콜백함수는 다시 resolve함수와 reject함수를 받음
Promise 사용 - then(), catch()
Promise 적용
- 결과가 언제 돌아갈지 모르지만, 내가 이것을 완료되면 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) );
실습
동기적 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("sucess");
}
//비동기적 콜백
function bbb(func) {
setTimeout(function() {
func("sucess");
},2000);
}
//결과: 1 sucess 2 3 5 sucess 4
console.log(1);
//동기적 콜백의 반환
aaa(function(result) {
console.log(result); //sucess
console.log(2);
})
console.log(3);
//비동기적 콜백의 반환
bbb(function(result) {
console.log(result); //sucess
console.log(4);
})
console.log(5);
</script>
</body>
</html>
- aaa는 동기적으로 실행되는 함수이고, bbb는 setTImeOut으로 비동기적으로 실행되는 함수
- 1 success 2 3 까지는 동기적으로 실행되지만 bbb를 만나서 2초의 딜레이가 걸리면서 두번째 success와 4보다 5가 더 빨리 찍힘
Promise 객체의 실패 (성공은 주석)
<!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>
//프로미스 객체 - 자바스크립트 내장객체 (특별한 패턴을 제공해줍니다.)
//excuter라는 콜백함수를 전달받고, excuter 첫번째는 성공시킬 함수, 두번째는 실패일 때 실행시켜줄 함수
var promise = new Promise(function(success, fail) {
//성공
// setTimeout( function() {
// 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>
- Promise 객체를 생성해서 fail되게 한 다음 Error로 "에러남"이라는 텍스트를 던지면서 promise라는 변수에 담고, 콘솔에 찍어보면 Promise 객체가 반환됨을 알 수 있음
- 여기에는
state: reject,
result:- message: "에러남"
- stack: "Error: 에러남\n at http://127.0.0.1:5502/08async_await/index02.html:23:19\n at new Promise (<anonymous>)\n at http://127.0.0.1:5502/08async_await/index02.html:15:23" 등의 상태가 뜸
- Promise.catch로 reject된 상태가 받아서 에러남 이라는 텍스트가 콘솔에 출력됨
async, await으로 then없이 간결하게 만들기
<!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 => {
console.log(response); //data... 나옴
})
</script>
</body>
</html>
- myFetch 함수로 Promise 객체가 생성되었을 때, then으로 받아줄 success, fail 함수가 들어갈 매개변수가 없으므로 Promise 객체가 나옴 (?)
<!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
//async(이거 비동기야) - function 앞에 선언하게 되면 return 값을 자동으로 Promise로 변형해줍니다.
async function myFetch(url) {
return "data...";
}
//await(기다려) - 리턴이 Promise라면 await을 적용할 수 있고, then절을 생략할 수 있습니다. (단, async 함수 안에서만 사용할 수 있음)
(async () => { //즉시실행 함수
let result = await myFetch("http://...."); // await을 붙이면 .then(response => console.log(response)); 이 구문을 자동으로 해줌
console.log(result);
}) ();
</script>
</body>
</html>
- async을 사용하면 리턴값을 자동으로 Promise로 변형해 줌
- 리턴값이 Promise 라면 await을 사용할 수 있고, await을 이용하면 then절 생략가능
- 그래서 위의 코드와 똑같이 result로만 받았어도 결과는 ...data로 정상적으로 나옴
전체코드
<!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 => {
console.log(response); //data... 나옴
})
myFetch("http://~~")
.then(response => response)
.then(data => data)
.then(data => console.log(data)); //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://...."); // await을 붙이면 .then(response => console.log(response)); 이 구문을 자동으로 해줌
console.log(result);
}) ();
</script>
</body>
</html>
Ajax 실습 - news API
App.js
import { Fragment } from "react"
import { Route, Routes } from "react-router-dom";
import Header from "./layout/Header";
import NewsHome from "./component2/NewsHome";
const App = () => {
return (
<Routes>
<Route element={<Header/>}> {/* 헤더 사용하려고 */}
<Route path="/" element={<NewsHome/>}/> {/* 본문 내용 */}
<Route path="/:category" element={<NewsHome/>}/> {/* 본문 내용 */}
</Route>
</Routes>
)
}
export default App;
- 라우터의 사용과 각 주소를 연결하는 기본 틀을 만드려고 App.js 사용
- 라우터로 NewsHome에 연결하고 header 사용
- <Route path="/:category" element={<NewsHome/>}/> {/* 본문 내용 */}
카테고리 값이 바뀌면 그에 따라서 article이 바뀌게 하기 위해서 설정
NewsHome.js - 기본 구성
import { Fragment } from "react";
import NewsCategory from "./NewsCategory";
import NewsList from "./NewsList";
const NewsHome = () => {
return (
<Fragment>
<NewsCategory/>
<NewsList/>
</Fragment>
)
}
export default NewsHome;
- NewsHome에서는 카테고리와 리스트 컴포넌트를 넣음
- 결과적으로 아래 사진처럼 배치
NewsList.js
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 가져오기
//8eeb2d41ecd540b38900f5df80334afc
//5. 라우터로 들어오는 값에 처리
const {category} = useParams();
//category가 없거나 undefined이면 all
const query = (category || 'all') === 'all' ? '' : `&category=${category}`; //이 조건은 변할 수 있음!
// console.log(query);
//2. useEffect에서 화면 로딩시 데이터처리
const [data, setData] = useState(); //이렇게 하면 첫번쨰 데이터는 undefined고, 두번째부터 20개의 articles 들어옴
//useEffect는 내장 함수이기 때문에 그대로 유지하고, 함수를 하나 걸어서 async 해주기
useEffect( () => {
(async() => { //즉시 실행함수의 매개변수로 async
const url = `https://newsapi.org/v2/top-headlines?country=kr${query}&apiKey=8eeb2d41ecd540b38900f5df80334afc`;
console.log(url);
let {data: {articles}} = await axios.get(url); //data를 가져와서 그 안의 articles, 구조분해 할당
setData(articles);
setLoding(true); //로딩완료
}) (); /* 즉시실행함수 괄호 빼먹지 말자 */
}, [query] ); //6. 변화가 일어날 때마다 재실행할 변수
// console.log(data);
//3. 데이터 로딩처리 (데이터가 오기전에 state는 undefined)
const [loading, setLoding] = useState(false);
if(loading === false) {
return <div>로딩중</div>
}
//4. li태그를 컴포넌트로 변경
return (
<div className={styled.news_container}>
<h3>오늘의 헤드라인</h3>
<ul className={styled.news_wrap}>
{
/* 1. url, urlToImage, title, author, description, publishedAt */
data.map( (item, index) => <NewsArticle key={index+1} item={item}/>)
}
</ul>
</div>
)
}
export default NewsList;
NewsCategory.js
import { NavLink } from "react-router-dom";
const NewsCategory = () => {
/*
business, entertainment, general, health, science, sports, technology
1. 카테고리 맵 회전 - 카테고리는 고정으로 있는 값 (state로 관리할 필요x)
2. 라우터 설정 = App.js 가보면 /여도 NewsHome, /:category여도 NewsHome
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}>
{/* 3항 연산자 부분: 돌아가는 주소 부분이 all이면 공백으로 바꿔주고, 나머지는 링크에 걸린 이름 나오게 */}
<NavLink to={item.name === 'all' ? '/' : `/${item.name}`} style={({isActive}) => isActive ? myStyle : undefined}>{item.topic}</NavLink>
</li>
)
}
</ul>
)
}
export default NewsCategory;
NewsArticle.js
const NewsArticle = ( /* props */ {item} ) => { //props 확인해 보고 해석하기
// console.log(props);
/* 1. url, urlToImage, title, author, description, publishedAt */
const {url, urlToImage, title, author, description, publishedAt} = item;
//날짜 데이터 형식 수정
let date = new Date(publishedAt);
let year = date.getFullYear();
let month = date.getMonth() + 1;
let day = date.getDate();
return (
<li>
<a href={url}>
<img src={urlToImage} alt={title} />
<div>
<p>{author === null? "기자없음" : author}</p>
<p>{`${year}년 ${month}월 ${day}일`}</p>
<p>{title}</p>
<p>{description}</p>
</div>
</a>
</li>
)
}
export default NewsArticle;
NewsList.module.js
.news_container {
padding: 50px 40px;
}
.news_wrap {
display: flex;
flex-direction: column;
}
.news_wrap li {
position: relative;
min-height: 150px; /* 최소넓이 */
padding-left: 200px;
}
.news_wrap li img {
position: absolute;
top: 0;
left: 0;
width: 150px;
height: 100px;
}
.news_wrap li a {
color: black;
text-decoration: none;
}
오늘 하루
더보기
기억에 남는 부분
-
-
어려운 부분
-
-
문제 해결 부분
-
-
728x90
'클라이언트 > React' 카테고리의 다른 글
[React] contextAPI, Redux, firebase (0) | 2023.01.25 |
---|---|
[React] SPA, Router, Link(to, element) queryString(useLocation, useSearchParams), URLParameter(useParams), 중첩라우터(Outlet), NavLink(useNavigate) (0) | 2023.01.19 |
[React] css적용 (0) | 2023.01.18 |
[React] Component 반복, 이미지 가져오기, hook(useState, userEffect, useRef, useReduce) (0) | 2023.01.17 |
[React] state, useState({객체}), 이벤트 핸들링 (3) | 2023.01.16 |