본문 바로가기
framework_library/react

React가 다시 그리기로 결정하는 정확한 순간들

by 죄니안죄니 2026. 1. 9.
React가 다시 그리기로 결정하는 정확한 순간들React가 다시 그리기로 결정하는 정확한 순간들React가 다시 그리기로 결정하는 정확한 순간들
 
 
 

렌더링은 언제 발생하는가

— React가 다시 그리기로 결정하는 정확한 순간들

지난 글에서 핵심 공식을 하나 남겼다.

UI = f(state)

이번 글에서는 이 질문을 끝까지 판다.

React는 정확히 언제, 어떤 조건에서 다시 렌더링을 하는가?

이걸 모르면

  • “왜 여기서 렌더가 되지?”
  • “왜 이 컴포넌트는 안 바뀌지?”
  • “왜 성능이 터지지?”

같은 질문을 평생 반복하게 된다.


1️⃣ React 렌더링의 정확한 정의

먼저 용어부터 정리한다.
실무에서 가장 많이 헷갈리는 부분이다.

React에서 말하는 “렌더링”이란

컴포넌트 함수가 다시 실행되어 JSX를 다시 계산하는 것

❌ DOM에 바로 그리는 것
❌ 화면이 실제로 바뀌는 것

⭕ 함수 실행
⭕ Virtual DOM 트리 생성

 
function Counter({ count }) {
  console.log("렌더링 발생");
  return <div>{count}</div>;
}

위 코드에서 console.log가 찍히면
렌더링은 이미 발생한 상태다.
DOM 반영은 그 다음 단계다.


2️⃣ 렌더링을 발생시키는 3가지 트리거 (실무 핵심)

React에서 렌더링을 유발하는 건 딱 세 가지다.

① state 변경

 
const [count, setCount] = useState(0);

setCount(1);
  • 가장 흔함
  • 값이 같아도 setState가 호출되면 렌더 후보가 된다
    (단, Object.is 비교로 bail-out 되는 경우 있음)

② props 변경

 
<Child value={count} />
<Child value={count} />
  • 부모가 렌더링되면
  • 자식은 props 비교 후 렌더 여부 판단

👉 그래서 “부모 렌더 = 자식 렌더”처럼 느껴진다.


③ context 값 변경

 
const ThemeContext = createContext();

<ThemeContext.Provider value={theme}>
  • Provider의 value가 바뀌면
  • 해당 context를 구독 중인 모든 컴포넌트가 렌더 후보

👉 전역 상태 남용이 위험한 이유다.


3️⃣ 렌더링 ≠ DOM 업데이트 (아주 중요)

이 코드를 보자.

function App() {
  const [count, setCount] = useState(0); // 구조분해할당 return [stateValue, setStateFunction]

  console.log("App 렌더");

  return (
    <>
      <button onClick={() => setCount(count)}>증가</button>
      <div>{count}</div>
    </>
  );
}

버튼을 눌러도 로그는 찍힌다.
하지만 화면은 안 바뀐다.

왜?

  • 렌더링은 발생했지만
  • 이전 결과와 새 결과가 같아서
  • DOM 업데이트 단계에서 스킵되기 때문이다

👉 React는
렌더링과 커밋(commit)을 분리해서 생각해야 한다.


4️⃣ 부모 렌더링은 왜 자식을 흔드나

실무에서 가장 많이 터지는 오해다.

function Parent() {
  const [count, setCount] = useState(0);

  return (
    <>
      <button onClick={() => setCount(c => c + 1)}>+</button>
      <Child />
    </>
  );
}

function Child() {
  console.log("Child 렌더");
  return <div>자식</div>;
}

버튼을 누르면 Child 렌더가 찍힌다.

이유는 단순하다.

  • Parent 함수가 다시 실행됨
  • JSX를 다시 만들면서 <Child />도 다시 평가됨
  • Child 함수도 실행됨

👉 부모 렌더 → 자식 렌더는 기본 동작이다.


5️⃣ 그럼 React.memo는 뭘 막는가

 
const Child = React.memo(function Child() {
  console.log("Child 렌더");
  return <div>자식</div>;
});

이제 버튼을 눌러도 로그가 안 찍힌다.

React.memo는

  • props가 바뀌지 않으면
  • 자식 렌더링을 스킵한다

여기서 중요한 점.

React.memo는 “렌더링 최적화 도구”이지
“상태 관리 도구”가 아니다.

남용하면 구조 이해가 더 어려워진다.


6️⃣ useEffect는 렌더링 이후에 실행된다

이 순서를 정확히 기억해야 한다.

  1. state/props 변경
  2. 컴포넌트 함수 실행 (렌더링)
  3. DOM 반영
  4. useEffect 실행
 
useEffect(() => {
  console.log("effect 실행");
}, [count]);

그래서 이런 코드는 위험하다.

useEffect(() => {
  setCount(count + 1);
}, [count]);

👉 렌더 → effect → setState → 렌더
👉 무한 루프의 전형적인 시작점


7️⃣ 실무 기준으로 기억해야 할 한 문장

이 문장 하나면 렌더링 사고가 정리된다.

React는 상태 변경이 감지되면
전체 컴포넌트 트리를 다시 계산하고,
바뀐 부분만 DOM에 반영한다

그래서 우리가 해야 할 일은 단 하나다.

  • 렌더링을 막으려 하지 말고
  • 렌더링이 싸도록 구조를 만든다

다음 글 예고

다음 글에서는 이 질문으로 간다.

👉 “useEffect는 왜 위험한가”

  • 왜 side effect가 렌더링 사고를 만든다 말하는지
  • 어떤 effect는 써도 되고
  • 어떤 effect는 구조가 잘못된 신호인지

실무에서 가장 많이 터지는 지점을
코드로 하나씩 해부해볼 예정이다.

댓글