Auto Generating Selectors

김동현·2026년 3월 4일

zustand 공식문서 번역

목록 보기
13/19

선택자(Selectors) 자동 생성하기

스토어에서 속성이나 액션을 꺼내 쓸 때는 선택자(selectors)를 사용하는 것을 강력히 추천해요. 보통은 아래처럼 스토어의 값에 접근하실 거예요.

const bears = useBearStore((state) => state.bears)

하지만 매번 상태를 가져올 때마다 이렇게 화살표 함수를 작성하는 건 꽤 번거롭고 지루한 작업이 될 수 있어요. 만약 이 과정이 귀찮게 느껴지신다면, 선택자를 자동으로 생성해서 쓰는 아주 멋진 방법이 있답니다!

💡 강사의 실무 팁:
리액트(React.js)나 넥스트JS(Next.js)로 프로젝트를 진행하다 보면 상태 관리해야 할 값들이 정말 많아지죠. 이때 선택자를 자동 생성하는 유틸리티를 프로젝트 구조에 미리 세팅해 두면 코드 타이핑 양도 확 줄어들고 코드 베이스가 훨씬 깔끔해집니다. 나중에 개인 포트폴리오를 만들 때 이런 디테일한 개발자 경험(DX) 개선 코드를 녹여내면, 단순히 라이브러리를 쓰는 것을 넘어 유지보수성까지 깊게 고민하는 개발자로 보여서 면접관들에게 아주 좋은 인상을 줄 수 있어요!

다음 함수를 만들어보세요: createSelectors

import { StoreApi, UseBoundStore } from 'zustand'

type WithSelectors<S> = S extends { getState: () => infer T }
  ? S & { use: { [K in keyof T]: () => T[K] } }
  : never

const createSelectors = <S extends UseBoundStore<StoreApi<object>>>(
  _store: S,
) => {
  const store = _store as WithSelectors<typeof _store>
  store.use = {}
  for (const k of Object.keys(store.getState())) {
    ;(store.use as any)[k] = () => store((s) => s[k as keyof typeof s])
  }

  return store
}

만약 여러분이 작성한 스토어가 아래와 같이 생겼다고 가정해 볼게요.

interface BearState {
  bears: number
  increase: (by: number) => void
  increment: () => void
}

const useBearStoreBase = create<BearState>()((set) => ({
  bears: 0,
  increase: (by) => set((state) => ({ bears: state.bears + by })),
  increment: () => set((state) => ({ bears: state.bears + 1 })),
}))

이제 아까 만든 createSelectors 함수를 방금 만든 스토어에 쓱 씌워주기만 하면 됩니다.

const useBearStore = createSelectors(useBearStoreBase)

짠! 이제 선택자들이 모두 자동으로 만들어졌어요. 굳이 콜백 함수를 쓰지 않아도 .use.를 통해 상태나 액션에 바로 접근할 수 있습니다.

// 속성(Property) 가져오기
const bears = useBearStore.use.bears()

// 액션(Action) 가져오기
const increment = useBearStore.use.increment()

💡 강사의 추가 팁:
이렇게 설정해 두면 타입스크립트의 자동완성(IntelliSense) 기능이 완벽하게 작동해요. IDE에서 useBearStore.use.까지만 쳐도 내부에 있는 상태들이 목록으로 쫙 뜨니까, 오타도 줄일 수 있고 개발 속도가 훨씬 빨라질 겁니다!


바닐라 스토어 (Vanilla Store)

리액트 컴포넌트 외부나 순수 자바스크립트 환경에서 바닐라 스토어를 사용하고 계신다면, 아래의 createSelectors 함수 버전을 사용해 주세요.

import { StoreApi, useStore } from 'zustand'

type WithSelectors<S> = S extends { getState: () => infer T }
  ? S & { use: { [K in keyof T]: () => T[K] } }
  : never

const createSelectors = <S extends StoreApi<object>>(_store: S) => {
  const store = _store as WithSelectors<typeof _store>
  store.use = {}
  for (const k of Object.keys(store.getState())) {
    ;(store.use as any)[k] = () =>
      useStore(_store, (s) => s[k as keyof typeof s])
  }

  return store
}

사용하는 방법은 리액트 스토어 방식과 완전히 똑같아요. 여러분의 바닐라 스토어가 아래와 같다면요:

import { createStore } from 'zustand'

interface BearState {
  bears: number
  increase: (by: number) => void
  increment: () => void
}

const store = createStore<BearState>()((set) => ({
  bears: 0,
  increase: (by) => set((state) => ({ bears: state.bears + by })),
  increment: () => set((state) => ({ bears: state.bears + 1 })),
}))

아까처럼 함수를 스토어에 씌워 줍니다.

const useBearStore = createSelectors(store)

이제 똑같이 자동 생성된 선택자를 이용해 바로바로 접근할 수 있죠!

// 속성(Property) 가져오기
const bears = useBearStore.use.bears()

// 액션(Action) 가져오기
const increment = useBearStore.use.increment()

라이브 데모 (Live Demo)

직접 코드를 만져보면서 눈으로 확인해 보는 게 이해하기 가장 좋겠죠? 실제로 어떻게 동작하는지 이 Code Sandbox 링크에서 테스트해 볼 수 있어요!


서드파티 라이브러리 (Third-party Libraries)

이런 유틸리티 함수를 프로젝트에 직접 작성하기 번거롭거나, 좀 더 확장된 기능이 필요하다면 이미 훌륭하게 만들어진 서드파티 라이브러리들을 활용해 보는 것도 실무에서 아주 흔히 쓰는 방식이에요. 아래 링크들을 참고해 보세요!



이전 글: Advanced TypeScript Guide
다음 글: Setup with Next.js

profile
프론트에_가까운_풀스택_개발자

0개의 댓글