[TIL-0511] Zustand의 getState() 함수

jiny·2025년 5월 13일

캡스톤2

목록 보기
5/22

🌟 전역 상태 타입 정의

AI 설문 선택 응답에 관련된 상태를 관리하기 위해 전역 상태의 타입을 다음과 같이 정의하였다.

// optionalSurvey.store.ts
interface OptionalSurveyState {
  transportation: string[]; // 교통수단
  preferTravelPurpose: string[]; // 선호하는 여행 목적
  nonPreferTravelPurpose: string[]; // 비선호하는 여행 목적
  preferAccommodation: string[]; // 선호하는 숙박 시설
  nonPreferAccommodation: string[]; // 비선호하는 숙박 시설
  preferRestaurant: string[]; // 선호하는 식당 유형
  nonPreferRestaurant: string[]; // 비선호하는 식당 유형
  setTransportation: (transportation: string[]) => void; // 교통수단 설정
  setPreferTravelPurpose: (purpose: string[]) => void; // 선호하는 여행 목적 설정
  setNonPreferTravelPurpose: (purpose: string[]) => void; // 비선호하는 여행 목적 설정
  setPreferAccommodation: (accommodation: string[]) => void; // 선호하는 숙박 시설 설정
  setNonPreferAccommodation: (accommodation: string[]) => void; // 비선호하는 숙박 시설 설정
  setPreferRestaurant: (restaurant: string[]) => void; // 선호하는 식당 유형 설정
  setNonPreferRestaurant: (restaurant: string[]) => void; // 비선호하는 식당 유형 설정
}

🌟 전역 상태 관리

Zustand를 이용하여 useSurveyStore 안에 set 함수를 모두 다음과 같은 형식으로 정의하였다.

setTransportation: (transportation: string[]) => set({ transportation });

그 후에 다른 파일에서 다음과 같이 사용하였더니 에러가 발생하였다.

setTransportation((prev: string[]) => [...prev, buttonName]);
  • 에러

    Argument of type '(prev: string[]) => string[]' is not assignable to parameter of type 'string[]'.
  • 에러 원인 분석

    • setTransportation의 타입은 현재 다음과 같이 정의되어 있음

      setTransportation: (transportation: string[]) => void;

      ➡️ 즉, string[] 값 그 자체를 받도록 되어 있음

    • 그런데 사용한 코드는

      setTransportation((prev: string[]) => [...prev, buttonName]);
      • 여기서는 setTransportation함수를 인자로 넘기고 있음
      • prev => [...prev, buttonName]은 함수인데, 타입 정의에서는 string[]만 받을 수 있도록 해놨기 때문에 타입 에러가 발생함
  • 에러 해결 방법
    setTransportation이 string[]만 받도록 정의되어 있으니, getState() 함수를 이용하여 이전 상태를 직접 가져와서 처리해야 함

    const current = useSurveyStore.getState().transportation;
    setTransportation([...current, buttonName]);

    ➡️ 이렇게 하면 prev 함수 없이도 같은 효과를 얻을 수 있음


🌟 getState()란?

  • Zustand에서 만든 store 객체가 가지고 있는 메서드 중 하나로, 현재 store의 상태(state) 값을 직접 가져오는 함수

  • 컴포넌트 외부나 이벤트 리스너 같은 React의 리렌더링 흐름과 무관한 코드에서 상태 값을 읽을 때 사용됨

  • 사용 예시

    const current = useSurveyStore.getState().transportation;

    이 코드는 다음과 같은 일을 함

    1. useSurveyStore.getState() : 현재 store의 전체 상태 객체를 가져옴
    2. .transportation : 그 상태 중 transportation 값만 꺼냄

🌟 getState() 사용 상황

  1. store 상태를 함수 바깥에서 조회할 때
    Zustand의 hook(useSurveyStore)은 리액트 컴포넌트 내부에서만 사용 가능하지만, getState()컴포넌트 외부이벤트 핸들러, 비동기 로직 안에서도 사용할 수 있음

    const handleClick = () => {
      const prev = useSurveyStore.getState().transportation;
      useSurveyStore.getState().setTransportation([...prev, "택시"]);
    };
  2. 이전 상태를 기반으로 업데이트 할 때
    Zustand는 set()에 이전 상태를 함수형으로 넘겨줄 수 있지만, setter가 값만 받도록 만들어졌다면 이렇게 해야 함

    const prev = useSurveyStore.getState().transportation;
    setTransportation([...prev, newValue]);

🌟 getState() 함수 주의사항

  • getState()현재 상태를 한 번만 가져옴
    ➡️ 따라서 이 값을 계속 감지하거나 반응하려면 useSurveyStore((state) => state.xxx) 같은 훅을 써야 함

  • 상태가 변경되어도 getState()는 자동으로 반응하지 않음
    ➡️ 리렌더링과는 관련이 없음

    • React의 useStateuseSurveyStore(state => ...)처럼 hook을 쓰면?

      const transportation = useSurveyStore((state) => state.transportation);
      • transportation 값이 바뀌면 컴포넌트가 자동으로 리렌더링
      • 즉, 상태가 바뀌면 → React가 감지 → UI를 다시 그림 (자동 반응)
    • useSurveyStore.getState()는?

      const current = useSurveyStore.getState().transportation;
      • 이건 그 순간의 값만 가져옴 (snapshot)
      • 상태가 바뀌어도 자동으로 다시 불러오거나 업데이트되지 않음
      • React 컴포넌트는 이 값을 보고도 변화에 리렌더링하지 않음

🌟 get() vs. getState()

  • get() : 스토어 생성 함수 내에서 사용하는 함수

    export const useStore = create(
      (set, get) => ({
        someValue: 0,
        increment: () => {
          const current = get().someValue;
          set({ someValue: current + 1});
        },
      })
    );
    • get()스토어 생성 함수의 인자로 주어지는 함수임
    • 해당 시점에서는 훅(useStore)이 아직 만들어지기 전이므로, get()을 이용해서 현재 상태를 참조함
    • 즉, create(...)() 안에서만 사용할 수 있음
  • getState() : 만들어진 스토어 객체에서 직접 사용하는 메서드

    const state = useStore.getState();
    console.log(state.someValue);
    • getState()useStore 훅 객체가 가진 메서드

    • 리액트 컴포넌트 바깥(예: 유틸 파일, 이벤트 리스너, 외부 모듈)에서 상태를 동기적으로 직접 가져오고 싶을 때 사용함

    • useStore.getState()를 쓰면 현재 저장된 스토어의 전체 상태를 즉시 반환

    • [주의] getState()create()가 반환하는 store 객체의 메서드라서 store가 생성되기 전에는 사용할 수 없음
      ➡️ 즉, create(...)() 안에서는 사용할 수 없음

    • [주의] useStore.state.someValue처럼 직접 접근하는 건 불가능

      useStore의 정체는 무엇인가?

      const useStore = create(...);

      이렇게 만든 useStore는 사실 다음과 같은 훅(hook)이자 함수 객체

      • useStore() : 컴포넌트 안에서 Reack Hook처럼 사용
      • useStore.getState() : 현재 상태 동기 접근
      • useStore.setState() : 외부에서 상태 직접 변경
      • useStore.subscribe() : 외부에서 상태 구독

      즉, useStore는 상태를 관리하는 특수한 함수 객체이며, state라는 직접 속성은 존재하지 않음

      🤔 useStore.state.someValue는 어떻게 되나?

      console.log(useStore.state.someValue);
      • 컴파일은 될 수도 있지만,
      • 런타임에서 undefined 오류가 남 → state라는 속성은 useStore 객체에 존재하지 않기 때문임

      상태를 정확히 가져오려면?

      반드시 이렇게 해야 함

      const state = useStore.getState();
      console.log(state.someValue);

      또는 리액트 컴포넌트 안에서 사용할 경우에는, 다음과 같이 해도 됨

      const someValue = useStore((state) => state.someValue);
  • 표로 비교하기

    구분get()getState()
    사용 위치create() 안에서만 사용 가능어디서든 사용 가능 (컴포넌트 밖 등)
    용도스토어 초기 생성 시 현재 상태 참조현재 스토어 상태 동기적으로 가져오기
    인자로 받는가(set, get) => ({ ... }) 형태로 받음useStore.getState()처럼 메서드 형태
    예시get().someValueuseStore.getState().someValue

0개의 댓글