Recoil์— ๋Œ€ํ•ด์„œ ๊ฐ€๋ณ๊ฒŒ ํ†บ์•„๋ณด๊ธฐ

NDD ํ”„๋กœ์ ํŠธ์˜ FE์—์„œ๋Š” recoil์„ ๋„์ž…ํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!!

Profile Picture
adultlee
2023-11-20

์›๋ณธ

NDD ํ”„๋กœ์ ํŠธ์˜ FE์—์„œ๋Š” recoil์„ ๋„์ž…ํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!! ์ €๋Š” ์ง€๊ธˆ๊นŒ์ง€ mobx์™€ redux๋งŒ์„ ์‚ฌ์šฉํ•ด ์™”๊ธฐ ๋•Œ๋ฌธ์—, recoil์— ๋Œ€ํ•ด์„œ๋Š” ์ƒˆ๋กญ๊ฒŒ ํ•™์Šต์ด ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ์— ๋”ฐ๋ผ (์ •๋ง ์ •๋ง) ๊ฐ€๋ณ๊ฒŒ ํ†บ์•„๋ณด๋ ค ํ•ฉ๋‹ˆ๋‹ค!

Recoil์ด๋ž€?

Recoil์€ React ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์œ„ํ•œ ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. ์ด๋Š” React์˜ ๊ธฐ๋ณธ์ ์ธ ์ƒํƒœ๊ด€๋ฆฌ ์˜์—ญ์—์„œ ํ™•์žฅ๋˜์–ด, ํŠนํžˆ ๋ณต์žกํ•˜๊ณ  ๋Œ€๊ทœ๋ชจ์˜ ์„œ๋น„์Šค์—์„œ์˜ ํšจ์œจ์ ์ธ ์ƒํƒœ๊ด€๋ฆฌ๋ฅผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. Recoil์€ Facebook์—์„œ ๊ฐœ๋ฐœ๋˜์—ˆ์œผ๋ฉฐ, React์˜ ๊ธฐ์กด ๊ฐœ๋…๊ณผ ์ž˜ ํ†ตํ•ฉ๋˜๋„๋ก ์„ค๊ณ„๋˜์—ˆ์Šต๋‹ˆ๋‹ค!

๋ฐ”๋กœ ๊ณต์‹๋ฌธ์„œ๋ฅผ ๋“ค์–ด๊ฐ€ ๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค!

๋“ค์–ด๊ฐ€์ž ๋งˆ์ž ํ•˜๋‹จ์˜ ์„ธ ์š”์†Œ๊ฐ€ ๋ˆˆ์— ๋•๋‹ˆ๋‹ค!

๊ณต์‹๋ฌธ์„œ ํ™ˆ์˜ ์„ธ ์š”์†Œ๊ฐ€ ์•„๋ฌด๋ž˜๋„ recoil์˜ ํ…Œ๋งˆ๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ์ฃผ์š”ํ•œ ๊ธฐ๋Šฅ์ธ๋“ฏํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ํ•™์Šตํ•˜๋ฉฐ ๊ธฐ์กด์— ์ œ๊ฐ€ ํ•™์Šตํ•ด์˜ค๋˜ mobx์™€ ๋น„๊ตํ•˜๋ฉด ์ข‹์„๋“ฏ ํ•ฉ๋‹ˆ๋‹ค!

์ž‘๊ณ  React์Šค๋Ÿฌ์šด (Small and React-ish)

~~์š” ๊ท€์—ฌ์šด ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ๋– ์˜ค๋ฅด๋Š” ์ œ๋ชฉ์ธ๋“ฏํ•ฉ๋‹ˆ๋‹ค...!~~

ํ•ด๋‹น ์ œ๋ชฉ์˜ ์ฃผ๋œ ๋‚ด์šฉ์€ ํฌ๊ฒŒ ๋‘๊ฐ€์ง€๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋จผ์ € "์ž‘๊ณ "๋ผ๋Š” ๋ถ€๋ถ„์€ recoil์ด ๊ฐ€๋ณ๊ฒŒ ๋„์ž…ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ํฐ ๋Ÿฌ๋‹ ์ปค๋ธŒ๋ฅผ ์š”ํ•˜์ง€ ์•Š์Œ์„ ์˜๋ฏธํ•˜๋Š”๋“ฏ ํ•ฉ๋‹ˆ๋‹ค. ๋‘๋ฒˆ์งธ๋กœ "React์Šค๋Ÿฌ์šด" ์ด๋ผ๋Š” ๋ถ€๋ถ„์€ ๋‹น์—ฐํžˆ Facebook์—์„œ ๋งŒ๋“  ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค ๋ณด๋‹ˆ, ๊ทธ ์—ฐ๊ฒฐ์„ ๊ณค๊ณ ํžˆ ํ•˜๊ธฐ ์œ„ํ•ด ๋„์ž…๋˜์—ˆ๋‹ค๊ณ  ์ƒ๊ฐํ•  ์ˆ˜์žˆ๋Š”๋ฐ ์•„๋งˆ๋„ ์ด๋ฅผ ๊ฐ•์กฐํ•˜๋Š”๋“ฏ ํ•ฉ๋‹ˆ๋‹ค.

React์˜ ์ฒ ํ•™์„ ๋”ฐ๋ฅด๋Š” ์ƒํƒœ ๊ด€๋ฆฌ

Recoil์€ React์˜ ์ฃผ์š” ์ฒ ํ•™์ธ ์„ ์–ธ์  UI, ์ปดํฌ๋„ŒํŠธ ์ค‘์‹ฌ์˜ ์„ค๊ณ„, ๊ทธ๋ฆฌ๊ณ  ํ›…์Šค(Hooks)๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ฉ๋‹ˆ๋‹ค. React์—์„œ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•ด ํ›…์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์€ ๋งค์šฐ ์ง๊ด€์ ์ด๊ณ  ๊ฐ„๊ฒฐํ•ฉ๋‹ˆ๋‹ค. Recoil๋„ ์ด๋Ÿฌํ•œ ํŒจํ„ด์„ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค. ์•„๋ž˜๋Š” ์ •๋ง ๊ฐ„๋‹จํ•˜๊ฒŒ recoil์„ ํ†ตํ•˜์—ฌ ์ƒํƒœ๊ด€๋ฆฌ๋ฅผ ์ง„ํ–‰ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

import React from "react";
import { atom, useRecoilState } from "recoil";

// Atom ์ •์˜: ์ „์—ญ ์ƒํƒœ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋‹จ์œ„
const textState = atom({
	key: "textState", // ๊ณ ์œ ํ•œ ํ‚ค
	default: "", // ๊ธฐ๋ณธ๊ฐ’
});

function TextInput() {
	const [text, setText] = useRecoilState(textState); // ์‚ฌ์‹ค์ƒ useState์™€ ๋™์ผํ•œ ์—ญํ• 

	const onChange = (event) => {
		setText(event.target.value);
	};

	return <input type="text" value={text} onChange={onChange} />;
}

์œ„ ์˜ˆ์‹œ์—์„œ textState๋Š” ์ „์—ญ์ ์œผ๋กœ ๊ณต์œ ๋˜๋Š” ์ƒํƒœ(atom)์ด๋ฉฐ, TextInput ์ปดํฌ๋„ŒํŠธ๋Š” ์ด ์ƒํƒœ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ๋ Œ๋”๋งํ•˜๊ณ  ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์€ React์˜ useState ํ›…์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ ๋งค์šฐ ์œ ์‚ฌํ•˜๋ฉฐ, Recoil์€ ์ด๋Ÿฌํ•œ ์ผ๊ด€์„ฑ์„ ํ†ตํ•ด React ๊ฐœ๋ฐœ์ž๋“ค์—๊ฒŒ ์นœ์ˆ™ํ•œ ๊ฒฝํ—˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

๊ฐ„๊ฒฐํ•จ๊ณผ ํšจ์œจ์„ฑ

Recoil์€ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ ์ •๋ง ์ตœ์†Œํ•œ์˜ API๋งŒ์„ ์ œ๊ณตํ•จ์œผ๋กœ์จ ๋Ÿฌ๋‹์ปค๋ธŒ๋ฅผ ๋‚ฎ์ถ”๊ณ , ํšจ์œจ์ ์ธ ๊ฐœ๋ฐœ ๊ฒฝํ—˜์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

Recoil์˜ Selector๋Š” Atom์—์„œ์˜ ํŒŒ์ƒ ์ƒํƒœ๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•œ ๊ฐ„๊ฒฐํ•˜๊ณ  ๊ฐ•๋ ฅํ•œ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” React์˜ ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง ์ตœ์ ํ™”์™€ ์œ ์‚ฌํ•œ ๊ฐœ๋…์„ ์ƒํƒœ ๊ด€๋ฆฌ์— ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.

import { selector } from "recoil";

const charCountState = selector({
	key: "charCountState",
	get: ({ get }) => {
		const text = get(textState);
		return text.length;
	},
});

function CharacterCount() {
	const count = useRecoilValue(charCountState);

	return <>Character Count: {count}</>;
}

์œ„ ์˜ˆ์‹œ์—์„œ charCountState๋Š” textState์˜ ๋ณ€๊ฒฝ์— ์˜์กดํ•˜๋Š” ํŒŒ์ƒ ์ƒํƒœ(selector)์ž…๋‹ˆ๋‹ค. ์ด๋Š” ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋กœ ์ •์˜๋˜๋ฉฐ, textState์˜ ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งŒ ์žฌ๊ณ„์‚ฐ๋ฉ๋‹ˆ๋‹ค. ์ด ๋ฐฉ์‹์€ React์˜ ์„ ์–ธ์  ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํŒจ๋Ÿฌ๋‹ค์ž„์„ ๋”ฐ๋ฅด๋ฉด์„œ, ์ƒํƒœ ๊ด€๋ฆฌ์—์„œ๋„ ๋ถˆํ•„์š”ํ•œ ๊ณ„์‚ฐ์„ ์ตœ์†Œํ™”ํ•ฉ๋‹ˆ๋‹ค.

๋ฐ์ดํ„ฐ ํ๋ฆ„ ๊ทธ๋ž˜ํ”„(Data-Flow Graph)

"๋ฐ์ดํ„ฐ ํ๋ฆ„ ๊ทธ๋ž˜ํ”„ (Data-Flow Graph)"๋Š” ์„œ๋น„์Šค์˜ ์ƒํƒœ์™€ ๊ทธ ์ƒํƒœ(state)์— ์˜์กดํ•˜๋Š” ํŒŒ์ƒ ๋ฐ์ดํ„ฐ ๊ฐ„์˜ ๊ด€๊ณ„๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. ์ด๋Š” ๋ฐ์ดํ„ฐ์˜ ํ๋ฆ„๊ณผ ์—…๋ฐ์ดํŠธ๋ฅผ ๋ช…ํ™•ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•˜๊ณ  ๊ฐ์‹œํ• ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด๋ฅผ ํšจ์œจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

๋ฐ์ดํ„ฐ ํ๋ฆ„ ๊ทธ๋ž˜ํ”„๋Š” ์ƒํƒœ(Atoms)์™€ ์ด ์ƒํƒœ์— ์˜์กดํ•˜๋Š” ํŒŒ์ƒ ๋ฐ์ดํ„ฐ(Selectors) ๊ฐ„์˜ ๊ด€๊ณ„๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. ๊ฐ Atom์€ ๊ทธ๋ž˜ํ”„์˜ ๋…ธ๋“œ๋กœ, Selector๋Š” Atom ๋…ธ๋“œ์— ์˜์กดํ•˜๋Š” ๋‹ค๋ฅธ ๋…ธ๋“œ๋กœ ํ‘œํ˜„๋ฉ๋‹ˆ๋‹ค. Atom์˜ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด, ์ด ๋ณ€๊ฒฝ์€ ์˜์กดํ•˜๋Š” ๋ชจ๋“  Selector์— ์ „ํŒŒ๋ฉ๋‹ˆ๋‹ค. Selector๋Š” Atom์˜ ์ƒˆ๋กœ์šด ์ƒํƒœ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋‹ค์‹œ ๊ณ„์‚ฐ๋˜๋ฉฐ, ์ด๋ฅผ ํ†ตํ•ด ๊ด€๋ จ๋œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์—…๋ฐ์ดํŠธ๋ฉ๋‹ˆ๋‹ค. Selector๋Š” ํ•„์š”ํ•  ๋•Œ๋งŒ ์žฌ๊ณ„์‚ฐ๋ฉ๋‹ˆ๋‹ค. Atom์˜ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜์–ด๋„, ํ•ด๋‹น Selector์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๋Š” ๋ณ€๊ฒฝ์ธ ๊ฒฝ์šฐ Selector๋Š” ์žฌ๊ณ„์‚ฐ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๋ถˆํ•„์š”ํ•œ ๊ณ„์‚ฐ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•ฉ๋‹ˆ๋‹ค.

// atom ์€ text์˜ ์ƒํƒœ๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
const textState = atom({
	key: "textState",
	default: "",
});
// ์ด Atom์€ ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•˜๋Š” ํ…์ŠคํŠธ๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ƒํƒœ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๊ธฐ๋ณธ ๋‹จ์œ„์ž…๋‹ˆ๋‹ค.

// selector๋Š” atom์œผ๋กœ ๋ณ€๊ฒฝ๋˜๋Š” ํŒŒ์ƒ ๊ฐ’์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

const charCountState = selector({
	key: "charCountState",
	get: ({ get }) => {
		const text = get(textState);
		return text.length;
	},
});

์ด Selector๋Š” textState Atom์— ์˜์กดํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ํ…์ŠคํŠธ๋ฅผ ์ž…๋ ฅํ•  ๋•Œ๋งˆ๋‹ค textState์˜ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜๊ณ , charCountState๋Š” ์ด ๋ณ€๊ฒฝ์„ ๊ฐ์ง€ํ•˜์—ฌ ๋ฌธ์ž ์ˆ˜๋ฅผ ๋‹ค์‹œ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.

์ปดํฌ๋„ŒํŠธ์—์„œ์˜ ์ƒํ˜ธ์ž‘์šฉ

function TextInput() {
  const [text, setText] = useRecoilState(textState);
  // ... ์ž…๋ ฅ ์ฒ˜๋ฆฌ ๋กœ์ง
  return <InputArea value={text} onChange(setText)>
} // ์—ฌ๊ธฐ์„œ TextInput์€ text๋ฅผ ๊ตฌ๋…ํ•ฉ๋‹ˆ๋‹ค. ๊ตฌ๋…์— ๋Œ€ํ•œ ๋‚ด์šฉ์€ ์•„๋ž˜ ํ›„์ˆ ํ•ฉ๋‹ˆ๋‹ค.

function CharacterCount() {
  const count = useRecoilValue(charCountState);
  // ... ๋ฌธ์ž ์ˆ˜ ํ‘œ์‹œ ๋กœ์ง
  return <div>{count}</div>
}

TextInput ์ปดํฌ๋„ŒํŠธ๋Š” textState๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ณ , CharacterCount ์ปดํฌ๋„ŒํŠธ๋Š” charCountState๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ณ„์‚ฐ๋œ ๋ฌธ์ž ์ˆ˜๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” Recoil์ด ๋ฐ์ดํ„ฐ ํ๋ฆ„์„ ์–ด๋–ป๊ฒŒ ๊ด€๋ฆฌํ•˜๋Š”์ง€ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

recoil์—์„œ์˜ ๊ตฌ๋…์ด๋ž€

Recoil์—์„œ์˜ "๊ตฌ๋…(subscription)"์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ƒํƒœ(atom) ๋˜๋Š” ํŒŒ์ƒ ๋ฐ์ดํ„ฐ(selector)์˜ ๋ณ€๊ฒฝ์„ ๊ฐ์ง€ํ•˜๊ณ , ์ด๋Ÿฌํ•œ ๋ณ€๊ฒฝ์— ๋ฐ˜์‘ํ•˜๋Š” ๊ฐœ๋…์„ ๋งํ•ฉ๋‹ˆ๋‹ค. Recoil์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜๋Š” ์ƒํƒœ์˜ ๋ณ€๊ฒฝ์„ ๊ตฌ๋…ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์—๊ฒŒ ํšจ์œจ์ ์œผ๋กœ ์•Œ๋ฆฌ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ๊ตฌ๋… ๋ฉ”์ปค๋‹ˆ์ฆ˜์€ React์˜ ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง ์ตœ์ ํ™”์™€ ์ƒํ˜ธ ์ž‘์šฉํ•ฉ๋‹ˆ๋‹ค.

Recoil์—์„œ atom ๋˜๋Š” selector์˜ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด, Recoil์€ ์ด ์ƒํƒœ๋ฅผ ๊ตฌ๋…ํ•˜๊ณ  ์žˆ๋Š” ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ์—๊ฒŒ ์•Œ๋ฆฝ๋‹ˆ๋‹ค. ์ƒํƒœ๋ฅผ ๊ตฌ๋…ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋Š” ์ƒํƒœ์˜ ๋ณ€๊ฒฝ์„ ๊ฐ์ง€ํ•˜๊ณ , ์ด์— ๋”ฐ๋ผ ReRendering๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค UI๊ฐ€ ์ตœ์‹  ์ƒํƒœ๋ฅผ ๋ฐ˜์˜ํ•˜๋„๋ก ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. Recoil์€ ํ•„์š”ํ•œ ์ปดํฌ๋„ŒํŠธ๋งŒ ์—…๋ฐ์ดํŠธ๋˜๋„๋ก ์ตœ์ ํ™”ํ•˜์—ฌ, ๋ถˆํ•„์š”ํ•œ Rendering์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ํŠน์ • atom์— ๋Œ€ํ•œ ๋ณ€๊ฒฝ์ด ์ผ๋ถ€ ์ปดํฌ๋„ŒํŠธ์—๋งŒ ์˜ํ–ฅ์„ ๋ฏธ์นœ๋‹ค๋ฉด, Recoil์€ ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๋งŒ์„ ReRenderingํ•ฉ๋‹ˆ๋‹ค.

function TextInput() {
	// textState atom์„ ๊ตฌ๋…ํ•˜๊ณ , ์ƒํƒœ ๋ณ€๊ฒฝ ์‹œ ์žฌ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค.
	const [text, setText] = useRecoilState(textState);

	// ... ์ปดํฌ๋„ŒํŠธ ๋กœ์ง
}

์œ„ ์ฝ”๋“œ์—์„œ TextInput์˜ textState๋ฅผ ๊ตฌ๋…ํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด textState๋ฅผ ๋ณ€๊ฒฝํ•˜๋ฉด, TextInput๋Š” ์ƒˆ๋กœ์šด text ๊ฐ’์œผ๋กœ ์žฌ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค.

Recoil์—์„œ์˜ ๊ตฌ๋… ๊ฐœ๋…์€ ์ƒํƒœ ๊ด€๋ฆฌ์˜ ํ•ต์‹ฌ์œผ๋กœ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ ํ•„์š”ํ•œ ๋ถ€๋ถ„๋งŒ์„ ํšจ์œจ์ ์œผ๋กœ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฐ ์ค‘์š”ํ•œ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๊ฐœ๋ฐœ์ž๋Š” ์ƒํƒœ ๋ณ€ํ™”์— ๋”ฐ๋ผ ๋™์ ์œผ๋กœ ๋ฐ˜์‘ํ•˜๋Š” ๋ฐ˜์‘ํ˜• UI๋ฅผ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Recoil์˜ ๋‹ค์–‘ํ•œ hook๋“ค

1.useRecoilState

useRecoilState ํ›…์€ Recoil์˜ atom์— ์ €์žฅ๋œ ์ƒํƒœ๋ฅผ ์ฝ๊ณ  ์“ฐ๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” React์˜ useState ํ›…๊ณผ ์œ ์‚ฌํ•œ API๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค.

import { atom, useRecoilState } from "recoil";

// Atom ์ •์˜
const textState = atom({
	key: "textState", // ๊ณ ์œ ํ•œ ํ‚ค
	default: "", // ์ดˆ๊ธฐ๊ฐ’
});

function TextInput() {
	const [text, setText] = useRecoilState(textState);

	const onChange = (event) => {
		setText(event.target.value);
	};

	return <input type="text" value={text} onChange={onChange} />;
}

2. useRecoilValue

useRecoilValue ํ›…์€ Recoil์˜ atom ๋˜๋Š” selector์˜ ๊ฐ’์„ ์ฝ๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ด ํ›…์€ ์ƒํƒœ๋ฅผ ์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ ์‚ฌ์šฉํ•  ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

import { useRecoilValue } from "recoil";

function TextDisplay() {
	const text = useRecoilValue(textState);

	return <div>{text}</div>;
}

3. useSetRecoilState

useSetRecoilState ํ›…์€ Recoil ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ํ•จ์ˆ˜๋งŒ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด ํ›…์€ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ํ˜„์žฌ ์ƒํƒœ ๊ฐ’์„ ์ฝ์„ ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค.

import { useSetRecoilState } from "recoil";

function TextUpdater() {
	const setText = useSetRecoilState(textState);

	const onClick = () => {
		setText("Hello, Recoil!");
	};

	return <button onClick={onClick}>Update Text</button>;
}

4. useResetRecoilState

useResetRecoilState ํ›…์€ Recoil์˜ atom ์ƒํƒœ๋ฅผ ์ดˆ๊ธฐ๊ฐ’์œผ๋กœ ์žฌ์„ค์ •ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

import { useResetRecoilState } from "recoil";

function ResetButton() {
	const resetText = useResetRecoilState(textState);

	return <button onClick={resetText}>Reset</button>;
}

5. useRecoilValueLoadable

useRecoilValueLoadable ํ›…์€ ๋น„๋™๊ธฐ selector์˜ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ด ํ›…์€ ๋ฐ์ดํ„ฐ์˜ ๋กœ๋”ฉ, ์—๋Ÿฌ, ์™„๋ฃŒ ์ƒํƒœ๋ฅผ ์‰ฝ๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.

import { selector, useRecoilValueLoadable } from "recoil";

// ๋น„๋™๊ธฐ Selector ์ •์˜
const asyncDataState = selector({
	key: "asyncDataState",
	get: async () => {
		const response = await fetch("https://api.example.com/data");
		return response.json();
	},
});

function AsyncDataComponent() {
	const loadable = useRecoilValueLoadable(asyncDataState);

	switch (loadable.state) {
		case "hasValue":
			return <div>{loadable.contents}</div>;
		case "loading":
			return <div>Loading...</div>;
		case "hasError":
			return <div>Error: {loadable.contents.message}</div>;
	}
}

๊ต์ฐจํ•˜๋Š” ์•ฑ ๊ด€์ฐฐ(Cross-App Observation)

"๊ต์ฐจํ•˜๋Š” ์•ฑ ๊ด€์ฐฐ (Cross-App Observation)"์€ Recoil์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ์œผ๋กœ ๊ธฐ๋ณธ์ ์œผ๋กœ ์„œ๋น„์Šค ๋‚ด๋ถ€์˜ ์ƒ์„ธํ•œ ์ƒํƒœ๋ณ€ํ™”๋ฅผ ์ฒดํฌํ•ด ๊ฐ€๋ฉฐ ๋ชจ๋‘ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๊ด€์ฐฐ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ๋“ฏํ•ฉ๋‹ˆ๋‹ค.

About time trabel debuggin

Recoil์„ ์‚ฌ์šฉํ•˜๋ฉด, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ชจ๋“  ์ƒํƒœ ๋ณ€ํ™”๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ด€์ฐฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ชจ๋“  ๋ถ€๋ถ„์—์„œ ์ผ์–ด๋‚˜๋Š” ์ƒํƒœ ๋ณ€ํ™”๋ฅผ ์ •ํ™•ํžˆ ํŒŒ์•…ํ•˜๊ณ , ํ•„์š”ํ•œ ๋ฐ˜์‘์„ ์ทจํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค. ์ƒํƒœ ๋ณ€ํ™”๋ฅผ ๊ด€์ฐฐํ•จ์œผ๋กœ์จ ๊ฐœ๋ฐœ์ž๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ง€์†์„ฑ์„ ๊ด€๋ฆฌํ•˜๊ฑฐ๋‚˜, ๋ผ์šฐํŒ… ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜๊ฑฐ๋‚˜, ๋ณต์žกํ•œ ๋””๋ฒ„๊น… ๊ธฐ๋Šฅ(์˜ˆ: ์‹œ๊ฐ„ ์—ฌํ–‰ ๋””๋ฒ„๊น…)์„ ์†์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

useRecoilTransactionObserver_UNSTABLE(({ snapshot }) => {
	snapshot.getNodes_UNSTABLE({ isModified: true }).forEach((modifiedNode) => {
		const atomLoadable = snapshot.getLoadable(modifiedNode);
		if (atomLoadable.state === "hasValue") {
			console.log(
				`Atom ${modifiedNode.key}์˜ ์ƒˆ๋กœ์šด ๊ฐ’: `,
				atomLoadable.contents
			);
		}
	});
});

์œ„ ์ฝ”๋“œ ์˜ˆ์‹œ์—์„œ๋Š” recoil์—์„œ ์ œ๊ณตํ•˜๋Š” hooks์ž…๋‹ˆ๋‹ค. ์ด๋Š” Recoil์˜ ์ƒํƒœ ๋ณ€ํ™”๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ฐ์ง€ํ•˜๊ณ , ์ฝ˜์†”์— ๋กœ๊น…ํ•˜๋Š” ๋ฐฉ์‹์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ์ด์™€ ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ƒํƒœ ๋ณ€ํ™”๋ฅผ ๊ด€์ฐฐํ•˜๊ณ , ํ•„์š”ํ•œ ๋กœ์ง์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋งบ์Œ๋ง

์ •๋ง ๊ฐ„๋‹จํ•˜๊ฒŒ Recoil์— ๋Œ€ํ•ด์„œ ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค. ๊ฐ„๋‹จํ•˜๊ฒŒ ์ฐพ์•„ ์‚ฌ์šฉํ–ˆ๊ธฐ ๋•Œ๋ฌธ์—, ๊ทธ ์†์˜ ๋‚ด๋ถ€์ ์ธ ๋™์ž‘์ด๋‚˜ ์žฅ๋‹จ์— ๋Œ€ํ•ด์„œ ๋ชจ๋‘ ์ดํ•ดํ•˜์ง€ ๋ชปํ•œ์ ์ด ๋‹ค์†Œ ์•„์‰ฝ์Šต๋‹ˆ๋‹ค๋งŒ, ์šฐ์„ ์€ ๋‹น์žฅ ํ”„๋กœ์ ํŠธ์—์„œ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•œ ์ƒํƒœ๋กœ ์ง„์ž…ํ•˜๋Š”๊ฒƒ์ด ์šฐ์„ ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ถˆ๊ฐ€ํ”ผํ•˜๊ฒŒ ์—ฌ๊ธฐ์„œ ํ•™์Šต์„ ์ž ์‹œ ๋งˆ๋ฌด๋ฆฌํ•˜๋Š”๊ฒƒ์ด ์˜ณ๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ฐ„๋‹จํ•˜๊ฒŒ ํ”„๋กœ์ ํŠธ์—์„œ ์‚ฌ์šฉํ•ด๋ณด๋‹ˆ, ์†Œ๊ฐœ์—์„œ ์–ธ๊ธ‰ํ•œ๊ฒƒ๊ณผ ๊ฐ™์ด ์ •๋ง ๊ฐ„๋‹จํ•˜๊ฒŒ react ํ”„๋กœ์ ํŠธ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”๋“ฏํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ Mobx์—์„œ์™„ ๋‹ค๋ฅด๊ฒŒ ๊ฐ์ฒด ์ง€ํ–ฅ์ ์ธ ๋Š๋‚Œ๋ณด๋‹ค๋Š” react ์™€ ์–ด์šธ๋ฆฌ๋„๋ก ํ•จ์ˆ˜ํ˜•๊ณผ hooks๋ฅผ ํ†ตํ•œ ๊ธฐ๋Šฅ ๋ถ„๋ฆฌ๋ฅผ ์ง€ํ–ฅํ•˜๋Š”๋“ฏ ํ•ฉ๋‹ˆ๋‹ค.

ํ•ด๋‹น PR์—์„œ ์ฝ”๋“œ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ยฉ 2024 Adultlee. All rights reserved.Made with โค by ์ด์„ฑ์ธ