โœ๏ธ What I Learned/TIL

[TIL] ๊ฐ„๋‹จํ•œ Recoil ์ฒซ ์‚ฌ์šฉ๊ธฐ

Jiwon() 2023. 8. 14. 20:45

๐Ÿ’ก TIL 20230814

๋ฆฌ์•กํŠธ ํ”„๋กœ์ ํŠธ๋ฅผ ๋นŒ๋“œํ•˜๋ฉด์„œ ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๋‹น์—ฐํ•˜๊ฒŒ๋„(?) Redux๋งŒ ์จ๋ดค์—ˆ๋‹ค. ํ•˜์ง€๋งŒ ์„ธ์ƒ์— ๋‹น์—ฐํ•œ ๊ฒƒ์€ ์—†๋Š” ๋ฒ•.. Recoil, Zustand, Jotai ๋“ฑ ๋‹ค๋ฅธ ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋„ ์จ๋ณด๊ณ  ์‹ถ๋‹ค๋Š” ์ƒ๊ฐ์— ๋จผ์ € Recoil๋ถ€ํ„ฐ ์„ค์น˜ํ•ด์„œ ์‚ฌ์šฉํ•ด๋ณด๊ฒŒ ๋˜์—ˆ๋‹ค.


1. Recoil์ด๋ž€?

Recoil์€ ํŽ˜์ด์Šค๋ถ์ด ๋งŒ๋“  ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค. React ์ „์šฉ์œผ๋กœ ๋‚˜์˜จ ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ ๋งŒํผ ๋ฆฌ์•กํŠธ์—์„œ ์ตœ์ ํ™”๋˜์–ด ์ž‘๋™ํ•œ๋‹ค.

 

์ถœ์ฒ˜ https://recoiljs.org/ko/

์•„๋ž˜๋Š” Recoil ๊ณต์‹ ํ™ˆํŽ˜์ด์ง€์—์„œ ์†Œ๊ฐœํ•˜๋Š” Recoil์˜ ํŠน์ง•์ธ๋ฐ ์„ค๋ช…์ด ๋ญ”๊ฐ€ ๊ท€์—ฝ๋‹ค.

 

์ถœ์ฒ˜ https://www.youtube.com/watch?v=_ISAA_Jt9kI&t=448s

๋ฆฌ์•กํŠธ ๊ณต์‹ ์œ ํŠœ๋ธŒ์—์„œ ์„ค๋ช…ํ•˜๋Š” Atom์˜ ๊ฐœ๋…๋„์ด๋‹ค. redux์—์„œ๋Š” store๋ผ๋Š” ์ค‘์•™ ์ƒํƒœ ๊ด€๋ฆฌ์†Œ์— ๋‹ด์•„๋‘๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ๊ฐœ๋…์ด์—ˆ๋‹ค๋ฉด, ๋ฆฌ์ฝ”์ผ์—์„œ๋Š” ๋ญ”๊ฐ€ ๊ณต์ค‘์— ๋– ๋‹ค๋‹ˆ๋Š” literally ์›์ž๋“ค์˜ ๊ฐœ๋… ๊ฐ™์•„์„œ ๊ท€์—ฝ๋‹ค.

์•”ํŠผ, ์ด atom๋“ค์€ ์–ด๋Š ์ปดํฌ๋„ŒํŠธ์— ํ•œ์ •๋˜์–ด ์žˆ์ง€ ์•Š๊ณ  ๊ณต์ค‘ ์–ด๋”˜๊ฐ€์— ๋– ์žˆ๊ณ , atom ์ค‘ ํ•˜๋‚˜๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋ฉด ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ์—์„œ ๊ณต์ค‘์— ๋– ์žˆ๋Š” atom๋“ค ์ค‘ ํ•„์š”ํ•œ atom์„ ๊ฐ€์ ธ๋‹ค ์“ฐ๋Š” ๊ฐœ๋…์ด๋ผ๊ณ  ์ดํ•ดํ•˜๋ฉด ๋œ๋‹ค.

 

 


2. ์„ธํŒ…

๋‹ค๋ฅธ ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ๊ทธ๋Ÿฌํ•˜๋“ฏ์ด, ์„ค์น˜๋ฅผ ํ•˜๊ณ  ๋ฃจํŠธ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ช‡ ๊ฐ€์ง€ ์„ค์ •์„ ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

 

1) ์„ค์น˜

์•„๋ฌด ์ƒ๊ฐ ์—†์ด yarn add recoil๋งŒ ํ•ด๋†“๊ณ  ์‚ฌ์šฉํ•˜๋Š”๋ฐ ์ž๊พธ ์—๋Ÿฌ๊ฐ€ ๋‚˜์„œ ..์„ค๋งˆ(???) ํ–ˆ๋Š”๋ฐ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—์„œ๋Š” @types/recoil๋„ ํ•จ๊ป˜ ์„ค์น˜ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

yarn add recoil @types/recoil

 

2) index.tsx

  • ๋ฃจํŠธ ์ปดํฌ๋„ŒํŠธ๋ฅผ <RecoilRoot></RecoilRoot>๋กœ ๊ฐ์‹ธ์ฃผ์–ด์•ผ ํ•œ๋‹ค.
import React from 'react';
import ReactDOM from 'react-dom/client';

import { QueryClient, QueryClientProvider } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools';

import { RecoilRoot } from 'recoil';

import App from './App';

const queryClient = new QueryClient();

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
    <RecoilRoot>
      <QueryClientProvider client={queryClient}>
        <App />
        <ReactQueryDevtools initialIsOpen={true} />
      </QueryClientProvider>
    </RecoilRoot>
  </React.StrictMode>
);

Recoil์„ ์‚ฌ์šฉํ•  ์ค€๋น„๋Š” ๋๋‚ฌ๋‹ค. ์•„๋ž˜๋ถ€ํ„ฐ๋Š” ๋‹คํฌ๋ชจ๋“œ ์ƒํƒœ๊ด€๋ฆฌ ์˜ˆ์‹œ๋กœ recoil ์‚ฌ์šฉ๋ฒ•์„ ์ •๋ฆฌํ•˜๋ ค๊ณ  ํ•œ๋‹ค.

 

3) atoms.ts

  • Atom์€ state์˜ ์ผ๋ถ€๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค.
  • Atom๋“ค์€ <RecoilRoot></RecoilRoot>๋กœ ๊ฐ์‹ธ์ง„ ์–ด๋Š ์ปดํฌ๋„ŒํŠธ์—์„œ๋‚˜ ์ฝ๊ณ  ์“ธ ์ˆ˜ ์žˆ๋‹ค.
  • atom์˜ ๊ฐ’์„ ์ฝ๋Š” ์ปดํฌ๋„ŒํŠธ๋“ค์€ ์•”๋ฌต์ ์œผ๋กœ atom์„ ๊ตฌ๋…ํ•œ๋‹ค. ๋”ฐ๋ผ์„œ atom์— ์–ด๋–ค ๋ณ€ํ™”๊ฐ€ ์žˆ์œผ๋ฉด ๊ทธ atom์„ ๊ตฌ๋…ํ•˜๋Š” ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žฌ๋ Œ๋”๋ง๋˜๋Š” ๊ฒฐ๊ณผ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.
  • atom์— ๊ณ ์œ ํ•œ key๋ฅผ ์ฃผ๊ณ , ๊ธฐ๋ณธ๊ฐ’(redux์˜ initialState ๊ฐœ๋…)์ธ default๊ฐ’์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
import { atom } from "recoil";

export const isDarkAtom = atom({
  key: "isDark",
  default: false,
});

 

 


3. ์‚ฌ์šฉ ๋ฐฉ๋ฒ•

React๋ฅผ ์œ„ํ•ด ๋งŒ๋“ค์–ด์ง„ ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ผ ๊ทธ๋Ÿฐ์ง€, ์‚ฌ์šฉ๋ฐฉ๋ฒ•์ด ๋Œ€์ฒด๋กœ useState ํ›…์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ ๋น„์Šทํ–ˆ๋‹ค.

1) useRecoilValue()

  • ์ปดํฌ๋„ŒํŠธ์—์„œ atom์„ ์ฝ๊ธฐ ์œ„ํ•ด useRecoilValue() ํ›…์„ ์‚ฌ์šฉํ•˜์—ฌ ์•„๋ž˜์™€ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ๋œ๋‹ค.
  • useRecoilValue(์•ˆ์—๋Š” Atom์„ ๋„ฃ์–ด์ค€๋‹ค)
import { ThemeProvider } from 'styled-components';
import { darkTheme, lightTheme } from './styles/theme';
import GlobalStyle from "./styles/GlobalStyle";
import Router from "./routes/Router";

import { useRecoilValue, useSetRecoilState } from "recoil";
import { isDarkAtom } from './recoil/atoms';

import styled from "styled-components";
import ToggleButton from "react-dark-mode-toggle";

const App = () => {
  const isDark = useRecoilValue(isDarkAtom); // ์„ค์ •ํ•ด์ค€ atom ๊ฐ€์ ธ์˜ค๊ธฐ
  
  const toggleDarkAtom = () => setDarkAtom(prev => !prev)
  return (
    <>
      <ThemeProvider theme={isDark ? darkTheme : lightTheme}>
        <GlobalStyle />
        <DarkModeToggleBtn
          checked={isDark}
          size={50}
          speed={1.5}
        />
        <Router />
      </ThemeProvider>
    </>
  );
};

export default App;

const DarkModeToggleBtn = styled(ToggleButton)`
  position: absolute;
  top: 3vh;
  right: 3%;
`

 

2) useSetRecoilState()

  • ์ด์ œ ํ† ๊ธ€ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅผ ๋•Œ, ์ด atom์˜ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ธฐ ์œ„ํ•ด์„œ useSetRecoilState()ํ›…์„ ์‚ฌ์šฉํ•˜์—ฌ setter ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด์ฃผ๋ฉด ๋œ๋‹ค.
  • useSetRecoilState()๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ atom์„ ๋ฐ›๊ณ , atom์„ ๋ณ€๊ฒฝํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • React์˜ ์ผ๋ฐ˜์ ์ธ setState ํ•จ์ˆ˜์™€ ๊ฐ™์€ ์›๋ฆฌ๋กœ ์ž‘๋™ํ•œ๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
import { ThemeProvider } from 'styled-components';
import { darkTheme, lightTheme } from './styles/theme';
import GlobalStyle from "./styles/GlobalStyle";
import Router from "./routes/Router";

import { useRecoilValue, useSetRecoilState } from "recoil";
import { isDarkAtom } from './recoil/atoms';

import styled from "styled-components";
import ToggleButton from "react-dark-mode-toggle";

const App = () => {
  const isDark = useRecoilValue(isDarkAtom);
  const setDarkAtom = useSetRecoilState(isDarkAtom); // setter ํ•จ์ˆ˜
  const toggleDarkAtom = () => setDarkAtom(prev => !prev) // setter ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ ํ† ๊ธ€ํ•จ์ˆ˜ ์ •์˜
  return (
    <>
      <ThemeProvider theme={isDark ? darkTheme : lightTheme}>
        <GlobalStyle />
        <DarkModeToggleBtn
          onChange={toggleDarkAtom}
          checked={isDark}
          size={50}
          speed={1.5}
        />
        <Router />
      </ThemeProvider>
    </>
  );
};

export default App;

const DarkModeToggleBtn = styled(ToggleButton)`
  position: absolute;
  top: 3vh;
  right: 3%;
`

 

 


์ฐธ๊ณ  ์‚ฌ์ดํŠธ

Recoil ๊ณต์‹ ํ•œ๊ตญ์–ด ๋ฌธ์„œ