Frontend ๐Ÿ“š/React

[์ฝ”๋”ฉ์• ํ”Œ] ์‡ผํ•‘๋ชฐ ํ”„๋กœ์ ํŠธ Part8 - localStorage, react-query

leejaejae 2023. 10. 29. 14:55

โœ๏ธ localStorage

- ์ƒˆ๋กœ๊ณ ์นจํ•˜๋ฉด ๋ธŒ๋ผ์šฐ์ €๋Š” html css js ํŒŒ์ผ๋“ค์„ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋‹ค์‹œ ์ฝ๊ธฐ ๋•Œ๋ฌธ์— ๋ชจ๋“  state ๋ฐ์ดํ„ฐ๋Š” ๋ฆฌ์…‹๋œ๋‹ค.
- ์ด๊ฑธ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” state ๋ฐ์ดํ„ฐ๋Š” ์„œ๋ฒ„๋กœ ๋ณด๋‚ด DB์— ์ €์žฅํ•˜๊ฑฐ๋‚˜ LocalStorage์— ์ €์žฅํ•˜๋Š”๋ฐ
- ์˜ค๋Š˜ ๋ฐฐ์šธ ๋‚ด์šฉ์€ LocalStorage์— ๊ด€ํ•ด์„œ์ด๋‹ค.

- LocalStorage๋Š” ์‰ฝ๊ฒŒ ๋งํ•ด ์œ ์ €์˜ ๋ธŒ๋ผ์šฐ์ €์— ๋ชฐ๋ž˜ ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์“ฐ๋Š” ๊ณต๊ฐ„์ธ๋ฐ ์œ ์ €๊ฐ€ ๋ธŒ๋ผ์šฐ์ € ์ฒญ์†Œ๋ฅผ ํ•˜์ง€ ์•Š๋Š” ์ด์ƒ ์˜๊ตฌ์ ์œผ๋กœ ๋‚จ์•„์žˆ๋‹ค.

ํฌ๋กฌ๊ฐœ๋ฐœ์ž ๋„๊ตฌ์—์„œ Application ํƒญ์— ๋“ค์–ด๊ฐ€๋ฉด ๋ณผ ์ˆ˜ ์žˆ๋‹ค

- ์‚ฌ์ดํŠธ๋งˆ๋‹ค 5MB ์ •๋„์˜ ๋ฌธ์ž์—ด์„ ์ €์žฅํ•  ์ˆ˜ ์žˆ๊ณ 
- object ์ž๋ฃŒ์™€ ๋น„์Šทํ•˜๊ฒŒ key/value ํ˜•ํƒœ๋กœ ์ €์žฅํ•œ๋‹ค.

โœ๏ธ localStorage ๋ฌธ๋ฒ• 
- ๊ทธ๋ƒฅ js ํŒŒ์ผ ์•„๋ฌด๋ฐ์„œ๋‚˜ ๋‹ค์Œ ๋ฌธ๋ฒ•์„ ์“ฐ๋ฉด localStorage์— ๋ฐ์ดํ„ฐ ์ž…์ถœ๋ ฅํ•  ์ˆ˜ ์žˆ๋‹ค.

localStorage.setItem('๋ฐ์ดํ„ฐ์ด๋ฆ„', '๋ฐ์ดํ„ฐ');  // ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€
localStorage.getItem('๋ฐ์ดํ„ฐ์ด๋ฆ„');  // ๋ฐ์ดํ„ฐ ์ฝ๊ธฐ
localStorage.removeItem('๋ฐ์ดํ„ฐ์ด๋ฆ„')  // ๋ฐ์ดํ„ฐ ์‚ญ์ œ

 

โœ๏ธ localStorage์— array/object ์ž๋ฃŒ๋ฅผ ์ €์žฅํ•˜๋ ค๋ฉด
- ๋ฌธ์ž๋งŒ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋Š” ๊ณต๊ฐ„์ด๋ผ array/object๋ฅผ ๋ฐ”๋กœ ์ €์žฅํ•  ์ˆ˜๋Š” ์—†๋‹ค.
- ๊ฐ•์ œ๋กœ ์ €์žฅํ•ด๋ณด๋ฉด ๋ฌธ์ž๋กœ ๋ฐ”๊ฟ”์„œ ์ €์žฅํ•ด์ฃผ๋Š”๋ฐ ๊ทธ๋Ÿผ array/object ์ž๋ฃŒ๊ฐ€ ๊นจ์ ธ์„œ ์ €์žฅ๋œ๋‹ค.
- ๊ทธ๋ž˜์„œ ํŽธ๋ฒ•์œผ๋กœ๋Š” array/object๋ฅผ JSON์œผ๋กœ ๋ณ€ํ™˜์‹œ์ผœ ์ €์žฅํ•œ๋‹ค.

// array/object -> JSON
localStorage.setItem('obj', JSON.stringify({name:'kim'}) );  // "{"name":"kim"}"

// JSON -> array/object
var a = localStorage.getItem('obj');
var b = JSON.parse(a)  // JSON -> array/object

- JSON.stringify() ๋ผ๋Š” ํ•จ์ˆ˜์— array/object๋ฅผ ์ง‘์–ด๋„ฃ์œผ๋ฉด ๊ทธ ์ž๋ฆฌ์— JSON์œผ๋กœ ๋ณ€ํ™˜๋œ๊ฑธ ๋‚จ๊ฒจ์ค€๋‹ค.
- ๋‹ค์‹œ JSON -> array/object๋กœ ๋ณ€ํ™˜ํ•˜๊ณ  ์‹ถ์œผ๋ฉด JSON.parse() ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.


๐Ÿ’‍โ™€๏ธ ์ตœ๊ทผ ๋ณธ ์ƒํ’ˆ UI ๊ธฐ๋Šฅ ๋งŒ๋“ค๊ธฐ
1. ๋ˆ„๊ฐ€ DetailํŽ˜์ด์ง€ ์ ‘์†ํ•˜๋ฉด
2. ํ˜„์žฌ ํŽ˜์ด์ง€์— ๋ณด์ด๋Š” ์ƒํ’ˆid ๊ฐ€์ ธ์™€์„œ
3. localStorage์— watchํ•ญ๋ชฉ์— ์žˆ๋˜ [ ] ์— ์ถ”๊ฐ€

// Detail.js

useEffect(()=>{
  let ๊บผ๋‚ธ๊ฑฐ = localStorage.getItem('watched')
  ๊บผ๋‚ธ๊ฑฐ = JSON.parse(๊บผ๋‚ธ๊ฑฐ)
  ๊บผ๋‚ธ๊ฑฐ.push(์ฐพ์€์ƒํ’ˆ.id)

  //Set์œผ๋กœ ๋ฐ”๊ฟจ๋‹ค๊ฐ€ ๋‹ค์‹œ array๋กœ ๋งŒ๋“ค๊ธฐ -> ์ค‘๋ณต ์ œ๊ฑฐ
  ๊บผ๋‚ธ๊ฑฐ = new Set(๊บผ๋‚ธ๊ฑฐ)
  ๊บผ๋‚ธ๊ฑฐ = Array.from(๊บผ๋‚ธ๊ฑฐ)
  localStorage.setItem('watched', JSON.stringify(๊บผ๋‚ธ๊ฑฐ))
}, [])

 

โœ๏ธ localStorage์— state๋ฅผ ์ž๋™์ €์žฅ๋˜๊ฒŒ ๋งŒ๋“ค๊ณ  ์‹ถ์œผ๋ฉด
- redux-persist ์ด๋Ÿฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜ํ•ด์„œ ์“ฐ๋ฉด redux store ์•ˆ์— ์žˆ๋Š” state๋ฅผ ์ž๋™์œผ๋กœ localStorage์— ์ €์žฅํ•ด์ค€๋‹ค
- state ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ๊ทธ์— ๋งž๊ฒŒ localStorage ์—…๋ฐ์ดํŠธ๋„ ์•Œ์•„์„œ ํ•ด์คŒ
- ํ•˜์ง€๋งŒ ์…‹ํŒ…๋ฌธ๋ฒ• ๋ณต์žกํ•˜๊ณ  ๊ท€์ฐฎ๋‹ค.

- ๊ทธ๋ž˜์„œ ์š”์ฆ˜์€ ์‹ ๊ทœ ์‚ฌ์ดํŠธ๋“ค์€ Redux ๋Œ€์‹  Jotai, Zustand ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
- ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š”๋ฐ ์…‹ํŒ…๋„ ๊ฑฐ์˜ ํ•„์š”์—†๊ณ  ๋ฌธ๋ฒ•์ด ํ›จ์”ฌ ๋” ์‰ฝ๊ณ  localStorage ์ž๋™์ €์žฅ๊ธฐ๋Šฅ๋“ค๋„ ์žˆ๋‹ค.



โœ๏ธ react-query

! ์—…๋ฐ์ดํŠธ ์‚ฌํ•ญ
→ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ด๋ฆ„์ด react-query์—์„œ @tanstack/react-query๋กœ ๋ฐ”๊ฟ”์–ด์„œ
1. ์„ค์น˜์‹œ์—” ์ด๋ ‡๊ฒŒ ์ž…๋ ฅ

npm install @tanstack/react-query@3

2. import ํ•ด์„œ ์‚ฌ์šฉํ•  ๋• ์ด๋ ‡๊ฒŒ ์ž…๋ ฅ

import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query'

3. useQuery ์“ธ ๋• '์ž‘๋ช…' ๋ง๊ณ  ['์ž‘๋ช…']

useQuery(['์ž‘๋ช…'],

 

๐Ÿ’‍โ™€๏ธ ajax ์š”์ฒญํ•˜๋‹ค๋ณด๋ฉด ์ด๋Ÿฐ ๊ธฐ๋Šฅ๋“ค์ด ๊ฐ€๋” ํ•„์š”ํ•˜๋‹ค
→ ๋ช‡์ดˆ๋งˆ๋‹ค ์ž๋™์œผ๋กœ ๋ฐ์ดํ„ฐ ๋‹ค์‹œ ๊ฐ€์ ธ์˜ค๊ฒŒ ํ•˜๋ ค๋ฉด?
→ ์š”์ฒญ ์‹คํŒจ์‹œ ๋ช‡์ดˆ ๊ฐ„๊ฒฉ์œผ๋กœ ์žฌ์‹œ๋„?
→ ๋‹ค์Œ ํŽ˜์ด์ง€ ๋ฏธ๋ฆฌ๊ฐ€์ ธ์˜ค๊ธฐ?
→ ajax ์„ฑ๊ณต/์‹คํŒจ์‹œ ๊ฐ๊ฐ ๋‹ค๋ฅธ html์„ ๋ณด์—ฌ์ฃผ๋ ค๋ฉด?

์ด๋Ÿด ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด react-query ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.

โœ๏ธ react-query ์„ค์น˜ & ์…‹ํŒ…
- ํ„ฐ๋ฏธ๋„์—์„œ npm install react-query ํ•˜๊ณ  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 ์š”์ฒญํ•˜๋Š” ๋ฒ•
- ๊ทธ๋ƒฅ ajax ์š”์ฒญํ•ด๋„ ๋˜๋Š”๋ฐ react-query๋ฅผ ์จ์„œ ajax ์š”์ฒญ ๋‚ ๋ฆฌ๋ฉด ๋” ํŽธ๋ฆฌํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค.

// ์ƒ๋‹จ์— useQuery import ํ•ด์˜จ ๋’ค

function App(){
  let result = useQuery('์ž‘๋ช…', ()=>
    axios.get('https://codingapple1.github.io/userdata.json')
    .then((a)=>{ return a.data })
  )
}

 

์žฅ์ 1. ajax ์š”์ฒญ ์„ฑ๊ณต/์‹คํŒจ/๋กœ๋”ฉ์ค‘ ์ƒํƒœ๋ฅผ ์‰ฝ๊ฒŒ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ๋‹ค.

function App(){
  let result = useQuery('์ž‘๋ช…', ()=>
    axios.get('https://codingapple1.github.io/userdata.json')
    .then((a)=>{ return a.data })
  )

  return (
    <div>
      { result.isLoading && '๋กœ๋”ฉ์ค‘' }
      { result.error && '์—๋Ÿฌ๋‚จ' }
      { result.data && result.data.name }
    </div>
  )
}

- result๋ผ๋Š” ๋ณ€์ˆ˜์— ajax ํ˜„์žฌ ์ƒํƒœ๊ฐ€ ์•Œ์•„์„œ ์ €์žฅ๋œ๋‹ค.
→ ajax ์š”์ฒญ์ด ๋กœ๋”ฉ์ค‘์ผ ๋• result.isLoading์ด true๊ฐ€ ๋œ๋‹ค.
→ ajax ์š”์ฒญ์ด ์‹คํŒจ์‹œ์—” result.error๊ฐ€ true๊ฐ€ ๋œ๋‹ค.
→ ajax ์š”์ฒญ์ด ์„ฑ๊ณต์‹œ์—” result.data ์•ˆ์— ๋ฐ์ดํ„ฐ๊ฐ€ ๋“ค์–ด์˜จ๋‹ค.

- ๊ทธ๋ž˜์„œ "ajax ๋กœ๋”ฉ์ค‘์ผ ๋• <A /> ๋ณด์—ฌ์ฃผ์„ธ์š”!", "ajax ์„ฑ๊ณต์‹œ์—” <B /> ๋ณด์—ฌ์ฃผ์„ธ์š”!" ์ง์ ‘ ๊ฐœ๋ฐœํ•  ๋• state๋ถ€ํ„ฐ ๋งŒ๋“ค์–ด์•ผ ํ–ˆ์—ˆ๋Š”๋ฐ ์–˜๋Š” ๊ทธ๋Ÿด ํ•„์š”๊ฐ€ ์—†๋‹ค.

์žฅ์ 2. ์•Œ์•„์„œ ajax ์žฌ์š”์ฒญํ•ด์ค€๋‹ค.
- ํŽ˜์ด์ง€ ์ฒด๋ฅ˜ํ•˜๊ณ ๋‚˜์„œ ์ผ์ •์‹œ๊ฐ„์ด ๊ฒฝ๊ณผํ•˜๊ฑฐ๋‚˜,๋‹ค๋ฅธ ์ฐฝ์œผ๋กœ ๊ฐ”๋‹ค๊ฐ€ ๋‹ค์‹œ ํŽ˜์ด์ง€๋กœ ๋Œ์•„์˜ค๊ฑฐ๋‚˜, ๋‹ค์‹œ ๋ฉ”์ธํŽ˜์ด์ง€๋กœ ๋Œ์•„๊ฐ€๊ฑฐ๋‚˜.
- ์ด๋Ÿฐ ์—ฌ๋Ÿฌ ๊ฒฝ์šฐ์— ์•Œ์•„์„œ ajax ์š”์ฒญ์„ ๋‹ค์‹œ ํ•ด์ค€๋‹ค. (์žฌ์š”์ฒญ ๋„๋Š” ๋ฒ•, ์žฌ์š”์ฒญ๊ฐ„๊ฒฉ ์กฐ์ ˆํ•˜๋Š” ๋ฒ•๋„ ์žˆ์Œ)

์žฅ์ 3. ์‹คํŒจ์‹œ ์žฌ์‹œ๋„ ์•Œ์•„์„œ ํ•ด์ค€๋‹ค.
- ์ž ๊น ์ธํ„ฐ๋„ท์ด ๋Š๊ฒผ๊ฑฐ๋‚˜ ์„œ๋ฒ„๊ฐ€ ์ฃฝ์—ˆ๊ฑฐ๋‚˜ ๊ทธ๋Ÿฌ๋ฉด ajax ์š”์ฒญ์ด ์‹คํŒจํ•œ๋‹ค.
- ์‹คํŒจํ–ˆ์„ ๋•Œ๋Š” ์–˜๊ฐ€ ์•Œ์•„์„œ 4-5ํšŒ ์ •๋„ ์žฌ์‹œ๋„๋ฅผ ํ•ด์ค˜์„œ ํŽธ๋ฆฌํ•˜๋‹ค.

์žฅ์ 4. ajax๋กœ ๊ฐ€์ ธ์˜จ ๊ฒฐ๊ณผ๋Š” state ๊ณต์œ  ํ•„์š”์—†๋‹ค.
- ์ง€๊ธˆ App ์ปดํฌ๋„ŒํŠธ์—์„œ ์œ ์ €์ด๋ฆ„ ๊ฐ€์ ธ์˜ค๋Š” ajax ์š”์ฒญ์„ ๋‚ ๋ฆฌ๊ณ  ์žˆ๋Š”๋ฐ
- ๊ทธ ์œ ์ € ์ด๋ฆ„ ๊ฒฐ๊ณผ๊ฐ€ Detail ์ปดํฌ๋„ŒํŠธ์—๋„ ํ•„์š”ํ•˜๋ฉด ์œ ์ €์ด๋ฆ„์„ props๋กœ ์ „์†กํ•ด์•ผํ•œ๋‹ค.
- ํ•˜์ง€๋งŒ props ์ „์†กํ•  ํ•„์š”์—†์ด Detail ์ปดํฌ๋„ŒํŠธ์—๋‹ค๊ฐ€ ์œ ์ €์ด๋ฆ„ ajax ์š”์ฒญํ•˜๋Š” ์ฝ”๋“œ ๋˜‘๊ฐ™์ด ๋˜ ์ ์œผ๋ฉด ๋œ๋‹ค.

- react-query๋Š” ajax ์š”์ฒญ์ด 2๊ฐœ๋‚˜ ์žˆ์œผ๋ฉด 1๊ฐœ๋งŒ ๋‚ ๋ ค์ฃผ๊ณ 
- ์บ์‹ฑ ๊ธฐ๋Šฅ์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฏธ ๊ฐ™์€ ajax ์š”์ฒญ์„ ํ•œ ์ ์ด ์ž‡์œผ๋ฉด ๊ทธ๊ฑธ ์šฐ์„  ๊ฐ€์ ธ์™€์„œ ์‚ฌ์šฉํ•œ๋‹ค.


๐Ÿ’‍โ™€๏ธ react-query๊ฐ€ ์ฃผ์žฅํ•˜๋Š” ์žฅ์ ์€
- server-state (DB ๋ฐ์ดํ„ฐ)๋ฅผ ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ์‹ค์‹œ๊ฐ„ ๋™๊ธฐํ™”ํ•ด์ฃผ๋Š”๊ฑธ ๋„์™€์ค€๋‹ค๊ณ  ํ•œ๋‹ค.
- ๊ทผ๋ฐ ajax ์š”์ฒญ์„ ๋ช‡์ดˆ๋งˆ๋‹ค ๊ณ„์† ๋‚ ๋ ค์„œ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ์‹์ด๋ผ http1์„ ์“ฐ๋Š” ์„œ๋ฒ„๋‚˜ ๋ธŒ๋ผ์šฐ์ €๋ผ๋ฉด ์ข€ ๋น„ํšจ์œจ์ ์ผ ์ˆ˜๋„ ์žˆ๋‹ค.

- ์‹ค์‹œ๊ฐ„์œผ๋กœ ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ž์ฃผ ๋ณด๋‚ด๋ ค๋ฉด ์›น์†Œ์ผ“์ด๋‚˜ server-sent events ๊ฐ™์€ ๊ฐ€๋ฒผ์šด ๋ฐฉ์‹๋„ ์žˆ๋‹ค.
- ๊ทธ๋ž˜์„œ react-query๋Š” ajax ๊ด€๋ จ ๊ธฐ๋Šฅ๊ฐœ๋ฐœ์„ ํŽธํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ์— ์˜์˜๊ฐ€ ์žˆ๋‹ค.


๐Ÿ’‍โ™€๏ธ
RTK Query ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋„ ์žˆ๋‹ค
- ajax ์š”์ฒญ ํ›„ Resux state ๋ณ€๊ฒฝํ•˜๊ณ  ์‹ถ์œผ๋ฉด Slice ์•ˆ์—์„œ ๊ด€๋ฆฌ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋„์™€์ค€๋‹ค.
- ๊ทธ๋ฆฌ๊ณ  ajax ์š”์ฒญํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ 100๋งŒ๊ฐœ ์žˆ์œผ๋ฉด ๊ทธ๊ฑธ ํŽธ๋ฆฌํ•˜๊ฒŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์ž‡๊ฒŒ ๋„์™€์ค€๋‹ค.(๋‹จ ์ฝ”๋“œ๊ฐ€ ์กฐ๊ธˆ ๋”๋Ÿฌ์›Œ์งˆ ์ˆ˜๋„..!)


๋ณธ ํฌ์ŠคํŒ…์€ << React ๋ฆฌ์•กํŠธ ๊ธฐ์ดˆ๋ถ€ํ„ฐ ์‡ผํ•‘๋ชฐ ํ”„๋กœ์ ํŠธ๊นŒ์ง€! >> ๊ฐ•์˜๋ฅผ ์ฐธ๊ณ ํ•ฉ๋‹ˆ๋‹ค.