검색 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
가 호출될 때마다 새로운 배열이 참조되었던 것이었다.
그렇다.. 이 부분이 문제의 원인이었다.
useSelector
는 useState
와 같은 개념으로, 같은 데이터를 가지고 있더라도 배열이 새로 생성되면 다른 값으로 인식해서 불필요한 리렌더링을 유발한다고 한다.
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하기