정답: A 1, C 0, B 1이 순서대로 출력된다.

You clicked 0 times

Console

    이전 렌더링의 클린업은 다음 렌더링의 컴포넌트가 브라우저에 그려진 이후에 실행된다.


    각 렌더링마다 코드들이 어떤 순서대로 실행되는지 자세하게 알아보겠다.

    1. 첫 번째 렌더링

      • 컴포넌트 내부의 코드가 실행된다. 즉, count, setCount가 정의되고 콘솔에 A 0이 출력된다.
      • 렌더링 결과물인 <p>You clicked 0 times</p>... 가 화면에 그려진다.
      • useEffect의 셋업이 실행된다. 즉, 콘솔에 B 0이 출력된다.
    2. (버튼을 클릭하여) 두 번째 렌더링

      • 컴포넌트 내부의 코드가 실행된다. 즉, count, setCount가 정의되고 콘솔에 A 1이 출력된다.
      • 렌더링 결과물인 <p>You clicked 1 times</p>... 가 화면에 그려진다.
      • useEffect의 클린업이 실행된다. 즉, 콘솔에 C 0이 출력된다.
      • useEffect의 셋업이 실행된다. 즉, 콘솔에 B 1이 출력된다.
    3. (버튼을 한 번 더 클릭하여) 세 번째 렌더링

      • 컴포넌트 내부의 코드가 실행된다. 즉, count, setCount가 정의되고 콘솔에 A 2이 출력된다.
      • 렌더링 결과물인 <p>You clicked 2 times</p>... 가 화면에 그려진다.
      • useEffect의 클린업이 실행된다. 즉, 콘솔에 C 1이 출력된다.
      • useEffect의 셋업이 실행된다. 즉, 콘솔에 B 2이 출력된다.

    ...

    이처럼 리액트는 항상 페인팅 이후 useEffect를 다룬다. 이펙트가 스크린 업데이트를 가로막지 않기 때문에 앱을 더 빠르게 만들어준다.

    function Input() {
      const [text, setText] = useState("");
     
      useEffect(() => {
        console.log(text);
        setText("default");
      });
     
      return (
        <div>
          {text}
          <input
            value={text}
            onChange={(e) => {
              setText(e.target.value);
            }}
          />
        </div>
      );
    }

    예를 들어 위와 같이 코드를 짜면 입력한 글자가 (아무 짧은 시간 동안이지만) 화면에 보인다는 것을 확인할 수 있다. 이 역시 리액트가 페인팅 이후에 useEffect를 처리하기 때문이다.

    참고로 개발 환경에서 해당 컴포넌트의 동작은 다음과 같다.

    • 맨 처음에 마운트되면 콘솔에 A 0, A 0, B 0, C 0, B 0을 출력한다.
    • 버튼을 한 번 누르면 콘솔에 A 1, A 1, C 0, B 1을 출력한다.
    • 버튼을 한 번 누르면 콘솔에 A 2, A 2, C 1, B 2을 출력한다.
    • ...

    배포 환경과 다르게 개발 환경에서 더 많은 출력이 생기는 이유는 다음의 두 규칙을 통해 이해할 수 있다. Strict Mode가 활성화 되어있을 경우,

    1. 리액트는 컴포넌트가 순수 함수(pure function)인지 확인하기 쉽도록 하기 위해 기본적으로 렌더링을 2번씩 진행한다.
    2. 리액트는 useEffect의 셋업 및 클린업 로직이 잘 작성되었는지 확인하기 쉽도록 하기 위해 실제 셋업 이전에 한 번씩의 셋업과 클린업을 추가적으로 실행한다.

    참고자료