Blog
test: 성능 개선 사례
test: 성능 개선 사례
관련 게시글
8개
test: tailwind2
test: tailwind2
test: next
test: next
test: supabase
test: supabase
test: tools
test: tools
test: 개발 과정 기록과 회고
test: 개발 과정 기록과 회고
test: 렌더링 최적화
test: 렌더링 최적화
test: tailwind
test: tailwind

React 19: Effect 내부에서 setState 호출 시 발생하는 경고(cascading render)와 해결 방법
React 19과 React Compiler 환경에서 프로젝트를 진행하면서 `useEffect` 내부에서 `setState`를 호출할 때 **cascading render 경고**가 발생했다. 이전 버전에서는 문제가 없던 코드이지만, React 19 Compiler는 초기 렌더 흐름을 더 엄격하게 검사한다. 해당 경고가 발생하는 원리와 해결 방법을 정리한다. ## 문제 코드 ```tsx const [isOpen, setIsOpen] = useState(false); useEffect(() => { if (typeof window !== "undefined") { const saved = localStorage.getItem("SidebarOpen"); if (saved !== null) { setIsOpen(saved === "true"); } } }, []); ``` ### 많은 사람들이 오해할 수 있는 부분 1. effect가 반복 실행돼서 발생하는 문제는 아니다 useEffect(() => {}, [])는 기본적으로 한 번만 실행된다. 문제는 effect 실행 횟수가 아니라 렌더 후 상태가 뒤늦게 변경되는 구조다. 2. localStorage 값에 따라 effect가 재실행되는 것은 아니다 localStorage의 내용은 effect 동작과 무관하다. 3. cascading render가 무한 루프를 의미하는 것은 아니다 React 19에서 말하는 “cascading render”는 초기 렌더 후 추가 렌더가 이어지는 비효율적인 초기화 과정을 의미한다. ### 왜 경고가 발생하는가 React Compiler는 초기 렌더 흐름이 다음과 같이 진행되는 경우를 비효율적이라고 판단한다. ```javascript 초기 render (isOpen = false) ↓ commit ↓ useEffect 실행 ↓ setIsOpen(...) ↓ re-render ``` 초기 렌더에서 실제 상태가 확정되지 않은 채 commit이 발생하기 때문에 Compiler는 이를 “초기 상태가 늦게 확정되는 패턴”으로 보고 경고를 띄운다. ### 해결 방법: 초기 상태를 렌더 전에 확정하기 localStorage에서 값을 읽는 로직을 useEffect가 아닌 useState의 lazy initializer로 옮기면 경고가 해결된다. ### 수정된 코드 ```javascript const [isOpen, setIsOpen] = useState(() => { if (typeof window !== "undefined") { const saved = localStorage.getItem("SidebarOpen"); return saved === "true"; } return false; }); ``` ### 정리 | 방식 | 렌더 횟수 | React Compiler 평가 | | ------------------------- | --------- | ------------------------ | | effect 안에서 setState | 최소 2번 | 비효율적, 경고 발생 가능 | | useState lazy initializer | 1번 | 가장 이상적인 패턴 |