리액트 코드 어떤 순서로 실행될까?

홍정민·2024년 7월 11일
0

C언어, Java 등 프로그래밍 언어를 작성할 때, 우리는 코드를 위에서 아래로 작성한다. 또한 우리는 작성한 코드를 위에서 아래로 순서대로 읽고 코드의 순서를 예측할 수 있다.

그러나 리액트를 처음 접하면 기존에 알고 있던 절차지향 개념과 다르게 코드의 순서가 예상과 다르게 실행 되거나 예측하지 못하는 경우가 많을 것 이다.

코드의 실행 순서를 알고 예측할 수 있는 것은 개발에 아주 중요한 요소이다. 따라서 리액트를 운용 한다면 코드의 실행순서 즉, 생명주기를 리액트 컴포넌트의 생명주기를 아는 것이 중요하다.

컴포넌트 코드의 실행 순서를 예측할 수 있나요?

다음은 버튼을 누르면 count 상태를 증가시키는 기본 예제이다. 해당 코드가 어떤 순서로 실행되는지 스스로 파악 해 보자.

예제

import { useState, useEffect } from 'react';

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

  useEffect(() => {
    console.log(`Count has changed to: ${count}`);

    return () => {
      console.log('Component will unmount');
    };
  }, [count]); 

  console.log(`Component render`);
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increase Count</button>
    </div>
  );
}

export default Counter;

리액트 생명주기

예제 코드 실행 순서를 알기 위해서는 리액트 생명주기를 알아야 한다. 클래스 컴포넌트와 함수형 컴포넌트의 생명주기 두 종류가 있는데, 공식문서에서 클래스 컴포넌트는 지양하므로 함수형 컴포넌트를 알아보자.

리액트 컴포넌트에는 총 3가지 상태가 존재한다.

  • (생성) Mounte
  • (변경) Update
  • (제거) UnMount

최대한 간단하게 설명하자면 다음과 같다.

Mount

요소가 생성되어 브라우저에 그려진 상태

Update

이벤트가 발생하여 컴포넌트가 관여하는 State가 변경되는 상태

UnMount

요소가 제거되어 브라우저에서 없어진 상태

생명주기를 다루는 함수

컴포넌트가 생성 됐을 때, 업데이트 됐을 때, 제거 됐을 때의 상황에서 코드를 실행시키고 싶을 것 이다. useEffect 해당 상황에서 우리의 코드를 실행할 수 있게 해준다.

useEffect는 기본적인 내용이기 때문에 사용법에 대해서는 다루지 않겠다.

컴포넌트 코드 실행 순서

코드가 어떤 순서로 동작하는지 예측하기 위해서 생명주기를 공부해야 할 것이다. 따라서, 이론도 좋지만 주 목적인 컴포넌트의 코드가 어떤 순서로 실행되는지 알아보는데 중점을 두도록 하자.

예제 코드

부모 컴포넌트와 자식 컴포넌트가 있고, 버튼을 통해 자식 컴포넌트를 생성하고 제거하는 예제이다.

function Counter() {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    console.log('Component mounted');
    
    return () => {
      console.log('Component unmounted');
    };
  }, [count]);
  
  const incrementCount = () => {
    setCount(count + 1);
  };
  
  console.log('Component render');
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={incrementCount}>Increment</button>
    </div>
  );
}

Mount

컴포넌트가 최초 실행되어 요소가 생성되는 상황

  1. 함수 내부 코드를 순차적으로 실행
  2. return 함수를 실행
  3. useEffect 내부 코드를 실행

Update

Increment 버튼을 눌러 이벤트가 발생되고 count 상태가 변경된 상황

  1. 함수 내부 코드를 순차적으로 실행
  2. return 함수를 실행
  3. count가 변경되기 이전의 useEffectreturn 실행
  4. count 상태 종속성이 포함된 useEffect 내부 코드를 실행

UnMount

컴포넌트가 제거된 상황

  1. useEffect 내부의 return 부분을 실행 시킨다.

UnMount는 컴포넌트를 제거할 수 있는 부모 자식 컴포넌트 예제에서 자세히 알아 보도록 하겠다.

앞에서 말했던 것 처럼, 예제를 통해 useEffect로 컴포넌트의 생명주기를 관리할 수 있다는 것을 알았다.

부모 자식 관계 컴포넌트 코드 실행 순서

단일 컴포넌트 생명주기 예제에서는 쉽게 이해할 수 있을 것 이다. 더 나아가, 부모 자식 관계의 컴포넌트 코드가 어떤 순서로 실행되는지 알아보자.

해당 예제를 이해하는 것이 중요하니 코드 예제를 잘 파악 하도록 하자.

코드 예제

부모와 자식 컴포넌트가 있고, 버튼을 통해 자식 컴포넌트를 생성 또는 제거하는 예제이다.

const ParentComponent = () => {
  const [showChild, setShowChild] = useState(true);

  useEffect(() => {
    console.log('ParentComponent mounted');
    
    return () => {
      console.log('ParentComponent unmounted');
    };
  }, []);

  const toggleChild = () => {
    setShowChild(!showChild);
  };

  console.log('ParentComponent render');
  return (
    <div>
      <button onClick={toggleChild}>Click</button>
      {showChild && <ChildComponent />}
    </div>
  );
};

const ChildComponent = () => {
  useEffect(() => {
    console.log('ChildComponent mounted');
    
    return () => {
      console.log('ChildComponent unmounted');
    };
  }, []);

  console.log('ChildComponent render');
  return (
    <div>
      <h2>Child Component</h2>
    </div>
  );
};

Mount

컴포넌트가 최초 실행되는 상황

  1. ParentComponent 내부 코드 실행
  2. ParentComponentreturn 실행
  3. ChilceComponent 내부 코드 실행
  4. ChilceComponentreturn 실행
  5. ChilceComponentuseEffect 실행
  6. ParentComponentuseEffect 실행

Update

부모 컴포넌트 버튼을 클릭하여 showChild 상태를 변경시킨 상황

  1. ParentComponent내부 코드 실행
  2. ParentComponentreturn실행
  3. ChilceComponentuseEffect내부 return 실행
  4. ParentComponent의 변경되기 이전useEffectreturn 실행
  5. ParentComponentuseEffect실행

UnMount

showChild 상태가 false로 상태가 바뀌어, 자식 컴포넌트가 제거 될 때의 상황

  1. ParentComponent내부 코드 실행
  2. ParentComponentreturn실행
  3. ChilceComponentuseEffect내부 return 실행
  4. ParentComponentshowChild 종속성이 담긴 useEffect실행

컴포넌트 업데이트에서 클린업 함수가 실행?

컴포넌트 예제를 실행시켜 보며, UnMount가 발생하면 useEffect내부의 return함수가 발생 한다는 것을 알았다. 그런데 컴포넌트 업데이트를 하는데도 클린업 함수가 발생 된다는 것을 알게 된다.

그 이유를 클린업 함수의 정의를 보고 이해 알아보자.

클린업 함수
useEffect내부의 return 함수를 말하며, 컴포넌트가 언마운트되거나 다음 렌더링 전에 실행되는 함수입니다.

즉, 상태가 업데이트 되어 리렌더링이 되는 경우 이전 상태의 useEffect의 클린업 함수를 발생 시키고, 그 다음 useEffect를 발생 시키는 것 이다.

debugger로 직접 코드 실행 순서 확인하기

나의 예제를 읽기 귀찮다면 debugger 키워드로 코드의 실행 순서를 확인 해 볼 수 있다. 개발자 도구를 켠 상태에서 웹을 실행 시키면 작동한다.

사용 예시

const Component = () => {
  debugger 
  ...
  
  return (
    ...
  )
}

리액트 생명주기 도표의 궁금증

debugger를 활용하여 예제를 직접 실행해 보면 UpdatingMounting 처럼 function () {}return ()을 실행 한다. 그러나 도표의 Updating에는 이 부분이 없다.

이 부분에 대해 아는 것이 있다면 댓글로 알려주시면 감사하겠습니다.!

0개의 댓글