header-img
Info :
728x90

 

ํ˜„์žฌ ํ”„๋กœ์ ํŠธ์˜ ๊ฒฝ์šฐ ๊ถŒํ•œ ๊ด€๋ฆฌ๋ฅผ recoil ๋กœ ๋‹ค๋ฃจ๊ณ  ์žˆ๋‹ค.

โ€‹

Rocoil ์ด๋ž€?

ํŽ˜์ด์Šค๋ถ์—์„œ ๋ฐœํ‘œํ•œ React ์ „์—ญ ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.

usestate ๋งŒ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ํ•˜๊ฒŒ ๋˜๋ฉด ํ”„๋กœ๊ทธ๋žจ์ด ๋ณต์žกํ•˜๊ฒŒ ๋  ์‹œ,

props drilling์ด ์ผ์–ด๋‚˜๊ฒŒ ๋˜๋Š”๋ฐ, Recoil์€ ์ด๋Ÿฌํ•œ ํ–‰์œ„๋ฅผ ๋ฐฉ์ง€ํ•˜๊ฒŒ ํ•ด์ค€๋‹ค.

โ€‹

Recoil์˜ ๊ฒฝ์šฐ. React ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์•„๋‹Œ, ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ์•„๋ž˜์˜ ๋งํฌ์—์„œ ๋‚ด์šฉ์„ ํ™•์ธํ•˜๋ฉด ๋œ๋‹ค.

 

 

Recoil

A state management library for React.

recoiljs.org

Recoil ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์ฃผ์š”ํ•œ ๊ฐœ๋…์œผ๋กœ๋Š” Atom ๊ณผ selector๊ฐ€ ์žˆ๋‹ค.

โ€‹

Recoil ์—์„œ ํ•˜๋‚˜์˜ ์ „์—ญ ์ƒํƒœ๋ฅผ Atom ์ด๋ผ๊ณ  ํ•˜๋Š”๋ฐ, Atom ์— ์žˆ๋Š” ์ƒํƒœ ๊ฐ’๋“ค์€ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ์—์„œ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ๋ฐฑ๊ณผ์‚ฌ์ „ ๊ฐ™์€ ๋Š๋‚Œ.

์œ„์˜ ๊ทธ๋ฆผ์„ ๋ณด๋ฉด ์•Œ ์ˆ˜ ์žˆ๋“ฏ Recoil์€ ๋‹จ๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ํ๋ฆ„์„ ๊ฐ€์ง„๋‹ค.

โ€‹

Atom ์— ๋Œ€ํ•˜์—ฌ ์ •๋ฆฌํ•ด ๋ณด๋ฉด

1. Atom์€ ์ƒํƒœ ๋‹จ์œ„์ด๋ฉฐ, ์—…๋ฐ์ดํŠธ์™€ ์ฐธ์กฐ๊ฐ€ ๊ฐ€๋Šฅํ•จ.

2. Atom์ด ์—…๋ฐ์ดํŠธ ๋˜๋ฉด ๊ฐ๊ฐ์˜ ์ปดํฌ๋„ŒํŠธ๋“ค์€ ์ƒˆ๋กœ์šด ๊ฐ’์„ ๋ฐ˜์˜ํ•˜์—ฌ ๋ฆฌ๋ Œ๋”๋ง ๋จ.

3. ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€ ์ƒํƒœ ๊ฐ’ ๋Œ€์‹  ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋™์ผํ•œ atom์ด ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๊ฒฝ์šฐ ๊ทธ ์ปดํฌ๋„ŒํŠธ๋“ค์€ ์ƒํƒœ๋ฅผ ๊ณต์œ ํ•œ๋‹ค.

 

 

Atom ์ƒ์„ฑ ๋ฐฉ๋ฒ•

const fontSizeState = atom({
  key: 'fontSizeState', // ๊ณ ์œ ํ•œ ํ‚ค ๊ฐ’
  default: 14
});

 

atom์˜ ํ‚ค ๊ฐ’์€ ์ „์—ญ์ ์œผ๋กœ ๊ณ ์œ ํ•ด์•ผ ํ•œ๋‹ค.

์—ฌ๊ธฐ์„œ ๋“ฑ๋ก๋œ ์ „์—ญ ์ƒํƒœ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” useRecoilState(ํ‚ค๊ฐ’) ์œผ๋กœ ๋ถˆ๋Ÿฌ์™€์ฃผ๋ฉด ๋œ๋‹ค.

useState ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•˜๋ฉด ๋˜์ง€๋งŒ, ๋‹ค๋ฅธ ์ ์€ state ๋Š” ๋‚ด๋ถ€ ์ƒํƒœ, Recoil์€ ์ „์—ญ ์ƒํƒœ๋ผ๋Š” ๊ฒƒ.

 

function FontButton() {
  const [fontSize, setFontSize] = useRecoilState(fontSizeState);
  return (
    <button onClick={() => setFontSize((size) => size + 1)} 
        style={{fontSize}}>
      Click to Enlarge
    </button>
  );
}

 

ํ•ด๋‹น ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๊ฒŒ ๋˜๋ฉด, ํฐํŠธ ์‚ฌ์ด์ฆˆ๊ฐ€ 1์”ฉ ์ฆ๊ฐ€ํ•˜๋ฉฐ, ๋ชจ๋“  ํ™”๋ฉด์—์„œ ์ฆ๊ฐ€๋œ ํฐํŠธ ์‚ฌ์ด์ฆˆ๊ฐ€ ์ ์šฉ๋œ๋‹ค.

โ€‹

selector ์ด๋ž€?

์ „์—ญ ์ƒํƒœ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์–ด๋–ค ๊ณ„์‚ฐ์„ ํ†ตํ•ด ์ƒˆ๋กœ์šด ๊ฐ’์„ ๋‚ด๋ฑ‰๋Š” ์ˆœ์ˆ˜ํ•จ์ˆ˜.

atom์ด๋‚˜ ๋˜ ๋‹ค๋ฅธ selector๋ฅผ ํ†ตํ•˜์—ฌ ์ž…๋ ฅ์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

โ€‹

๊ธฐ๋ณธ์ ์ด๊ณ  ์ตœ์†Œํ•œ์˜ ์ƒํƒœ๊ฐ’๋“ค์€ atom์— ์ €์žฅํ•ด๋‘๊ณ , seletor์— ๋ช…์‹œํ•œ ํ•จ์ˆ˜๋ฅผ ํ†ตํ•˜์—ฌ ์ƒํƒœ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋ฉด ๋œ๋‹ค.

 

selector ์‚ฌ์šฉ๋ฐฉ๋ฒ•

const fontSizeLabelState = selector({
  key: 'fontSizeLabelState', // ๊ณ ์œ ํ•œ ํ‚ค ๊ฐ’
  get: ({get}) => {
    const fontSize = get(fontSizeState); 
    const unit = 'px';

    return `${fontSize}${unit}`;
  },
});

 

get ์—๋Š” ์ƒํƒœ๋ฅผ ๊ณ„์‚ฐํ•  ํ•จ์ˆ˜๊ฐ€ ๋‹ด๊ฒจ์ ธ ์žˆ์œผ๋ฉฐ, get์œผ๋กœ ์ „๋‹ฌ๋˜๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ํ†ตํ•˜์—ฌ Atom์ด๋‚˜ ๋‹ค๋ฅธ Selector์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Œ. ์œ„์™€ ๊ฐ™์ด ์„ ์–ธํ•ด์ฃผ์—ˆ์œผ๋ฉด ์•„๋ž˜์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

 

function FontButton() {
  const [fontSize, setFontSize] = useRecoilState(fontSizeState);
  const fontSizeLabel = useRecoilValue(fontSizeLabelState);

  return (
    <>
      <div>Current font size: ${fontSizeLabel}</div>

      <button onClick={setFontSize(fontSize + 1)} 
        style={{fontSize}}>
        Click to Enlarge
      </button>
    </>
  );
}

 

seletor์˜ ๊ฒฝ์šฐ๋Š” useRecoilValue(ํ‚ค๊ฐ’) ์˜ ํ˜•ํƒœ๋กœ ์„ ์–ธํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ,

๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ํฐํŠธ ์‚ฌ์ด์ฆˆ ์ฆ๊ฐ€์™€ ๋™์‹œ์— fontSizeLabel ์…€๋ ‰ํ„ฐ์—๋„ ์—…๋ฐ์ดํŠธ ๋˜์–ด ์ปดํฌ๋„ŒํŠธ์— ๋ฐ˜์˜๋œ๋‹ค.

 


๊ถŒํ•œ ๊ด€๋ฆฌ ?

โ€‹

๊ถŒํ•œ ๊ด€๋ฆฌ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์—๋Š” ์—ฌ๋Ÿฌ๊ฐ€์ง€๊ฐ€ ์žˆ๊ฒ ์ง€๋งŒ... Recoil ๋กœ ์„ ์–ธํ•œ๋‹ค๋ฉด

์ „์—ญ ์ƒํƒœ ๊ด€๋ฆฌ์ธ ์ ์„ ์‚ฌ์šฉํ•˜์—ฌ login ์—ฌ๋ถ€๋ฅผ Recoil์— ์„ ์–ธํ•ด์ฃผ๊ณ ,

login ์™„๋ฃŒ ๋˜๋ฉด login์—ฌ๋ถ€๋ฅผ true๋กœ ๋ฐ”๊ฟ”์ค€๋‹ค.

๊ทธ๋ฆฌ๊ณ  login ์—ฌ๋ถ€์— ๋”ฐ๋ผ app.js ์—์„œ ํ•ด๋‹น ์ฃผ์†Œ์ง€์— ์ ‘์† ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ• ๊ฑด์ง€ ๋ง๊ฑด์ง€๋ฅผ ๋ถ„๊ธฐํ•˜๋Š” ํ˜•์‹์œผ๋กœ ๊ตฌํ˜„ํ•œ๋‹ค.

 

// useState.ts
// ์ด๊ฑธ ์™œ ํŒŒ์ผ๋ช…์„ state ๋ผ๊ณ  ํ•œ์ง€๋„ ๋ชจ๋ฅด๊ฒ ์Œ.. 

import { selector } from 'recoil';

// import { ACCESSTOKEN_KEY } from '../common/constants/user';

export const getUserIsLogin = selector({
	key: 'userLoginState',
	get: () => {
		// const accessToken = localStorage.getItem("ACCESSTOKEN_KEY");
		// return !!accessToken;
		return true;
	},
});

 

์ด๋Ÿฐ์‹์œผ๋กœ ์„ ์–ธํ•ด๋‘๊ณ  isLogin ๊ฐ’์„ ํŒ๋ณ„ํ•˜์—ฌ

app.js ์—์„œ ํ•ด๋‹น ๋ฃจํŠธ์— ์ ‘์† ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ• ๊ฑด์ง€ ๋ถ„๊ธฐ๋งŒ ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

 

( ์—ฌ๊ธฐ์„œ ๋ณดํ†ต Private, Punblic ๋ถ„๋ฆฌํ•˜์—ฌ.. ๋กœ๊ทธ์ธ์ด ์•ˆ๋˜์–ด ์žˆ๋Š”๋ฐ ํšŒ์›๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํ™”๋ฉด์— ์ ‘๊ทผํ•˜๋ ค๊ณ  ํ•˜๋ฉด login ํ™”๋ฉด์œผ๋กœ ๊ฐ•์ œ sending ์‹œํ‚จ๋‹ค๋˜์ง€์˜ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๊ธฐ๋„ ํ•˜๊ณ , ๋” ๋‚˜์•„๊ฐ€ ๊ด€๋ฆฌ์ž ๊ณ„์ •์˜ ํ™”๋ฉด ์ ‘๊ทผ ์—ฌ๋ถ€ ๋˜ํ•œ ๋ถ„๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.)

 

// PrivateRouter.js

import { useEffect } from 'react';
import { Navigate, Outlet } from 'react-router-dom';

import { URL } from '../common/constants/url';
// import useSnackBar from '@/hooks/common/useSnackBar';

const PrivateRouter = ({ isAuthenticated }) => {
	// const { showSnackBar } = useSnackBar();
	useEffect(() => {
		if (!isAuthenticated) {
			// showSnackBar('๋กœ๊ทธ์ธ ํ›„ ์‚ฌ์šฉํ•ด์ฃผ์„ธ์š”');
			console.log('๋กœ๊ทธ์ธ ํ›„ ์‚ฌ์šฉํ•ด์ฃผ์„ธ์š”')
		}
	}, [isAuthenticated]);

	return isAuthenticated ? <Outlet /> : <Navigate to={URL.LOGIN} replace />;
};

export default PrivateRouter;

 

// PrivateRouter.js

import { useEffect } from 'react';
import { Navigate, Outlet } from 'react-router-dom';
// import useSnackBar from '@/hooks/common/useSnackBar';

const PublicRouter = ({ isAuthenticated }) => {
	// const { showSnackBar } = useSnackBar();
	useEffect(() => {
		if (isAuthenticated) {
			// showSnackBar('๋กœ๊ทธ์ธ ์ƒํƒœ์—์„œ ์ด์šฉํ• ์ˆ˜ ์—†๋Š” ์„œ๋น„์Šค์ž…๋‹ˆ๋‹ค');
			console.log("๋กœ๊ทธ์ธ ์ƒํƒœ์—์„œ ์ด์šฉํ• ์ˆ˜ ์—†๋Š” ์„œ๋น„์Šค์ž…๋‹ˆ๋‹ค")
		}
	}, []);

	return isAuthenticated ? <Navigate to="/" replace /> : <Outlet />;
};

export default PublicRouter;

 

์ง„์งœ ๊ถŒํ•œ ๊ด€๋ฆฌ ๊ฑฐ์˜ ์•ˆ๋งŒ๋“ค์–ด๋†จ๋„ค...;

// app.js
import React, { Suspense } from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { useRecoilState, useRecoilValue } from "recoil";

import { getUserIsLogin } from "./store/userState";
import PublicRouter from "./router/PublicRouter";
import PrivateRouter from "./router/PrivateRouter";


const Contact = React.lazy(() => import("./components/pages/Contact"));

  return (
    <Suspense fallback={<Loading />}>
      {/* <BrowserRouter > */}
      <Routes>
        <Route path="/" element={<MainLayout />}>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
          <Route element={<PrivateRouter isAuthenticated={isLogin} />}>
            <Route path="/contact" element={<Contact />} />
          </Route>
          <Route element={<PublicRouter isAuthenticated={isLogin} />}>
            <Route path={URL.LOGIN} element={<Login />} />
          </Route>
        </Route>
      </Routes>
    </Suspense>
  );
};

 

๋Œ€์ถฉ ์ด๋Ÿฐ์‹์œผ๋กœ ๊ตฌ์กฐ๋งŒ ํ™•์ธํ•˜๋ฉด ๋˜๊ฒ ๋‹ค.

๋‚˜์ค‘์— ๊ถŒํ•œ๊ด€๋ฆฌ ๋งŒ๋“ค์–ด์ง„ ๊ฒƒ ์ฝ”๋“œ ๊ฐ€์ ธ์™€๋ณด๋Š” ๊ฑธ๋กœ...

 


โ€‹๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ฝ๊ฑฐ๋‚˜ ๊ฐœ๋ฐœํ•˜๋ฉฐ ๋Š๊ผˆ๋˜ ๋‡Œํ”ผ์…œ๋กœ ์ž‘์„ฑํ•˜๊ณ  ์žˆ์œผ๋‹ˆ, 
๊ถ๊ธˆํ•˜์‹  ์ ์ด๋‚˜ ์ž˜๋ชป๋œ ์ ์ด ์žˆ๋‹ค๋ฉด ๋Œ“๊ธ€ ๋‚จ๊ฒจ์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 

 

 

 

 

 

728x90
๋”๋ณด๊ธฐ
Document/๋จ•์„ ์ƒ์˜ ์ฝ”๋”ฉ๊ต์‹ค