일단 발생되는 코드는 아래와 같다.
export function usePublishingGroupQuery() {
const { businessId, propertyId } = useAuth();
const {
data: publishingGroup,
isLoading: isPublishingGroupLoading,
isFetching: isPublishingGroupFetching,
error: publishingGroupError,
refetch: refetchPublishingGroup,
} = useQuery({
enabled: !!businessId && !!propertyId,
queryKey: generateDeployGroupsKey(),
queryFn: () => fetchDeploysGroups({ business_id: businessId, property_id: propertyId }),
staleTime: Infinity,
select: (data: PublishingGroupResponseData) => data.result.deployGroups,
});
const deployedPublishingGroup = useMemo(() => publishingGroup?.filter((data) => data.project_id), [publishingGroup]);
return {
publishingGroup,
deployedPublishingGroup,
isPublishingGroupLoading,
isPublishingGroupFetching,
publishingGroupError,
refetchPublishingGroup,
};
}
const TVPortal: React.FC<TVPortalProps> = ({ setPublishGroup, publishGroup }) => {
const { t } = useTranslation();
const { deployedPublishingGroup } = usePublishingGroupQuery();
const { editor } = useEditorQuery();
const { setPublishData, publishData } = useRoomManagerStore((state) => state.publish);
useEffect(() => {
if (editor && deployedPublishingGroup) {
const _publishData = deployedPublishingGroup.map((data) => {
const findOne = editor.result?.projects?.find((_data) => _data.project_id === data.project_id);
const modified_date = findOne?.project?.modified_date;
return {
...data,
modified_date,
project_id: data.project_id,
};
});
if (_publishData?.length > 0) {
setPublishData(_publishData as TVPublishDeployData[]);
setPublishGroup(_publishData[0].deploy_id);
}
}
}, [editor, deployedPublishingGroup, setPublishData, setPublishGroup]);
...... // 나머지는 생략
}
usePublishingGroupQuery에서 deployedPublishingGroup을 다음과 같이 정의했을 때:
const deployedPublishingGroup = publishingGroup?.filter((data) => data.project_id);
이 코드가 매 렌더링마다 새로운 배열 객체를 생성하게 되면서, TVPortal 컴포넌트의 useEffect가 무한 호출되는 문제가 발생하였다.
deployedPublishingGroup은 배열을 새로 생성하기 때문에, 값이 같더라도 참조가 매번 달라짐
useEffect는 참조가 바뀌면 실행되므로, 렌더링마다 실행됨
setPublishData는 상태를 변경하므로 또다시 렌더링 발생 → 무한 루프
const { setPublishData, publishData } = useRoomManagerStore((state) => state.publish);
만약에 useEffect에서 setPublishData호출하는 부분이 없어 스토어에 구독하는 데이터가 없다면 TVPortal은 리렌더링이 되지 않을것이다. 그러나 데이터가 변경됨을 감지함으로써 리렌더링이 발생하고 deployedPublishingGroup 데이터가 변경이 됨으로써 또 다시 무한호출로 이어진다.
스토어의 데이터가 변경되어야 하고, 변경후에는 리렌더링이 되어야 한다. 그렇다면 무한호출을 막을 수 있는 방법은 deployedPublishingGroup의 데이터의 값자체가 변경되지 않았음에도 불구하고 새로운 데이터 객체가 생성됨으로써 새로운 데이터라고 판단하는 부분을 고쳐야한다.
그렇기에 useMemo훅을 사용해서 실제 데이터값이 변경되었는지를 확인하고 의존성 배열로써 publishingGroup이 변경될떄만 새로운 객체를 생성해서 해결하였다.
const deployedPublishingGroup = useMemo( () => publishingGroup?.filter((data) => data.project_id), [publishingGroup]);
publishingGroup이 변경될 때만 deployedPublishingGroup을 새로 계산
값이 변경되지 않으면 같은 참조를 유지하므로 useEffect가 다시 실행되지 않음
결과적으로 불필요한 리렌더링과 무한 호출을 방지