[번역] React에서 "다음 렌더"란 무엇인가?
Soshy·
왜 상태는 즉시 업데이트되지 않을까?
React를 어느 정도 사용해본 적 있다면, 아마 이런 코드를 마주친 적이 있을 것이다:
setLoading(false);
console.log(loading);
false가 출력될 거라 예상했지만, 실제로는 이전 값인 true가 출력된다.
처음엔 이상하게 느껴진다. 방금 상태를 업데이트했는데, 왜 바로 반영되지 않는 걸까?
이 동작을 이해하려면, React에서 "다음 렌더(next render)"가 정확히 무엇을 의미하는지 명확하게 알아야 한다.
React에서 "렌더"란 무엇인가?
React에서 컴포넌트는 기본적으로 JSX를 반환하는 함수다:
function App() {
return <div>{name}</div>;
}
상태(state)나 props가 변경될 때마다, React는 이 함수를 다시 실행해 업데이트된 UI를 만들어낸다. 이 재실행 과정을 렌더(render) 라고 한다.
렌더란, 쉽게 말해 컴포넌트 함수를 다시 실행해서 다음 UI 결과물을 만드는 것이다.
"다음 렌더"는 언제 일어나는가?
setState를 호출할 때, React는 상태를 즉시 업데이트하지 않는다. 대신 다음과 같이 동작한다.
- 상태 업데이트를 큐(queue)에 등록한다
- 여러 업데이트를 배치(batch)로 묶는다
- 이후 새로운 렌더를 트리거한다
이때 발생하는 컴포넌트의 새 실행이 바로 다음 렌더다.
setState는 업데이트를 예약할 뿐, 즉시 적용하지 않는다.
실제 예시
초기 상태가 이렇다고 가정해보자.
const [loading, setLoading] = useState(true);
const [name, setName] = useState<string | undefined>(undefined);
그리고 다음과 같은 이펙트가 있다.
useEffect(() => {
getPerson().then((person) => {
setLoading(false);
setName(person.name);
console.log("State values:", loading, name);
});
}, []);
person.name이 "Bob"이라면, 이렇게 출력될 거라 기대할 것이다.
State values: false Bob
하지만 실제 출력은 다음과 같다.
State values: true undefined
왜일까?
console.log가 실행되는 시점에는 아직 다음 렌더가 일어나지 않았기 때문이다. React는 업데이트를 큐에 등록만 했을 뿐, 아직 적용하지 않은 상태다.
실행 흐름 (단계별)
1. 초기 렌더
loading = true
name = undefined
React가 컴포넌트를 렌더링한다.
2. useEffect 실행
getPerson() Promise가 호출된다.
3. Promise 완료
.then() 내부에서 React가 이 업데이트들을 큐에 등록한다.
setLoading(false);
setName("Bob");
4. console.log 실행
아직 다음 렌더가 일어나지 않았으므로, 값은 여전히 다음과 같다.
loading = true
name = undefined
5. 다음 렌더 발생
React가 업데이트된 상태로 컴포넌트를 재실행하면, UI가 그에 맞게 업데이트된다.
loading = false
name = "Bob"
React가 이렇게 동작하는 이유
만약 React가 모든 상태 업데이트를 즉시 적용한다면
- 각 업데이트마다 렌더가 발생하고
setState호출 여러 개가 각각 렌더를 유발하며- 성능이 크게 저하될 것이다
그 대신 React는 업데이트를 배치로 처리한다.
setState 3번 호출
↓
렌더 1번
이 방식 덕분에 React 애플리케이션은 더 효율적이고 예측 가능하게 동작한다.
JavaScript 클로저의 역할
한 가지 더 중요한 개념이 있다: JavaScript 클로저다.
useEffect가 실행될 때, 해당 렌더 시점의 상태 값을 캡처한다. 즉, 이펙트 내부의 console.log는 상태 업데이트가 예약됐더라도 여전히 이전 값을 참조하고 있다.
이는 React의 버그가 아니라, 정상적인 JavaScript 동작이다.
업데이트된 상태에 접근하는 방법
업데이트된 상태 값을 사용하고 싶다면, 별도의 이펙트에서 구독하면 된다.
useEffect(() => {
console.log("Updated values:", loading, name);
}, [loading, name]);
이 이펙트는 loading 또는 name이 변경된 렌더 이후에 실행되므로, 항상 최신 상태를 출력한다.
마무리
setState는 상태를 즉시 업데이트하지 않는다- React는 업데이트를 큐에 등록하고, 다음 렌더에서 적용한다
- 컴포넌트는 업데이트된 상태로 재실행된다
- 업데이트된 값은 다음 렌더 이후에만 사용 가능하다
- 이 렌더링 모델을 이해하면, React의 동작이 훨씬 예측 가능하고 명확해진다
이 렌더링 모델을 완전히 이해하고 나면, React를 쓰면서 겪었던 많은 혼란스러운 순간들이 자연스럽게 납득되기 시작할 것이다.