Zustand 셀렉터... 사용하고 계시죠? (리렌더 폭탄 방지하기)

2025. 6. 20. 12:09·React

공식 홈페이지의 얕은 함정

Zustand 공식 홈페이지에 들어가면 가장 먼저 보이는 예제입니다.

import { create } from 'zustand'

const useStore = create((set) => ({
  count: 1,
  inc: () => set((s) => ({ count: s.count + 1 })),
}))

function Counter() {
  const { count, inc } = useStore()
  return (
    <div>
      <span>{count}</span>
      <button onClick={inc}>one up</button>
    </div>
  )
}

물론 이 코드를 그대로 복사해도 문제없이 잘 돌아가는듯 합니다.

그러나 프로젝트가 커지면서 '원인 모를 리렌더'가 빈번히 발생하고,

추적끝에 Zustand를 사용하면서 발생한 문제라는 것을 알게됩니다.

 

셀렉터 개념을 알아야 해요!

다음 예시코드를 통해 테스트 해 봅시다.

const useAnimalStore = create<AnimalState>((set) => ({
  bear: 0,
  shark: 0,
  addBear: () => set((state) => ({ bear: state.bear + 1 })),
  addShark: () => set((state) => ({ shark: state.shark + 1 })),
}));

function WholeBox() {
  const { bear } = useAnimalStore();
  console.log(`WholeBox render & bear:${bear}`);
  return <></>;
}

function SelectorBox() {
  const bear = useAnimalStore((state) => state.bear);
  console.log(`SelectorBox render & bear:${bear}`);
  return <></>;
}

export default function Page() {
  const addBear = useAnimalStore((state) => state.addBear);
  const addShark = useAnimalStore((state) => state.addShark);
  return (
    <>
      <button onClick={addBear}>addBear</button>
      <button onClick={addShark}>addShark</button>
      <SelectorBox />
      <WholeBox />
    </>
  );
}

 

Add Bear 버튼과 Add Shark버튼 UI가 존재하며, 클릭 시 각각에 해당하는 state(bear, shark)를 증가시킵니다.

여기서 집중해서 볼것은 bear를 사용하고 있는 WholeBox와 SelectorBox의 리렌더링에 따른 console.log 출력 여부입니다.

컴포넌트 bear 사용을 위해 작성한 Store 호출 방식 콘솔 출력(리렌더)을 트리거하는 버튼
WholeBox const { bear } = useAnimalStore(); Add Bear, Add Shark
SelectBox const bear = useAnimalStore((state) => state.bear); Add Bear

두 컴포넌트 모두 스토어에서 bear만 사용하는 것 같은데 shark의 상태를 변경해도 WholeBox에서는 불필요하게 리렌더링이 발생합니다.

차이점을 발생시킨 것은 두 컴포넌트의 useAnimalStore 호출 방식입니다.

1. WholeBox(셀렉터 문법 미적용)

 const { bear } = useAnimalStore(); 

  • 공식홈페이지를 들어가자 마자 보았던 예제
  • 스토어 전체 객체를 반환(구독)
  • 상태가 하나라도 바뀌면 새로운 스토어 객체가 생성됨
  • 스토어 전체를 구독하는 컴포넌트는 모두 리렌더됨

2. SelectorBox(셀렉터 문법 적용)

 const bear = useAnimalStore((state) => state.bear); 

  • 공식문서 중 Render Optimization에서 설명하고 있는 예제
  • 셀렉터가 해당 state만 골라서 반환(구독)
  • 다른 상태가 바뀌더라도 해당 state만 구독하는 컴포넌트는 리렌더되지 않음
저작자표시 비영리 변경금지 (새창열림)

'React' 카테고리의 다른 글

React 컴포넌트가 순수해야 하는 이유  (0) 2025.07.04
React Core 구현하기 - 2. JSX 런타임 로직  (0) 2025.02.18
React Core 구현하기 - 1. JSX 컴파일  (0) 2025.02.14
React18 useEffectEvent  (0) 2024.12.30
React useLayoutEffect  (2) 2024.12.27
'React' 카테고리의 다른 글
  • React 컴포넌트가 순수해야 하는 이유
  • React Core 구현하기 - 2. JSX 런타임 로직
  • React Core 구현하기 - 1. JSX 컴파일
  • React18 useEffectEvent
URE
URE
Skill: Javascript, ReactJS, Next.js, React Native ... *블로그 이전 작업(24. 04. 11 ~ 24. 04. 15)
  • URE
    Dev++
    URE
  • 전체
    오늘
    어제
    • 분류 전체보기 (51)
      • Browser (6)
      • OS (1)
      • Javascript (14)
      • React (19)
      • Next.js (4)
      • React Native (0)
      • Architecture (1)
      • Network (3)
      • 스크랩 (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    react
    react18
    Port
    javascript
    원시타입
    리액트 코어
    리액트
    상태관리라이브러리
    참조타입
    리덕스
    JS
    TCP
    URL
    Redux
    IP
    dataFetching
    swr
    자바스크립트
    react concurrent
    브라우저
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
URE
Zustand 셀렉터... 사용하고 계시죠? (리렌더 폭탄 방지하기)
상단으로

티스토리툴바