본문 바로가기
React-study/dil

[모던 리액트 Deep Dive] 6장 리액트 개발 도구로 디버깅하기

by 어느새벽 2024. 11. 18.

 

6.1 리액트 개발 도구란?

  • 리액트 애플리케이션의 개발 도구인 react-dev-tools가 있다. 브라우저에 리액트 개발도구를 브라우저 확장 도구로 설치하여 사용할 수 있다. 
  • 설치하면 우측 상단에 리액트 로고가 표시돼있는데 회색이면 사용할 수 없고, 빨간색이면 정상 작동, 파란색이면 배포된 웹사이트도 사용할 수 있다는 뜻이다.

6.2 리액트 개발 도구 설치

https://ko.react.dev/learn/react-developer-tools

 

React Developer Tools – React

The library for web and native user interfaces

ko.react.dev

 

6.3 리액트 개발 도구 활용하기

6.3.1 컴포넌트 Components

  • 현재 리액트 애플리케이션의 컴포넌트 트리를 확인할 수 있다. props와 내부 hooks 등 다양한 정보를 확인할 수 있다.

컴포넌트 트리

  • components의 왼쪽 영역은 해당 리액트 페이지의 컴포넌트 트리를 나타낸다.
  • 기명 함수로 선언되었으면 해당하는 컴포넌트명을 보여주고, 익명 함수면 Anonymous라는 이름으로 보여준다 .
  • 함수 선언식과 함수 표현식으로 생성한 컴포넌트는 모두 함수명으로 표시 가능하지만 이외에 컴포넌트는 다음과 같은 문제를 확인할 수 있다.
더보기

익명 함수를 default로 export한 컴포넌트는 _default로 표시된다.

 

memo를 사용해 익명 함수로 만든 컴포넌트를 감싼 경우, 함수명을 명확히 추론하지 못해서 Anonymous로 표시됐다. 추가로 memo 라벨을 통해 memo로 감싸진 컴포넌트임을 알 수 있다.

 

고차 컴포넌트도 Anonymous로 선언된다.

  • 16.9 버전 이후부터는 일부 명칭을 추론할 수 없는 Anonymous가 _c3, _c5 등으로 개선되어 이전보다는 가독성이 좋아졌지만 여전히 개발도구에서 컴포넌트를 특정하기 어렵다.
  • 디버깅을 하는데에 확실한 도움을 얻으려면 컴포넌트를 기명 함수로 선언하는 것이 좋다.
  • 기명 함수로 바꾸기 어렵다면 함수에 displayName 속성을 추가하는 방법도 있다. 
  • 디버깅을 위해 사용하는 함수명과 displayName는 개발 모드에서만 사용하는 것이 좋다.
const MemoizedComponent = memo(function (){
	return <>MemoizedComponent</>
})

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

//이렇게 하면 컴포넌트 트리에서 '메모 컴포넌트입니다.' 확인 가능 !

 

 

 

컴포넌트명과 props

  • 왼쪽 컴포넌트 트리에서 해당 컴포넌트에 대한 자세한 내용을 보여주는 영역이다.

 

1. 컴포넌트명과 key

  • key는 kidsValueProp, Anonymous 익명함수인 컴포넌트명이다.
  • 빨간 경고 이모티콘은 해당 애플리케이션이 strict mode로 렌더링되지 않음을 의미한다.

2. 컴포넌트 도구

  • 아이콘 : 해당 컴포넌트가 HTML의 어디에서 렌더링됐는지 확인할 수 있다.
  • 벌레 아이콘 : 콘솔탭에서 해당컴포넌트의 정보가 console.log를 실행해 기록된 것을 확인할 수 있다. 여기에는 컴포넌트가 받는 props, 컴포넌트 내부에서 사용하는 hooks, 컴포넌트의 HTML 요소인 nodes가 기록된다.
  • 소스코드 아이콘 : 해당 컴포넌트의 소스코드를 확인할 수 있다. 좌측하단의 {} 을 클릭하여 읽기 쉬운 형태로 변경된다.

 3. 컴포넌트 props

  • 해당 컴포넌트가 받은 props 확인 가능하다.
  • props 우클릭 시 Store as global variable→ window.$r에 해당 정보가 담긴다. → 콘솔로 이동하면 해당 변수에 대한 정보가 담겨있다.
  • props 클릭 시 Go to definition → 해당 함수가 선언됨 코드로 이동한다. + 값을 더블클릭하면 내용 수정 가능하다.

4. 컴포넌트 hooks

  • 컴포넌트에서 사용 중인 훅 정보를 확인할 수 있다.
  • useState는 use가 생략된다.
  • 훅도 기명함수로 넘겨주면 훅이 실행할 때 실행되는 함수의 이름을 확인할 수 있다.
  • props와 마찬가지로 더블 클릭 시 원하는 값으로 수정할 수 있다.

5. 컴포넌트를 렌더링한 주체, rendered by

  • 해당 컴포넌트를 렌더링한 주체가 누구인지 확인할 수 있다.

 

6.3.2 프로파일러

  • 리액트가 렌더링하는 과정에서 발생하는 상황을 확인하기 위한 도구이다.
  • 렌더링되는 과정에서 어떤 컴포넌트가 렌더링됐는지, 몇 차례나 렌더링이 일어났으며 어떤 작업에서 오래 걸렸는지 등 컴포넌트 렌더링 과정에서 발생하는 일을 확인할 수 있다.
import React, { ChangeEvent, useEffect, useState } from 'react'

export default function App() {
  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>
  )
}

 

설정 변경하기

 

  • General >> Hightlight updates when components render :  컴포넌트가 렌더링될 때마다 해당 컴포넌트에 하이라이트를 표시한다. 매우 유용한 기능으로 꼭 켜두는 것이 좋다.
  • Debugging >> Hide logs during second render in Strict Mode : 리액트 애플리케이션이 엄격 모드에서 실행되는 경우, 원활한 디버깅을 위해 useEffect 등이 두번씩 작동한다. 이를 막고 싶다면 해당 버튼을 활성화하면 된다.
  • Profiler >> Record why each component rendered while profiling : 프로파일링 도중 무엇 때문에 컴포넌트가 렌더링됐는지 기록한다. 애플리케이션 속도가 조금 느려질 수는 있지만 디버깅에 도움이 되는 옵션이므로 켜두는 것이 좋다.

 

프로파일링

프로파일링 메뉴

  • 리액트가 렌더링할 때 어떠한 일이 벌어지는지 확인할 수 있는 도구다.

 

 

 

  • A : 프로파일링이 시작된다. 시작되면 버튼이 적색으로 바뀐다. 다시 누르면 프로파일링이 중단되고 결과가 나타난다. 
  • B : 새로고침버튼, 새로고침 동시에 프로파일링이 시작된다. 마찬가지로 다시 누르면 프로파일링이 중단되고 결과가 나온다. 새로고침이 끝나도 프로파일링은 중단되지 않아 A버튼을 눌러야 한다.
  • C : 프로파일링 종료버튼으로 기록을 모두 삭제한다.
  • 위아래 화살표 버튼 : 각각 프로파일 불러오기, 프로파일 저장하기 버튼이다. 
  • D : 세션 중 커밋 목록을 보여주는 커밋 차트
  • E : 세션 중에 렌더링된 구성 요소를 보여주는 구성 요소 목록
  • F : E와 같은 구성요소 목록을 표시하는 플레임 차트 버튼
  • G : 순위가 매겨진 차트버튼, 구성요소 목록을 순위에 따라 표시한다.

 

 

 

 

Flamegraph (F)

  • 렌더 커밋별로 어떠한 작업이 일어났는지 나타낸다.
  • 바가 넓을수록 해당 컴포넌트를 렌더링하는데 오래 걸렸다는 의미다.
  • 렌더링되지 않은 컴포넌트에 대한 정보도 확인할 수 있다. 'Did not render'

Ranked (G)

  • 해당 커밋에서 렌더링하는 데 오랜 시간이 걸린 컴포넌트를 순서대로 나열한 그래프다.
  • 렌더링이 발생한 컴포넌트만 보여준다.

타임라인 (캘린더 이모티콘)

  • 시간이 지남에 따라 컴포넌트에서 어떤 일이 일어났는지를 확인할 수 있다.
  • 리액트 18 이상의 환경에서만 확인할 수 있다.
  • 시간 단위로 프로파일링 기간 동안 무슨 일이 있었는지, 무엇이 렌더링됐고, 어느 시점에서 렌더링됐는지, 리액트의 유휴 시간은 어느 정도였는지 등 자세히 확인할 수 있다.

프로파일러로 렌더링 원인 파악해서 수정해 보기

  • 위 예시 코드는 최초 렌더링 외에 사용자가 아무런 작동을 하지 않았음에도 두번째 렌더링이 발생한 상황이다.

 

1. 화살표를 누르거나 보고 싶은 커밋을 클릭한다. 두번째 렌더링 발생 이유를 위해 두번째 렌더링 커밋을 누른다.

2. App에서 발생한 것을 알 수 있다.

 

3. App 내부에 훅이 변경됐음을 확인할 수 있다.

4. Hook 1 changed는 컴포넌트 코드에서 가장 먼저 선언된 훅이라는 뜻이다. 

 

5. hooks에서 state를 확인한다.

6. 타임라인 도구를 통해 어느 시점에서 렌더링 됐는지 확인한다.

7. 타임라인을 살펴보면 약 3000ms 경에 App의 state 변화가 발생했음을 알 수 있다.

8. 이 렌더링을 발생시킨 범인은 useEffect 함수 내부에서 3초 뒤에 실행되는 setTimeout이다. 이 useEffcet를 제거하고 다시 실행하면 렌더링이 한번만 일어나는 것을 확인 할 수 있다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

[참고:  https://bori-note.tistory.com/36 [보리의 FE 개발 노트:티스토리] ]