[React ์ฝ”๋”ฉ์• ํ”Œ๐ŸŽ] ํ”„๋กœ์ ํŠธ shop

์ด๋‹ค์˜ยท2024๋…„ 7์›” 1์ผ

React

๋ชฉ๋ก ๋ณด๊ธฐ
11/31

๐ŸŽ LocalStorage

  • ํŽ˜์ด์ง€ ์ƒˆ๋กœ๊ณ ์นจ ํ–ˆ์„ ๋•Œ state ์ดˆ๊ธฐ๊ฐ’์œผ๋กœ ๋Œ์•„๊ฐ€๋Š”๋ฐ ๊ทธ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด state๋ฅผ ์„œ๋ฒ„๋กœ ๋ณด๋‚ด์„œ DB์— ์˜๊ตฌ์ €์žฅ ํ•˜๋Š” ๊ฒƒ
  1. key/value ํ˜•ํƒœ๋กœ ์ €์žฅ๊ฐ€๋Šฅ
  2. ์ตœ๋Œ€ 5MB๊นŒ์ง€ ๋ฌธ์ž ์ €์žฅ์ด ๊ฐ€๋Šฅํ•˜๋‹ค
  3. ์‚ฌ์ดํŠธ ์žฌ์ ‘์†ํ•ด๋„ ๋‚จ์•„์žˆ์œผ๋‚˜ ๋ธŒ๋ผ์šฐ์ € ์ฒญ์†Œํ•˜๊ฒŒ ๋˜๋ฉด ์‚ญ์ œ๋œ๋‹ค

โญ LocalStorage ์™€ SessionStorage ์ฐจ์ด

  • LocalStorage๋Š” ์žฌ์ ‘์†ํ•ด๋„ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‚จ์•„์žˆ์œผ๋‚˜ SessionStorage๋Š” ๋ธŒ๋ผ์šฐ์ €๋ฅผ ๊ป์„ ๋•Œ ๋ฐ์ดํ„ฐ๊ฐ€ ์‚ฌ๋ผ์ง„๋‹ค

โญ LocalStorage ๋ฐ์ดํ„ฐ ์ €์žฅ/์ถœ๋ ฅ/์‚ญ์ œ

//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); 

๐ŸŽ React Query

  • ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณ„์† ๊ฐ€์ ธ์™€์•ผ ํ•˜๋Š” ์‚ฌ์ดํŠธ๋ฅผ ๋งŒ๋“ค ๋•Œ ์‚ฌ์šฉํ•˜๋ฉด ์ข‹๋‹ค

โญ <React Query ์žฅ์ >

  • ajax ์„ฑ๊ณต/์‹คํŒจ/๋กœ๋”ฉ์ค‘ ์ผ ๋•Œ ํŒŒ์•…ํ•˜๊ธฐ ์‰ฝ๋‹ค
  • ajax ์š”์ฒญ์„ ์•Œ์•„์„œ ์žฌ์š”์ฒญ ํ•ด์ค€๋‹ค
  • ์‹คํŒจ ์‹œ ์žฌ์‹œ๋„ ํ•ด์ค€๋‹ค
  • ajax๋กœ ๊ฐ€์ ธ์˜จ ๊ฒฐ๊ณผ๋Š” state ๊ณต์œ ๋ฅผ ํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค

โญ <React Query ์„ค์น˜>

  1. ์„ค์น˜๋ฐฉ๋ฒ• โžก๏ธ npm install react-query@3
  2. react-query๋กœ import ํ•˜๊ธฐ
import { QueryClient, QueryClientProvider, useQuery } from 'react-query' //import ํ•˜๊ธฐ
  1. ์ž‘๋ช…ํ•˜๊ธฐ
useQuery(['์ž‘๋ช…'], // [] ์ƒ๋žตํ•˜๋ฉด ์•ˆ ๋จ
  1. index.js ํŒŒ์ผ์— ์„œ 1๋ฒˆ, 2๋ฒˆ, 3๋ฒˆ ์ž‘์„ฑํ•˜๊ธฐ
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>
); 

โญ <React Query๋กœ Ajax ์š”์ฒญํ•˜๋Š” ๋ฒ• >

//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>

๐ŸŽ lazy import

  • ๋ฆฌ์•กํŠธ๋Š” 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"));
  • lazy ๋ฌธ๋ฒ•์€ ๋˜‘๊ฐ™์ด import๊ฐ€ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ Detail, CartํŒŒ์ผ์ด ํ•„์š” ํ•  ๋•Œ import๊ฐ€ ๋˜๊ฒŒ ํ•ด์ค€๋‹ค. ์ด๋ ‡๊ฒŒ ์ž‘์„ฑํ•˜๊ฒŒ ๋˜๋ฉด ์ฒซ ํŽ˜์ด์ง€ ๋กœ๋”ฉ์†๋„๋ฅผ ๋น ๋ฅด๊ฒŒ ํ–ฅ์ƒ ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค

  • lazy ์‚ฌ์šฉํ•˜๋ฉด ์ปดํฌ๋„ŒํŠธ ๋กœ๋“œ๊นŒ์ง€ ์ง€์—ฐ์‹œ๊ฐ„์ด ์ƒ๊ธธ ์ˆ˜๋„ ์žˆ๋‹ค ๊ทธ๋Ÿด ๋•Œ์—๋Š” ์•„๋ž˜ ์ฝ”๋“œ ์ฒ˜๋Ÿผ ์ง€์—ฐ์‹œ๊ฐ„๋™์•ˆ ๋ณด์—ฌ์งˆ html์„ ๋งŒ๋“ค๋ฉด ๋œ๋‹ค
<Suspense fallback={<div>๋กœ๋”ฉ ์ค‘ ์ž…๋‹ˆ๋‹ค ...</div>}> //์ง€์—ฐ์‹œ๊ฐ„ ๋ฐœ์ƒํ•  ๋•Œ ๋กœ๋”ฉ์ค‘์ž…๋‹ˆ๋‹ค ํ™”๋ฉด ๋ณด์—ฌ์ง 
  <Routes>
	<Route
      path="/detail/:id"
      element={<Detail shoes={shoes} />
	<Route 
	  path="cart" 
	  element={<Cart />} />
  </Routes>
</Suspense>
  • Detail ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ง€์—ฐ ์ค‘ ์ผ๋•Œ๋งŒ ๋ณด์—ฌ์ง€๊ฒŒ ํ•ด๋„ ๋˜์ง€๋งŒ <Suspense>๋กœ <Routes>๋ฅผ ์ „๋ถ€ ๊ฐ์‹ธ๋„ ๋œ๋‹ค

๐ŸŽ memo

  • 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>

๐ŸŽ usememo

  • useEffect์™€ ๋น„์Šทํ•œ ์šฉ๋„์ด๋‹ค. ๋‹ค๋ฅธ ์ ์€ ์‹คํ–‰์‹œ์ ์˜ ์ฐจ์ด ๋ฐ–์— ์—†๋‹ค usememo๊ฐ€ ์‹คํ–‰ ๋  ๋•Œ ํ•จ์ˆ˜๋„ ๋™์‹œ์— ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ปดํฌ๋„ŒํŠธ ์ƒ์„ฑ ์‹œ 1ํšŒ๋งŒ ์‹คํ–‰ํ•˜๊ณ  ์‹ถ์€ ์ฝ”๋“œ๊ฐ€ ์žˆ์„ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค
  • dependency๋„ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํŠน์ • state, props๊ฐ€ ๋ณ€ํ•  ๋•Œ๋งŒ ์‹คํ–‰ ํ•  ์ˆ˜๋„ ์žˆ๋‹ค

๐ŸŽ automatic batching

  • ๋ฆฌ์•กํŠธ์—์„œ๋Š” state ๋ณ€๊ฒฝํ•จ์ˆ˜๊ฐ€ ์—ฐ๋‹ฌ์•„์„œ 3๊ฐœ์ด์ƒ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ–ˆ์„ ๋•Œ ๋ฆฌ๋ Œ๋”๋ง์ด 3๋ฒˆ ๋˜์–ด์•ผ ํ•˜์ง€๋งŒ ๋ฆฌ๋ Œ๋”๋ง์„ ๋งˆ์ง€๋ง‰์— 1ํšŒ๋งŒ ์ฒ˜๋ฆฌํ•ด์ฃผ๋Š” ๋ฆฌ๋ Œ๋”๋ง ๋ฐฉ์ง€ ๊ธฐ๋Šฅ์ด๋‹ค

    โ—๋ฆฌ์•กํŠธ 17๋ฒ„์ „์—์„œ๋Š” ajax์š”์ฒญ, setTimeout์•ˆ์— state ๋ณ€๊ฒฝํ•จ์ˆ˜๊ฐ€ ์—ฌ๋Ÿฌ๊ฐœ ์žˆ๋Š” ๊ฒฝ์šฐ์—๋Š” batching์ด ์ผ์–ด๋‚˜์ง€ ์•Š๋Š”๋‹ค
    ํ•˜์ง€๋งŒ โ—๋ฆฌ์•กํŠธ 18๋ฒ„์ „์—์„œ๋Š” batching์ด ์ ์šฉ๋œ๋‹ค

๐ŸŽ useTransition, useDeferredValue

  • ๋ฆฌ์•กํŠธ 18๋ฒ„์ „ ์ดํ›„๋ถ€ํ„ฐ ์ถ”๊ฐ€๋œ ๊ธฐ๋Šฅ์ด๋‹ค. ๋ Œ๋”๋ง ์„ฑ๋Šฅ์ด ์ €ํ•˜๋˜๋Š” ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค

โญ useTransition

  • ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์ปดํฌ๋„ŒํŠธ ๋ถ€๋ถ„์„ 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

  • useDeferredValue() ์•ˆ์— state๋ฅผ ๋„ฃ์œผ๋ฉด state๊ฐ€ ๋ณ€๋™์‚ฌํ•ญ์ด ์žˆ์„ ๋•Œ ๋‚˜์ค‘์— ์ฒ˜๋ฆฌํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์ด๋‹ค (startTransition๊ณผ ์šฉ๋„๊ฐ€ ๋˜‘๊ฐ™๋‹ค)
import {useState, useTransition, useDeferredValue} from 'react'

let state1 = useDeferredValue(name)

โž• ํ•œ ํŽ˜์ด์ง€์— htmlํƒœ๊ทธ๊ฐ€ ์—„์ฒญ๋‚˜๊ฒŒ ๋งŽ์ด ์žˆ์–ด์„œ ์„ฑ๋Šฅ์ด ์ €ํ•˜๋œ๋‹ค๋ฉด html ํƒœ๊ทธ๋“ค์„ ํŽ˜์ด์ง€ ์—ฌ๋Ÿฌ๊ฐœ์— ๋‚˜๋ˆ ๋†“๋Š” ๊ฒŒ ์ข‹๋‹ค


๐ŸŽ PWA (progressive Web App)

  • ์•ˆ๋“œ๋กœ์ด๋“œ/iOS ๋ชจ๋ฐ”์ผ ์•ฑ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“œ๋Š” ์›น๊ฐœ๋ฐœ ๊ธฐ์ˆ ์ด๋‹ค.
  • ์•ฑ์œผ๋กœ ๋ฐœํ–‰ํ•˜๋Š” ๊ฒŒ ์•„๋‹Œ ์›น์‚ฌ์ดํŠธ ์ž์ฒด๋ฅผ ์Šค๋งˆํŠธํฐ ํ™ˆ ํ™”๋ฉด์— ์„ค์น˜ํ•˜๋Š” ๊ฒƒ์„ PWA๋ผ๊ณ  ํ•œ๋‹ค
  • ์›น์‚ฌ์ดํŠธ๋ฅผ PWAํ™” ์‹œํ‚ค๋ฉด ์ข‹์€ ์ ์ด ๋ช‡๊ฐ€์ง€ ์žˆ๋‹ค ์Šค๋งˆํŠธํฐ ๋ฐ”ํƒ•ํ™”๋ฉด์— ์›น์‚ฌ์ดํŠธ ์„ค์น˜๊ฐ€ ๊ฐ€๋Šฅํ•˜๊ณ , ์˜คํ”„๋ผ์ธ์—์„œ๋„ ๋™์ž‘์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์Šคํ† ์–ด์— ๋“ค์–ด๊ฐ€์„œ ์•ฑ์„ ์„ค์น˜ํ•˜๊ณ  ๋‹ค์šด๋ฐ›๊ฒŒ ํ•˜๊ธฐ ๊นŒ์ง€์˜ ๋งˆ์ผ€ํŒ… ๋น„์šฉ์ด ๋งŽ์ด ๋“ค์–ด๊ฐ€๋Š”๋ฐ PWA๋Š” ๋ฐฉ๋ฌธ์ž๋“ค์—๊ฒŒ ๊ฐ„๋‹จํ•œ ํŒ์—…์„ ๋„์›Œ์„œ ์„ค์น˜ ์œ ๋„๋ฅผ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋•Œ๋ฌธ์— ๋งˆ์ผ€ํŒ… ๋น„์šฉ์ด ์ ๊ฒŒ ๋“ค๊ณ , ๋ฏธ๊ตญ์—์„œ๋Š” ์‹ค์ œ๋กœ๋„ PWA๋ฅผ ์ด์šฉํ•˜๊ณ  ์žˆ๋Š” ์‡ผํ•‘๋ชฐ๋“ค์ด ๋งŽ๋‹ค

โญ PWA ๋ฐœํ–‰

  • ์•„๋ž˜ ํŒŒ์ผ๋“ค์„ ์ž๋™์œผ๋กœ ์ƒ์„ฑ์„ ์›ํ•˜๋‹ค๋ฉด ํ”„๋กœ์ ํŠธ๋ฅผ ์ฒ˜์Œ ๋งŒ๋“ค ๋•Œ ํ„ฐ๋ฏธ๋„์— ์ž…๋ ฅํ•ด์•ผ ํ•œ๋‹ค
    โžก๏ธ npx create-react-app ํ”„๋กœ์ ํŠธ๋ช… --template cra template-pwa

    manifest.json service-worker.js ํŒŒ์ผ ์ž๋™์ƒ์„ฑ

  • index.js ํ•˜๋‹จ์—์„œ ์ฝ”๋“œ๋ฅผ ๋ฐ”๊ฟ”์ฃผ๊ณ  ํ„ฐ๋ฏธ๋„์— yarn build/npm run build ๋‘˜ ์ค‘ ํ•˜๋‚˜๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ํŒŒ์ผ์ด ์ž๋™์ƒ์„ฑ ๋œ๋‹ค

// ์ด ์ฝ”๋“œ๋ฅผ ์•„๋ž˜์ฒ˜๋Ÿผ ๋ฐ”๊ฟ”์ฃผ๊ธฐ
serviceWorkerRegistration.unregister();

//์•„๋ž˜์ฒ˜๋Ÿผ ๋ฐ”๊ฟ”์ฃผ๊ธฐ
serviceWorkerRegistration.register();

โญ manifest.json ํŒŒ์ผ

  • ์›น์•ฑ์˜ ์•„์ด์ฝ˜, ์ด๋ฆ„, ํ…Œ๋งˆ์ƒ‰ ๋“ฑ์„ ๊ฒฐ์ •ํ•˜๋Š” ํŒŒ์ผ์ด๋‹ค

โญ service-worker.js

  • ํ•„์š”ํ•œ ์ด๋ฏธ์ง€, ๋ฐ์ดํ„ฐ๋ฅผ ์„œ๋ฒ„์— ์š”์ฒญํ•˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ Cache Storage์— ์ €์žฅ๋˜์–ด ์žˆ๋˜ HTML, CSS, JS ํŒŒ์ผ์„ ์‚ฌ์šฉํ•œ๋‹ค

ํ”„๋กœ์ ํŠธ๊ฐ€ PWA์ธ ์ง€ ํ™•์ธํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ฐฝ์œผ๋กœ ์‚ฌ์ดํŠธ์— ์ ‘์†ํ•ด์„œ ๊ฐœ๋ฐœ์ž๋„๊ตฌ > Applicationํƒญ > manifest.json, service-worker.js ํŒŒ์ผ์— ๋Œ€ํ•œ ๋‚ด์šฉ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค


๐ŸŽ sync / async

  • ๋™๊ธฐ๋ฐฉ์‹(synchronous)์€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๋ฅผ ์ ์€ ์ˆœ์„œ๋Œ€๋กœ ์œ„์—์„œ ๋ถ€ํ„ฐ ์•„๋ž˜๋กœ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋˜๋Š” ๊ฒƒ์„ ๋งํ•œ๋‹ค

  • ๋น„๋™๊ธฐ ๋ฐฉ์‹(asynchronous)์€ ajax, ์ด๋ฒคํŠธ๋ฆฌ์Šค๋„ˆ, setTimeout ์ด๋Ÿฐ ํ•จ์ˆ˜๋“ค์„ ์‚ฌ์šฉํ•  ๋•Œ ๋ฐœ์ƒํ•˜๋Š”๋ฐ ํ•จ์ˆ˜๋“ค์€ ์ฝ”๋“œ ์‹คํ–‰์‹œ๊ฐ„์ด ์˜ค๋ž˜๊ฑธ๋ ค์„œ ์ฒ˜๋ฆฌ์‹œ๊ฐ„์ด ๊ธธ์–ด์ง„๋‹ค ๊ทธ๋ ‡๊ฒŒ ๋•Œ๋ฌธ์— ์œ„์—์„œ ๋ถ€ํ„ฐ ์•„๋ž˜๋กœ ์‹คํ–‰๋˜์ง€ ์•Š๊ณ  ์™„๋ฃŒ๋˜๋Š” ๋Œ€๋กœ ์‹คํ–‰๋œ๋‹ค

  • setState ํ•จ์ˆ˜ ํŠน์ง•

// state ๋ณ€๊ฒฝํ•จ์ˆ˜๋“ค์€ ์ „๋ถ€ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋œ๋‹ค (setName)
const [name, setName] = useState('kim')

// setName()์ด ์˜ค๋ž˜๊ฑธ๋ฆฌ๋ฉด ์ด๊ฒƒ๋ถ€ํ„ฐ ์‹คํ–‰ํ•˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ๋ฐ‘์— ์žˆ๋Š” ์ฝ”๋“œ๋“ค ๋ถ€ํ„ฐ ์‹คํ–‰์ด ๋œ๋‹ค

๐ŸŽ Node+Express ์„œ๋ฒ„์™€ React ์—ฐ๋™

  • ์„œ๋ฒ„์™€ ์—ฐ๋™ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์‰ฝ๊ฒŒ ๋งํ•ด์„œ ์œ ์ €๊ฐ€ ๋ฉ”์ธํŽ˜์ด์ง€๋กœ ์ ‘์†ํ–ˆ์„ ๋•Œ ๋ฆฌ์•กํŠธ๋กœ ๋งŒ๋“  html ํŒŒ์ผ์„ ์ „์†กํ•ด์ฃผ๋ฉด ์—ฐ๋™์ด ๋œ๋‹ค

  • Node+Express๋กœ ์ž„์‹œ์„œ๋ฒ„ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•

  1. node.js ์„ค์น˜
  2. ํด๋” ๋งŒ๋“ค๊ณ  server.js ํŒŒ์ผ ๋งŒ๋“ค๊ณ , ์•„๋ž˜์ฝ”๋“œ ์ž‘์„ฑํ•ด์„œ ์„œ๋ฒ„ ์ƒ์„ฑ
//server.js

const express = require('express');
const path = require('path');
const app = express();

app.listen(8080, function () {
  console.log('listening on 8080')
}); 
  1. ํ„ฐ๋ฏธ๋„ ์ž…๋ ฅ โžก๏ธ npm init -y
  2. ํ„ฐ๋ฏธ๋„ ์ž…๋ ฅ โžก๏ธ npm install express
  • ์›น์„œ๋ฒ„๊ฐ€ ๋งŒ๋“ค์–ด์ง„๋‹ค ์„œ๋ฒ„๋ฅผ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋กœ ํ™•์ธํ•˜๊ณ  ์‹ถ์œผ๋ฉด ํ„ฐ๋ฏธ๋„ ์ž…๋ ฅ โžก๏ธ node server.js

์š”์•ฝ ) ๋ฆฌ์•กํŠธ ํ”„๋กœ์ ํŠธ ๋งŒ๋“œ๋Š” ์ˆœ์„œ

  • 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'));
});

์ฝ”๋”ฉ์• ํ”Œ ๋ฆฌ์•กํŠธ

0๊ฐœ์˜ ๋Œ“๊ธ€