state ์ด๊ธฐ๊ฐ์ผ๋ก ๋์๊ฐ๋๋ฐ ๊ทธ๊ฒ์ ๋ฐฉ์งํ๊ธฐ ์ํด state๋ฅผ ์๋ฒ๋ก ๋ณด๋ด์ DB์ ์๊ตฌ์ ์ฅ ํ๋ ๊ฒ
- key/value ํํ๋ก ์ ์ฅ๊ฐ๋ฅ
- ์ต๋ 5MB๊น์ง ๋ฌธ์ ์ ์ฅ์ด ๊ฐ๋ฅํ๋ค
- ์ฌ์ดํธ ์ฌ์ ์ํด๋ ๋จ์์์ผ๋ ๋ธ๋ผ์ฐ์ ์ฒญ์ํ๊ฒ ๋๋ฉด ์ญ์ ๋๋ค
LocalStorage๋ ์ฌ์ ์ํด๋ ๋ฐ์ดํฐ๊ฐ ๋จ์์์ผ๋ SessionStorage๋ ๋ธ๋ผ์ฐ์ ๋ฅผ ๊ป์ ๋ ๋ฐ์ดํฐ๊ฐ ์ฌ๋ผ์ง๋ค//console.log์์ ํ์ธํ๊ธฐ
//๋ฐ์ดํฐ ์ถ๊ฐ
localStorage.setItem('์ด๋ฆ', '๊ฐ')
//๋ฐ์ดํฐ ๋ถ๋ฌ์ค๊ธฐ
localStorage.getItem('์ด๋ฆ')
'๊ฐ' // ๊ฐ์ด ์ถ๋ ฅ๋๋ค
//๋ฐ์ดํฐ ์ญ์
localStorage.removeItem('์ด๋ฆ') //localstorage์์ ์ญ์ ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค
getItem์ผ๋ก ๊บผ๋ด์ ์์ ํ๊ณ , ๋ค์ ์ ์ฅํ๋ฉด ๋๋คJSON์ผ๋ก ๋ฐ๊พธ๋ฉด ๋ฌธ์๋ก ์ทจ๊ธ๋๊ธฐ ๋๋ฌธ์ ์ ์ฅ์ด ๊ฐ๋ฅํ๋ค//๋ฐฐ์ด/๊ฐ์ฒด๋ฅผ JSON์ผ๋ก ๋ณํํ๋ ๋ฐฉ๋ฒ
const obj = { name: "kim" };
localStorage.setItem("data", JSON.stringify(obj));

JSON์ผ๋ก ๋ณํํ๊ธฐ ๋๋ฌธ์ localstorage์ ์ ์ฅ์ด ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค. ์ด๋ฌํ ๋ฐฉ๋ฒ์ ๋จ์ ์ผ๋ก๋ getItem์ผ๋ก ๊บผ๋ด๋ณด๋ฉด JSON์ผ๋ก ๋ณํ๋์ ๋ฌธ์๋ก ๋ณ๊ฒฝ ๋์ด์๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ๊ฐ์ฒด์ ์ ๊ทผ์ ๊ฐ์ฒด ๋ฌธ๋ฒ์ผ๋ก ํ์ง ๋ชปํ๋ค ๋ค์ ๊ฐ์ฒด๋ก ๋๋๋ฆฌ๋ ค๋ฉด ์๋ ์ฝ๋๋ฅผ ์ฐธ๊ณ ํ์//JSON์ ๋ฐฐ์ด/๊ฐ์ฒด๋ก ๋ณํํ๋ ๋ฐฉ๋ฒ
JSON.parse(obj);
npm install react-query@3import { QueryClient, QueryClientProvider, useQuery } from 'react-query' //import ํ๊ธฐ
useQuery(['์๋ช
'], // [] ์๋ตํ๋ฉด ์ ๋จ
import { QueryClient, QueryClientProvider } from "react-query" //1๋ฒ
const queryClient = new QueryClient() //2๋ฒ
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<QueryClientProvider client={queryClient}> //3๋ฒ
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
</QueryClientProvider>
);
//App.js
`{ // ๋ฐ์ดํฐ ๋ด์ฉ
"name" : "Mark",
"email" : "mark123@gmail.com"
}`
let result = useQuery('์๋ช
', ()=>
axios.get('https://codingapple1.github.io/userdata.json')
.then((a)=>{ return a.data })
)
<Nav className="ms-auto">
{result.isLoading && "๋ก๋ฉ์ค"} // isLodading์ด๋ฉด "๋ก๋ฉ์ค" ๋ณด์ด๊ฒ, ์๋๋ ๊ฐ์ ๊ธฐ๋ฅ
{result.error && "Error"}
{result.data && result.data.name}
</Nav>
Single Page Application์ด๋ค ํน์ง์ html, jsํ์ผ์ด ํ๋๋ง ์์ฑ๋๋ค ๊ทธ ์์ ์ง๊ธ๊น์ง ๋ง๋ App.jsx, Detail.jsx ๋ฑ ๋ชจ๋ ๋ด์ฉ์ด ๋ค์ด ์์ด์ ํ์ผ ์ฌ์ด์ฆ๊ฐ ํฌ๋ค ๊ทธ๋์ ๋ฆฌ์กํธ ์ฌ์ดํธ๋ค์ ์ฒซ ํ์ด์ง ๋ก๋ฉ์๋๊ฐ ๋ง์ด ๋๋ฆด ์ ์๋ค ๊ทธ๊ฒ์ ๋ฐฉ์งํ๊ธฐ ์ํด jsํ์ผ์ import๋ฌธ๋ฒ์ ๊ณ ์น๋ฉด ๋๋๋ฐ ์๋ ์ฝ๋ ์ฐธ๊ณ !import { createContext, lazy, useEffect, useState } from "react"; //lazy import ํ๊ธฐ
// import Detail from "./routes/Detail"; // ์ด๋ ๊ฒ ์์ฑํ๋ฉด ํ์ผ ์ฌ์ด์ฆ๊ฐ ํฌ๊ณ ๋ก๋ฉ์๋๊ฐ ๋๋ ค์ง
// import Cart from "./routes/Cart";
const Detail = lazy(() => import("./routes/Detail"));
const Cart = lazy(() => import("./routes/Cart"));

<Suspense fallback={<div>๋ก๋ฉ ์ค ์
๋๋ค ...</div>}> //์ง์ฐ์๊ฐ ๋ฐ์ํ ๋ ๋ก๋ฉ์ค์
๋๋ค ํ๋ฉด ๋ณด์ฌ์ง
<Routes>
<Route
path="/detail/:id"
element={<Detail shoes={shoes} />
<Route
path="cart"
element={<Cart />} />
</Routes>
</Suspense>
<Suspense>๋ก <Routes>๋ฅผ ์ ๋ถ ๊ฐ์ธ๋ ๋๋คmemo๋ ์ปดํฌ๋ํธ๊ฐ ํ์ํ ๋๋ง ๋ฆฌ๋ ๋๋ง ํด์ฃผ๊ธฐ ๋๋ฌธ์ ๋ถ๋ชจ์ ๊ฐ์ด ๋ฐ๋์ด๋ ๋ฆฌ๋ ๋๋ง์ด ๋์ง ์๋๋ค
memo์ ์๋ฆฌ๋ Child์ ํ๋กญ์ค๊ฐ ๋ณํ ๋๋ง ๋ฆฌ๋ ๋๋ง์ ํด์ค๋ค
โmemo ๋จ์ ์ ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง์ ๋ง๊ธฐ ์ํด์ ๊ธฐ์กด props === ๋ฐ๋ props ๋ฅผ ๋น๊ตํ๋ ์ฐ์ฐ์ ํ๊ณ , ์คํํ๋ ํน์ง์ด ์๋๋ฐ props๊ฐ ๋ง์ฝ์ ํฌ๊ณ ๋ณต์กํ๋ฉด ์๋ ์ง์ฐ๋๊ธฐ ๋๋ฌธ์ ๊ผญ ํ์ํ ๊ณณ์๋ง ์ฌ์ฉํ๋ค
//Cart.jsx
import { memo, useState } from "react"; // memo import ํ๊ธฐ
const Child = memo(() => { //Child ์ปดํฌ๋ํธ memo() ๊ฐ์ธ์ฃผ๊ธฐ
console.log("๋ฆฌ๋ ๋๋ง"); // Child์ props๊ฐ ๋ณํ ๋๋ง ์ถ๋ ฅ
return <div>์์์์</div>;
});
return (
<>
<Child count={count}/> // props์ ๋ฌ
<Button
onClick={() => {
setCount(count + 1);
}}
variant="secondary"
>
+
</Button>
useEffect์ ๋น์ทํ ์ฉ๋์ด๋ค. ๋ค๋ฅธ ์ ์ ์คํ์์ ์ ์ฐจ์ด ๋ฐ์ ์๋ค usememo๊ฐ ์คํ ๋ ๋ ํจ์๋ ๋์์ ์คํ๋๊ธฐ ๋๋ฌธ์ ์ปดํฌ๋ํธ ์์ฑ ์ 1ํ๋ง ์คํํ๊ณ ์ถ์ ์ฝ๋๊ฐ ์์ ๋ ์ฌ์ฉํ๋ค 
โ๋ฆฌ์กํธ 17๋ฒ์ ์์๋ ajax์์ฒญ, setTimeout์์ state ๋ณ๊ฒฝํจ์๊ฐ ์ฌ๋ฌ๊ฐ ์๋ ๊ฒฝ์ฐ์๋ batching์ด ์ผ์ด๋์ง ์๋๋ค
ํ์ง๋ง โ๋ฆฌ์กํธ 18๋ฒ์ ์์๋ batching์ด ์ ์ฉ๋๋ค
useTransition๋ก ๊ฐ์ธ๋ฉด ๋ ๋๋ง ํ ๋ ๋ฒ๋ฒ
๊ฑฐ๋ฆฌ์ง ์๊ฒ ํด์ค๋ค (๋ณดํต์ ์ฝ๋ ์คํ์์ ๋ง ์กฐ์ ํด์ค๋ค ! )import {useState, useTransition} from 'react'
let a = new Array(10000).fill(0) //์ฑ๋ฅ ์ ํ๋ฅผ ์ผ์ผํค๋ 10,000๊ฐ ๋ฐฐ์ด
function App(){
let [name, setName] = useState('') //์ฌ์ฉ์๊ฐ ์
๋ ฅํ๋ ๋ฐ์ดํฐ
let [isPending, startTransition] = useTransition()
return (
<div>
<input onChange={ (e)=>{
startTransition(()=>{ //์ค๋๊ฑธ๋ฆฌ๋ ์ฝ๋ ๊ฐ์ธ์ฃผ๊ธฐ
setName(e.target.value) //์ฝ๋ ์์์ ๋ค๋ก ๋ฆ์ถฐ์ค๋ค
})
}}/>
{
isPending ? "๋ก๋ฉ์ค..." : // startTransition() ์ผ๋ก ๊ฐ์ผ ์ฝ๋๊ฐ ์ฒ๋ฆฌ์ค์ผ ๋ true๋ก ๋ณํ๋ ๋ณ์
a.map(()=>{ // ๋ณ์ a ๋จผ์ ๋ณด์ฌ์ค
return <div>{name}</div>
})
}
</div>
)
}
useDeferredValue() ์์ state๋ฅผ ๋ฃ์ผ๋ฉด state๊ฐ ๋ณ๋์ฌํญ์ด ์์ ๋ ๋์ค์ ์ฒ๋ฆฌํด์ฃผ๋ ๊ธฐ๋ฅ์ด๋ค (startTransition๊ณผ ์ฉ๋๊ฐ ๋๊ฐ๋ค)import {useState, useTransition, useDeferredValue} from 'react'
let state1 = useDeferredValue(name)
โ ํ ํ์ด์ง์ htmlํ๊ทธ๊ฐ ์์ฒญ๋๊ฒ ๋ง์ด ์์ด์ ์ฑ๋ฅ์ด ์ ํ๋๋ค๋ฉด html ํ๊ทธ๋ค์ ํ์ด์ง ์ฌ๋ฌ๊ฐ์ ๋๋ ๋๋ ๊ฒ ์ข๋ค
PWA๋ผ๊ณ ํ๋คPWAํ ์ํค๋ฉด ์ข์ ์ ์ด ๋ช๊ฐ์ง ์๋ค ์ค๋งํธํฐ ๋ฐํํ๋ฉด์ ์น์ฌ์ดํธ ์ค์น๊ฐ ๊ฐ๋ฅํ๊ณ , ์คํ๋ผ์ธ์์๋ ๋์์ด ๊ฐ๋ฅํ๋ค. ๊ทธ๋ฆฌ๊ณ ์คํ ์ด์ ๋ค์ด๊ฐ์ ์ฑ์ ์ค์นํ๊ณ ๋ค์ด๋ฐ๊ฒ ํ๊ธฐ ๊น์ง์ ๋ง์ผํ
๋น์ฉ์ด ๋ง์ด ๋ค์ด๊ฐ๋๋ฐ PWA๋ ๋ฐฉ๋ฌธ์๋ค์๊ฒ ๊ฐ๋จํ ํ์
์ ๋์์ ์ค์น ์ ๋๋ฅผ ํ ์ ์๊ฒ ๋๋ฌธ์ ๋ง์ผํ
๋น์ฉ์ด ์ ๊ฒ ๋ค๊ณ , ๋ฏธ๊ตญ์์๋ ์ค์ ๋ก๋ PWA๋ฅผ ์ด์ฉํ๊ณ ์๋ ์ผํ๋ชฐ๋ค์ด ๋ง๋ค ์๋ ํ์ผ๋ค์ ์๋์ผ๋ก ์์ฑ์ ์ํ๋ค๋ฉด ํ๋ก์ ํธ๋ฅผ ์ฒ์ ๋ง๋ค ๋ ํฐ๋ฏธ๋์ ์
๋ ฅํด์ผ ํ๋ค
โก๏ธ npx create-react-app ํ๋ก์ ํธ๋ช
--template cra template-pwa
manifest.jsonservice-worker.jsํ์ผ ์๋์์ฑ
index.js ํ๋จ์์ ์ฝ๋๋ฅผ ๋ฐ๊ฟ์ฃผ๊ณ ํฐ๋ฏธ๋์ yarn build/npm run build ๋ ์ค ํ๋๋ฅผ ์
๋ ฅํ๋ฉด ํ์ผ์ด ์๋์์ฑ ๋๋ค
// ์ด ์ฝ๋๋ฅผ ์๋์ฒ๋ผ ๋ฐ๊ฟ์ฃผ๊ธฐ
serviceWorkerRegistration.unregister();
//์๋์ฒ๋ผ ๋ฐ๊ฟ์ฃผ๊ธฐ
serviceWorkerRegistration.register();

Cache Storage์ ์ ์ฅ๋์ด ์๋ HTML, CSS, JS ํ์ผ์ ์ฌ์ฉํ๋คํ๋ก์ ํธ๊ฐ PWA์ธ ์ง ํ์ธํ๋ ๋ฐฉ๋ฒ์ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ฐฝ์ผ๋ก ์ฌ์ดํธ์ ์ ์ํด์ ๊ฐ๋ฐ์๋๊ตฌ > Applicationํญ > manifest.json, service-worker.js ํ์ผ์ ๋ํ ๋ด์ฉ์ ํ์ธํ ์ ์๋ค
๋๊ธฐ๋ฐฉ์(synchronous)์ ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋๋ฅผ ์ ์ ์์๋๋ก ์์์ ๋ถํฐ ์๋๋ก ์ฝ๋๊ฐ ์คํ๋๋ ๊ฒ์ ๋งํ๋ค
๋น๋๊ธฐ ๋ฐฉ์(asynchronous)์ ajax, ์ด๋ฒคํธ๋ฆฌ์ค๋, setTimeout ์ด๋ฐ ํจ์๋ค์ ์ฌ์ฉํ ๋ ๋ฐ์ํ๋๋ฐ ํจ์๋ค์ ์ฝ๋ ์คํ์๊ฐ์ด ์ค๋๊ฑธ๋ ค์ ์ฒ๋ฆฌ์๊ฐ์ด ๊ธธ์ด์ง๋ค ๊ทธ๋ ๊ฒ ๋๋ฌธ์ ์์์ ๋ถํฐ ์๋๋ก ์คํ๋์ง ์๊ณ ์๋ฃ๋๋ ๋๋ก ์คํ๋๋ค
setState ํจ์ ํน์ง
// state ๋ณ๊ฒฝํจ์๋ค์ ์ ๋ถ ๋น๋๊ธฐ์ ์ผ๋ก ์ฒ๋ฆฌ๋๋ค (setName)
const [name, setName] = useState('kim')
// setName()์ด ์ค๋๊ฑธ๋ฆฌ๋ฉด ์ด๊ฒ๋ถํฐ ์คํํ๋ ๊ฒ ์๋๋ผ ๋ฐ์ ์๋ ์ฝ๋๋ค ๋ถํฐ ์คํ์ด ๋๋ค
์๋ฒ์ ์ฐ๋ํ๋ ๋ฐฉ๋ฒ์ ์ฝ๊ฒ ๋งํด์ ์ ์ ๊ฐ ๋ฉ์ธํ์ด์ง๋ก ์ ์ํ์ ๋ ๋ฆฌ์กํธ๋ก ๋ง๋ html ํ์ผ์ ์ ์กํด์ฃผ๋ฉด ์ฐ๋์ด ๋๋ค
Node+Express๋ก ์์์๋ฒ ๋ง๋๋ ๋ฐฉ๋ฒ
//server.js
const express = require('express');
const path = require('path');
const app = express();
app.listen(8080, function () {
console.log('listening on 8080')
});
์์ฝ ) ๋ฆฌ์กํธ ํ๋ก์ ํธ ๋ง๋๋ ์์
npx create-react-app ํ๋ก์ ํธ๋ช- ํ๋ก์ ํธ ์์ฑ๋๋ฉด ๋ฆฌ์กํธ ๋ฌธ๋ฒ์ผ๋ก ๋ง๋ค๊ณ ์ถ์ ํ๋ก์ ๋ฅผ ๋ง๋ค๊ณ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๋์ฐ๊ธฐ๋ก ํ์ธํ๊ณ ์ถ์ ๋ (
npm run start) ํฐ๋ฏธ๋์ ์ ๋ ฅํ๋ฉด ๋๋ค- ํ๋ก์ ํธ๊ฐ ์์ฑ๋๋ฉด
npm run build์ ๋ ฅ ํ๋ฉด html ํ์ผ์ด build ํด๋์ ์์ฑ์ด ๋๋ค- html ํ์ผ์ ์๋ฒ์์ ํ์ํ ๋ ์ ์ ์๊ฒ ๋ณด๋ด์ฃผ๋ฉด ๋๋ค
//server.js
//'react-project/build' ์์ฑํด์ผ ํน์ ํด๋์ ํ์ผ์ ์ ์กํ ์ ์๋ค
app.use(express.static(path.join(__dirname, 'react-project/build')));
app.get('/', function (์์ฒญ, ์๋ต) { // ๋ฉ์ธํ์ด์ ์ ์ ์ ๊ฐ ์ ์ํ์ ๋
์๋ต.sendFile(path.join(__dirname, '/react-project/build/index.html'));//ํ์ผ๊ฒฝ๋ก
// '/react-project/build/index.html' > index.html ํ์ผ ๋ณด๋ด์ฃผ๊ธฐ
});
//server.js
app.use(express.static(path.join(__dirname, 'react-project/build')));
app.get('/', function (์์ฒญ, ์๋ต) {
์๋ต.sendFile(path.join(__dirname, '/react-project/build/index.html'));
app.get('*', function (์์ฒญ, ์๋ต) { //์ตํ๋จ์ ์์ฑ (*๋ ๋ชจ๋ ๋ฌธ์๋ฅผ ์๋ฏธ)
์๋ต.sendFile(path.join(__dirname, '/react-project/build/index.html'));
});