[React] Deep Dive 모던 리액트(19) 리액트 개발 도구
FrontEnd/Deep Dive

[React] Deep Dive 모던 리액트(19) 리액트 개발 도구

728x90

 

 

 

프로그래밍에서 가장 중요한 것 중 하나는 디버깅이다. 코드에서 잘못된 부분을 파악하고 수정을 할 수 있게 해주는 방법이기 때문이다.

리액트에서는 리액트로 개발된 애플리케이션의 디버깅을 돕기 위한 리액트 내장 도구인 react-dev-tools를 제공한다.

 

이 react-dev-tools는 리액트 뿐 아니라 리엑트 네이티브 등 다양한 플랫폼에서 사용할 수 있다. 브라우저 확장 프로그램을 사용하면 이를 쉽게 사용할 수 있다.

 

크롬

 

 

페이지에 접속했을때 아래와 같이 뜬다면 정상적으로 리액트 개발 도구를 사용할 수 있다는 뜻이다.

 

 

 

리액트를 활용해 정상적인 배포를 진행중이라면 아래와 같이 나오게 된다.

 

 

만약 비 정상적인 접근이거나 리액트로 만든 페이지가 아니라면 아래처럼 나온다.

 

 

리액트 개발 도구를 키면 개발자도구에 아래와 같이 못보던 옵션들이 생긴다.

 

 

 

책의 저자를 따라서 넷플릭스 페이지를 분석해보자!

 

컴포넌트

현재 리액트 애플리케이션의 컴포넌트 트리를 확인할 수 있다. 컴포넌트 구조뿐 아니라 props 나 hooks에 대한 정보들도 확인이 가능하다.

이는 트리 형태로 나타나 있어 컴포넌트의 구조를 파악하기가 매우 편리하다.

 

 

 

 

만약 함수 이름이 익명으로 되어있다면 Anonymous 라는 이름으로 컴포넌트를 보여준다.

 

 

 

 

memo를 사용하여 컴포넌트를 감싼 경우 Anonymous로 표시

고차컴포넌트로 감싼 경우또한 Anonymous로 표시

 

 

리액트 16.9 버전 이상을 쓰고있다면 이와 같은 문제가 조금은 해결되었다. 일부 명칭을 추론할 수 없는 함수가 _c3 _c5 등으로 조금은 추론이 가능하다.

 

하지만 이조차도 컴포넌트를 특정하기는 어렵다. 따라서 리액트 코드를 작성하는 경우에 기명함수로 작성하는 것이 개발자 도구를 활용하는데 더 유리할 것이다. 만약 함수를 기명 함수로 바꾸기 어렵다면 함수에 displayName속성을 추가하는 것도 방법이 된다.

 

const MemorisedComponent = memo(function(){
	return <>MemorizedComponent</>
})

MemoizedComponent.displayName = '메모컴포넌트입니다'

 

 

특히 고차컴포넌트의 경우 위와같은 방법을 적용하기 좋을 것이다. 물론 이와같은 방법을 적용해도 압축 도구 등이 컴포넌트 등을 난수화하기 때문에 빌드한 트리를 분석하는 것은 어려우며 개발 도구에서만 제한적으로 참고하는게 좋다는 점을 알아두자.

 

 

컴포넌트명과 props

컴포넌트와 받고있는 props가 무엇인지 알려준다.

 

 

1. 빨간 경고는 해당 애플리케이션이 strict mode로 렌더링되지 않았음을 의미한다.

2. 상단의 두번째 벌레 아이콘을 누르면 내가 선택한 컴포넌트에 대한 정보를 콘솔창에 띄워준다.

 

 

3. 좌측 하단의 { } 을 누르면 난독화되어있던 코드를 적절한 여백과 줄바꿈을 거쳐 읽기 쉬운 형태로 변경된다.

 

 

4. 컴포넌트 props

컴포넌트가 받은 props를 확인할 수 있으며 원시값 , 함수 모두 보여준다.

 

5. Copyvalue to clipboard를 하면 복사를 할 수 있다.

6. Store as global variable 버튼을 누르면 해당 값에 들어있는 값을 콘솔 창에 띄워준다.

 

 

 

 

 

 

프로파일러

 

컴포넌트 메뉴가 정적인 현재 리액트 컴포넌트 트리의 내용을 디버깅한다면 프로파일러는 리액트가 렌더링 하는 과정에서 발생하는 상황을 볼 수 있다.

 

어떤 컴포넌트가 렌더링 되었는지 , 몇 차례나 렌더링이 일어났는지 등등 렌더링 과정에서 발생하는 일에 대해 알아볼 수 있다.

 

넷플릭스

 

프로파일링을 지원하지 않으면 위와같은 화면이 뜬다 (넷플릭스는 지원하지 않는다.)

 

 

프로파일링을 실험하기 위해서 일부러 약간 문제있는 코드를 작성해보았다.

/** @jsxImportSource @emotion/react */
import React, { ChangeEvent, useEffect, useState } from "react";

const MyComponent = () => {
  const [text, setText] = useState("");
  const [number, setNumber] = useState(0);
  const [list, setList] = useState([
    { name: "apple", amount: 5000 },
    { name: "orange", amount: 1000 },
    { name: "watermelon", amount: 1500 },
    { name: "pineapple", amount: 500 },
  ]);

  useEffect(() => {
    setTimeout(() => {
      console.log("surprise!");
      setText("1000");
    }, 3000);
  });

  function handleTextChange(e: ChangeEvent<HTMLInputElement>) {
    setText(e.target.value);
  }

  function handleSubmit() {
    setList((prev) => [...prev, { name: text, amount: number }]);
  }

  function handleNumberChange(e: ChangeEvent<HTMLInputElement>) {
    setNumber(e.target.valueAsNumber);
  }

  return (
    <div>
      <input type="text" value={text} onChange={handleTextChange} />
      <button onClick={handleSubmit}>추가</button>
      <input type="number" value={number} onChange={handleNumberChange} />
      <ul>
        {list.map((value, key) => (
          <li key={key}>
            {value.name} {value.amount}원
          </li>
        ))}
      </ul>
    </div>
  );
};

/* STYLE */

export default MyComponent;

 

 

보면 입력을 해도 조금있다가 사라지는 모습을 볼 수 있다.

 

 

Profiler 탭의 General 속성에서 Highlight updates when components render 를 누르면 컴포넌트가 렌더링될때마다 해당 컴포넌트에 하이라이트가 표시되게 된다.

'
요런식으로 민트색 ? 테두리가 생긴다.

 

 

 

아래 5가지 버튼의 기능을 알아보자.

 

 

 

1. StartProfiling

해당 버튼을 누르면 프로파일링이 시작된다.

2. Reload and Start profiling 

해당버튼을 누르면 새로고침을 하면서 프로파일링을 시작한다.

3. stop profiling

프로파일링 된 현재 내용을 모두 지운다.

4. Load Profile

프로파일링 결과를 불러온다.

5. Save Profile 

프로파일링 결과를 저장한다.

 

 

프로파일링을 해본 후 방금만든 페이지를 조금 만져본 후 불꽃(FlameGraph) 부분을 보자. 

 

 

해당 컴포넌트의 렌더링과 관련된 정보를 확인할 수 있을 뿐 아니라 렌더링이 되지 않은 컴포넌트에 대한 정보들또한 알 수 있다.

회색으로 표시되면 리렌더링이 되지 않은 페이지이다.

 

아래처럼 노란색에 가까울 수록 렌더링이 오래걸린 페이지이다. 

 

 

 

이제 방금 만든 페이지의 문제점을 분석해보자.

 

아무것도 안한 최초화면

 

아무것도 진행하지 않은 최초 화면임에도 렌더링이 두번되어있다. 

 

 

그 이유를 자세히 보면 자기 자신임을 알 수 있다.

hook이 변경됨

 

잘 보면 hook이 1000으로 변경되었다는 점을 알 수 있따.

 

즉 코드 중간에 State를 1000으로 만드는 코드가 있다는 것이다.

useEffect(() => {
    setTimeout(() => {
      console.log("surprise!");
      setText("1000");
    }, 3000);
  });

코드중에 위와같이 setTimeout을 이용하여 text를 바꾸는 코드가 있는데 이 코드가 오류의 원인 중 하나일 것이다 해당 코드를 없애보자.

 

 

이제 기능은 잘 동작한다.

 

 

 

하지만 위와같이 input에 값을 입력할 때마다 MyComponent 전체가 리렌더링이 되고 있음을 확인할 수 있다. 성능 향상을 위해서 input을 별도의 컴포넌트로 분리해 보자.

 

 

/** @jsxImportSource @emotion/react */
import React, { ChangeEvent, useEffect, useState } from "react";

function InputText({ onSubmit }: { onSubmit: (text: string) => void }) {
  const [text, setText] = useState("");

  function handleSubmit() {
    onSubmit(text);
  }

  function handleTextChange(e: ChangeEvent<HTMLInputElement>) {
    setText(e.target.value);
  }

  return (
    <>
      <input type="text" value={text} onChange={handleTextChange} />
      <button onClick={handleSubmit}>추가</button>
    </>
  );
}

const MyComponent = () => {
  const [text, setText] = useState("");
  const [number, setNumber] = useState(0);
  const [list, setList] = useState([
    { name: "apple", amount: 5000 },
    { name: "orange", amount: 1000 },
    { name: "watermelon", amount: 1500 },
    { name: "pineapple", amount: 500 },
  ]);

  function onSubmit(text: string) {
    setList((prev) => [...prev, { name: text, amount: number }]);
  }

  function handleNumberChange(e: ChangeEvent<HTMLInputElement>) {
    setNumber(e.target.valueAsNumber);
  }

  return (
    <div>
      <InputText onSubmit={onSubmit}></InputText>
      <input type="number" value={number} onChange={handleNumberChange} />
      <ul>
        {list.map((value, key) => (
          <li key={key}>
            {value.name} {value.amount}원
          </li>
        ))}
      </ul>
    </div>
  );
};

/* STYLE */

export default MyComponent;

 

 

이제 입력을 해도 MyComponent 전체가 아닌 일부만 리렌더링 된다는 것을 제대로 확인할 수 있다.

 

 

 

이를 활용하면 렌더링이 일어나는 실제 과정과 컴포넌트들을 시각적으로 확인하는 것이 가능하여 매우 효율적인 개발을 가능하게 해준다.

 

리액트 개발 도구를 활용하면 생성된 컴포넌트 트리를 보는 것부터 프로파일링을 통해 리액트 애플리케이션이 시간이 지남에 따라 어떤식으로 작동하는지 , 불필요한 리렌더링은 일어나지 않는지 알 수 있다.

 

 

물론 실제 프로젝트는 트리 구조도 복잡하기때문에 해당 도구를 사용해도 디버깅이 쉽지 않을 수 있지만 그래도 틈틈히 개발 도구를 활용해 보는것이 좋다. 특히 메모이제이션 , 최적화 등을 확인해보면서 프로그램을 짜는 습관을 들이는 것이 좋다. 

 

 

메모이제이션.. 렌더링 과정을 해당 개발도구로 볼 수 있다..? 지금 진행하는 프로젝트에도 한번 적용해 봐야겠다.

 

 

728x90