[Redux] useSelector 최적화 – 불필요한 리렌더링 방지 (배열 반환)

hyejinJo·2025년 4월 18일
0

React

목록 보기
16/16
post-thumbnail

검색 AI 기능쪽에서 현재 AI 답변 결과는 컨텐츠에 따라 문단으로 분류되어 데이터가 내려오는데, 그 컨텐츠의 title 값을 한글 제목으로 치환해야 했다. 그래서 redux 의 state 값을 활용하던 중 아래와 같은 useSelector 를 사용한 쪽에서 에러가 발생했고, 내가 사용한 곳 외에도 해당 state 값을 불러온 모든 부분에서 같은 에러가 발생하고 있었다 😱

“Selector unknown returned a different result when called with the same parameters. This can lead to unnecessary rerenders.
Selectors that return a new reference (such as an object or an array) should be memoized:”

리덕스에서 common 의 enum 값은 다음과 같은데, 내가 사용하고자 한 것은 ‘ai-search-category-type’ 라는 데이터였다.

commonSlice.ts

import { createSlice } from '@reduxjs/toolkit';
import { createSelector } from '@reduxjs/toolkit';

import { AppState } from '../types';

type Enum = {
  key: string;
  value: any;
};

const initialState = {
  enum: {},
};

const commonSlice = createSlice({
  name: 'common',
  initialState,
  reducers: {
    setEnum: (state, actions) => {
      state.enum = actions.payload;
    },
  },
});

export const { setEnum } = commonSlice.actions;

// Selectors
export const selectEnums =
  (selectors: string[] = []) =>
  ({ common }: AppState) => {
    const result: Enum[][] = [];
    selectors.forEach((selector) => result.push(common.enum[selector]));
    return result;
  };

export default commonSlice.reducer;

selectEnums 활용:

가져오고자 하는 enum 데이터의 key 값이 담긴 배열을 인수로 넣어 사용

import { useSelector } from 'react-redux';
import { selectEnums } from '@common/persistence/store/slices/commonSlice';

const [aiSearchCategoryTypeList] = useSelector(selectEnums(['ai-search-category-type']));

문제점

기존 코드에서 selectEnums 는 반환되는 값의 형태가 배열이다. 이때 result는 매번 새로운 배열([])로 생성되는데 이는 결국 useSelector 가 호출될 때마다 새로운 배열이 참조되었던 것이었다.

그렇다.. 이 부분이 문제의 원인이었다.

useSelectoruseState와 같은 개념으로, 같은 데이터를 가지고 있더라도 배열이 새로 생성되면 다른 값으로 인식해서 불필요한 리렌더링을 유발한다고 한다.

해결

createSelector 로 메모이제이션 적용

현재 selectEnums는 Reselect의 createSelector를 사용하지 않아서, 캐싱 없이 무조건 새로운 배열을 반환하고 있었다. 여기서 Reselect 는 기본적으로 메모이제이션을 제공하는 라이브러리이다.

createSelector 는 새로운 값을 반환할 때 다시 리렌더링 되더라도 이전의 로직을 수행하는 대신 캐싱해두었던 값을 반환해준다.

즉, 같은 입력이 들어왔을 때 기존 값을 재사용할 수 있다는 것이다.

import { createSelector } from '@reduxjs/toolkit';

...

export const selectEnums = (selectors: string[]) =>
  createSelector(
    (state: AppState) => state.common.enum,
    (enums) => selectors.map((selector) => enums[selector] || []),
  );

createSelector 로 코드를 수정한 후 다시 실행해보니 말끔히 사라진 에러를 확인할 수 있었다.

참고:

https://velog.io/@hahagarden/useSelector-최적화로-리렌더링-줄이기
https://velog.io/@2ast/React-redux-toolkit의-createSelector로-state-reselect하기

profile
Frontend Developer 💡

0개의 댓글