๐Ÿ“Œ Zustand์—์„œ Selector๋Š” ๊ผญ ์จ์•ผ ํ• ๊นŒ์š”?

์†ก์—ฐ์ง€ยท2025๋…„ 4์›” 17์ผ
0


Zustand, Recoil, Redux ๋“ฑ ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋‹ค ๋ณด๋ฉด "selector" ๋ผ๋Š” ๊ฐœ๋…์„ ์ž์ฃผ ์ ‘ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ฒ˜์Œ์—๋Š” โ€œ๊ตณ์ด ์ด๊ฑธ ์™œ ์จ์•ผ ํ•˜์ง€?โ€ ์‹ถ์€ ์ƒ๊ฐ์ด ๋“ค ์ˆ˜ ์žˆ์ง€๋งŒ, ์‹ค์ œ ํ”„๋กœ์ ํŠธ๊ฐ€ ์ปค์งˆ์ˆ˜๋ก selector์˜ ํ•„์š”์„ฑ๊ณผ ํšจ์œจ์„ฑ์„ ์ฒด๊ฐํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ด ๊ธ€์—์„œ๋Š” ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž์˜ ์ž…์žฅ์—์„œ Selector๊ฐ€ ๋ฌด์—‡์ธ์ง€, ์™œ ์‚ฌ์šฉํ•˜๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  Zustand์—์„œ ์‹ค์ œ๋กœ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€๊นŒ์ง€ ์ž์„ธํžˆ ์ •๋ฆฌํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค.


โœ… Selector๋ž€ ๋ฌด์—‡์ธ๊ฐ€์š”?

Selector๋Š” ์ƒํƒœ์—์„œ ์›ํ•˜๋Š” ๊ฐ’์„ ์„ ํƒํ•˜๊ฑฐ๋‚˜, ๊ฐ€๊ณตํ•ด์„œ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

๋‹จ์ˆœํžˆ ์ƒํƒœ๋ฅผ ๊ทธ๋Œ€๋กœ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ,

  • ํŠน์ • ์กฐ๊ฑด์œผ๋กœ ํ•„ํ„ฐ๋ง์„ ํ•˜๊ฑฐ๋‚˜
  • ๋ณต์ˆ˜์˜ ์ƒํƒœ๋ฅผ ์กฐํ•ฉํ•˜์—ฌ ์ƒˆ๋กœ์šด ์ •๋ณด๋ฅผ ๋งŒ๋“ค๊ฑฐ๋‚˜
  • ํฌ๋งทํŒ… ๋˜๋Š” ๋ณ€ํ™˜๋œ ์ƒํƒœ๊ฐ’์„ ์ œ๊ณตํ•˜๋Š” ์—ญํ•  ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ’ก React์˜ useMemo์™€ ๊ฐœ๋…์ ์œผ๋กœ ์œ ์‚ฌํ•˜์ง€๋งŒ, store ๋‚ด๋ถ€์—์„œ ์ •์˜๋˜๋ฉฐ ์ „์—ญ์ ์œผ๋กœ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์—์„œ ์ฐจ๋ณ„ํ™”๋ฉ๋‹ˆ๋‹ค.

๐Ÿค” โ€œ์…€๋ ‰ํ„ฐ๋Š” Recoil ์ „์šฉ ์•„๋‹ˆ์•ผ?โ€

โžก๏ธ ์•„๋‹ˆ์š”. ์…€๋ ‰ํ„ฐ(selector) ๋Š” Recoil์˜ ์ „์šฉ ๊ธฐ๋Šฅ์ด ์•„๋‹ˆ๋ผ
โ€œ์ƒํƒœ ํŒŒ์ƒ๊ฐ’(derived state)โ€์„ ๊ณ„์‚ฐํ•˜๋Š” ์ผ๋ฐ˜์ ์ธ ๊ฐœ๋…
์ด์—์š”.

์šฉ์–ด์˜๋ฏธ
Selector (์ผ๋ฐ˜ ์šฉ์–ด)์ƒํƒœ์—์„œ ํŠน์ • ๊ฐ’์„ ์ถ”์ถœํ•˜๊ฑฐ๋‚˜ ๊ฐ€๊ณตํ•ด ๋ฆฌํ„ดํ•˜๋Š” ํ•จ์ˆ˜ (๋ฆฌ์•กํŠธ ์ƒํƒœ ๊ด€๋ฆฌ ๊ณตํ†ต ๊ฐœ๋…)
Recoil Selectorselector() ํ•จ์ˆ˜๋กœ ๋“ฑ๋กํ•˜๋Š” ๋‚ด์žฅ API
Zustand Selector๋‚ด์žฅ API๋Š” ์—†๊ณ , ์ง์ ‘ ํ•จ์ˆ˜๋กœ ์ •์˜ํ•ด์„œ ์‚ฌ์šฉ
Redux SelectorcreateSelector (reselect ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ)๋กœ ๊ตฌํ˜„ํ•˜๋Š” ๋ฉ”๋ชจ์ด์ œ์ด์…˜ ํ•จ์ˆ˜

์ฆ‰, Recoil์ด ์…€๋ ‰ํ„ฐ๋ผ๋Š” ๊ฐœ๋…์„ ๊ณต์‹ API๋กœ ์ฑ„ํƒํ–ˆ์„ ๋ฟ,

์ด ๊ฐœ๋…์€ ์ƒํƒœ ๊ด€๋ฆฌ์—์„œ ๋„๋ฆฌ ์“ฐ์ด๋Š” ์ผ๋ฐ˜์ ์ธ ๊ฐœ๋…์ด์—์š”.


๐Ÿ” Zustand์—์„œ๋Š” ์–ด๋–ป๊ฒŒ ์“ฐ๋Š”๋ฐ?

Zustand์—์„œ๋Š” ์•„๋ž˜์ฒ˜๋Ÿผ ํ•จ์ˆ˜๋กœ ์ง์ ‘ ์…€๋ ‰ํ„ฐ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.


getSelectedDepartment: () => {
  const { selectedDeptId, departments } = get();
  return departments.find(d => d.deptId === selectedDeptId) || null;

๊ทธ๋ฆฌ๊ณ  ์•„๋ž˜์ฒ˜๋Ÿผ ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”:


const selected = useGroupStore.getState().getSelectedDepartment();

์ด๋Ÿฐ ๊ฑธ Recoil์—์„œ๋Š” selector()๋กœ ๋“ฑ๋กํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋ฟ์ด์—์š”.


๐Ÿ“ฆ ์ฆ‰, ์…€๋ ‰ํ„ฐ๋Š” โ€œRecoil๋งŒ์˜ ๊ธฐ๋Šฅโ€์ด ์•„๋‹ˆ๋ผโ€ฆ

  • Recoil์€ "selector"๋ฅผ ๊ณต์‹ ๊ธฐ๋Šฅ์œผ๋กœ ์ด๋ฆ„ ๋ถ™์˜€๊ณ 
  • Redux๋Š” createSelector๋ผ๋Š” ํ•จ์ˆ˜๋กœ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์ œ๊ณตํ•˜๊ณ 
  • Zustand๋Š” ์ž์œ ๋กญ๊ฒŒ โ€œ์šฐ๋ฆฌ๊ฐ€ ์ง์ ‘ ์…€๋ ‰ํ„ฐ์ฒ˜๋Ÿผ ํ•จ์ˆ˜ ์ •์˜โ€ํ•ด์„œ ์จ์š”

๐Ÿค” Selector๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ 

1. ์ค‘๋ณต ๋กœ์ง ์ œ๊ฑฐ

์•„๋ž˜์™€ ๊ฐ™์ด ๋ถ€์„œ ์ •๋ณด๋ฅผ ์ฐพ๋Š” ๋กœ์ง์ด ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฐ˜๋ณต๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค:


const dept = departments.find(d => d.deptId === selectedDeptId)

โ†’ ์ด๋Ÿฌํ•œ ์ฝ”๋“œ๋ฅผ store ๋‚ด๋ถ€์˜ selector ํ•จ์ˆ˜๋กœ ์ถ”์ถœํ•ด๋‘๋ฉด ์žฌ์‚ฌ์šฉ์„ฑ์ด ๋†’์•„์ง‘๋‹ˆ๋‹ค:


getSelectedDepartment: () => {
  const { selectedDeptId, departments } = get();
  return departments.find(dept => dept.deptId === selectedDeptId) || null;
}

2. ์ƒํƒœ ๊ตฌ์กฐ ๋ณ€๊ฒฝ์— ์œ ์—ฐํ•ฉ๋‹ˆ๋‹ค

์˜ˆ๋ฅผ ๋“ค์–ด ์ƒํƒœ ๊ตฌ์กฐ๊ฐ€ ๋ฐ”๋€Œ์–ด departments๊ฐ€ organization.departments๋กœ ๋ณ€๊ฒฝ๋œ๋‹ค๋ฉด,
์ปดํฌ๋„ŒํŠธ๋งˆ๋‹ค ์ „๋ถ€ ์ˆ˜์ •์„ ํ•ด์•ผ ํ•˜์ง€๋งŒ,

โ†’ selector๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด selector ํ•˜๋‚˜๋งŒ ๊ณ ์น˜๋ฉด ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค.


3. ์ปดํฌ๋„ŒํŠธ๋Š” ํ‘œํ˜„(UI)์—๋งŒ ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค


const leaderName = useGroupStore.getState().getLeaderName(5)

์œ„์ฒ˜๋Ÿผ ์ปดํฌ๋„ŒํŠธ๋Š” "์–ด๋–ป๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ์ง€"๊ฐ€ ์•„๋‹ˆ๋ผ,

"์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œํ˜„ํ• ์ง€"์—๋งŒ ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์–ด UI/๋กœ์ง ๋ถ„๋ฆฌ(SoC) ๊ฐ€ ์ž˜ ์ด๋ฃจ์–ด์ง‘๋‹ˆ๋‹ค.


4. ์•ˆ์ •์ ์ธ ์ฝ”๋“œ ์ž‘์„ฑ (null-safe)

์กฐ๊ฑด๋ฌธ ์—†์ด ๋ฌด์‹ฌ์ฝ” ์•„๋ž˜์™€ ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด:


const leader = departmentMembers[deptId].find(m => m.position === 1);

โ†’ ์—๋Ÿฌ ๋ฐœ์ƒ ๊ฐ€๋Šฅ์„ฑ (Cannot read properties of undefined)์ด ๋†’์•„์ง‘๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ selector ๋‚ด๋ถ€์—์„œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด๋‘๋ฉด ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” ์•ˆ์‹ฌํ•˜๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


๐Ÿง  Selector ์‹ค์ œ ์ฝ”๋“œ ์˜ˆ์‹œ (Zustand ๊ธฐ์ค€)


// store ๋‚ด๋ถ€์— ์ •์˜
getSelectedDepartment: () => {
  const { selectedDeptId, departments } = get();
  return departments.find(dept => dept.deptId === selectedDeptId) || null;
},

getLeaderName: (deptId: number) => {
  const members = get().departmentMembers[deptId] || [];
  const leader = members.find(member => member.position === 1);
  return leader?.userNm || '';
}

์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ฐ„๋‹จํ•˜๊ฒŒ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค:


const selectedDept = useGroupStore.getState().getSelectedDepartment();
const leaderName = useGroupStore.getState().getLeaderName(selectedDept?.deptId || 0);

๐Ÿ“ Selector๋Š” ์–ธ์ œ๋ถ€ํ„ฐ ๋„์ž…ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์„๊นŒ์š”?

  • ํŠน์ • ์ƒํƒœ์— ๋Œ€ํ•ด ๊ณ„์‚ฐ๋œ ๊ฐ’์„ ์—ฌ๋Ÿฌ ๊ณณ์—์„œ ๋ฐ˜๋ณต ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋ฉด
  • ์ƒํƒœ ๊ตฌ์กฐ๊ฐ€ ์ค‘์ฒฉ๋˜๊ฑฐ๋‚˜ ๋ณต์žกํ•ด์งˆ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋‹ค๋ฉด
  • ์œ ์ง€๋ณด์ˆ˜ ๋ฐ ๋ฆฌํŒฉํ† ๋ง ํšจ์œจ์„ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค๋ฉด

โžก๏ธ ์ด๋Ÿด ๊ฒฝ์šฐ, selector๋ฅผ ๋ฏธ๋ฆฌ ์ •์˜ํ•ด๋‘๋Š” ๊ฒƒ์ด ์ฝ”๋“œ ํ’ˆ์งˆ๊ณผ ๊ฐœ๋ฐœ ์†๋„ ๋ชจ๋‘์— ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.


๐Ÿ› ๏ธ ํŒ: ์…€๋ ‰ํ„ฐ๋Š” store ์™ธ๋ถ€๋กœ ๋”ฐ๋กœ ๋นผ๋„ ์ข‹์Šต๋‹ˆ๋‹ค


// store/group/selectors.ts
export const getSelectedDepartment = (state: GroupState) => {
  return state.departments.find(d => d.deptId === state.selectedDeptId) || null;
};

์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” ์•„๋ž˜์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

const dept = getSelectedDepartment(useGroupStore.getState());

โœ… ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

Selector๋Š” ์ดˆ๋ฐ˜์—๋Š” ์—†์–ด๋„ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฐ ๋ฌธ์ œ๊ฐ€ ์—†์–ด ๋ณด์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์ฝ”๋“œ๊ฐ€ ์ ์  ์ปค์ง€๊ณ  ์ƒํƒœ๊ฐ€ ๋ณต์žกํ•ด์งˆ์ˆ˜๋ก selector๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด

  • ๋กœ์ง์ด ์ค‘๋ณต๋˜๊ณ 
  • ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฌด๊ฑฐ์›Œ์ง€๊ณ 
  • ์ƒํƒœ ๋ณ€๊ฒฝ์— ๋”ฐ๋ฅธ ๋ฒ„๊ทธ ๋ฐœ์ƒ ์œ„ํ—˜์ด ๋†’์•„์ง‘๋‹ˆ๋‹ค.

Zustand์™€ ๊ฐ™์€ ๊ฒฝ๋Ÿ‰ ์ƒํƒœ ๊ด€๋ฆฌ ๋„๊ตฌ์—์„œ๋„,

์ƒํƒœ๋ฅผ โ€œ์–ด๋–ป๊ฒŒ ๊ฐ€๊ณตํ•ด์„œ ์“ธ์ง€๊นŒ์ง€ ์„ค๊ณ„ํ•˜๋Š” ๊ฒƒ์ด ์ง„์งœ ์ƒํƒœ ๊ด€๋ฆฌ์˜ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค.

profile
ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์Ÿˆ!!

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