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

공효은·2024년 5월 1일
0

react

목록 보기
10/11
post-thumbnail

6.1리액트 개발 도구란?

리액트 팀은 리액트 애플리케이션의 원활한 개발을 위한 개발 도구인 react-dev-tools를 만들어 제공하고 있다. 웹 개발 환경에서 가장 편리하게 사용할 수 있는 방법은 브라우저 확장 프로그램을 사용하는 것이다.

6.2리액트 개발 도구 설치

브러우저에 리액트 개발 도구를 브라우저 확장 도구로 설치해야한다.
나는 크롬을 쓰니까 크롬 익스텐션 설치

https://chromewebstore.google.com/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=ko

리액트 로고가 회색으로 표시된다면?

👉 리액트 개발 도구가 정상적으로 접근할 수 없는 페이지 or 리액트로 개발되지 않은 페이지.

리액트 로고가 빨간색으로 표시된다면?

👉 개발 모드인 리액트 웹 애플리케이션에 정상적으로 리액트 개발 도구가 접근할 수 있다는 의미 (next.js 애플리케이션 돌려봄)

리액트 로고가 원래 고유의 색깔인 파란색으로 표시된다면?

👉 현재 웹페이지가 리액트 프로덕션 모드로 빌드되어 실행되고 있다는 뜻 (velog)

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

리액트 개발 도구가 정상적으로 설치 됐고, 디버깅 할 수 있는 페이지에 접근했다면 크롬 개발자 도구에 두 가지 메뉴가 추가된 것을 확인할 수 있다.

Components, Profiler 가 추가 되었다.

이번 장에서 디버깅에 사용할 사이트는 넷플릭스 홈페이지인 https://www.netflix.com/kr/ 이다.

6.3.1컴포넌트

Components 탭에서는 현재 리액트 애플리케이션의 컴포넌트 트리를 확인할 수 있다. 단순히 컴포넌트의 구조뿐만 아니라 props와 내부 hooks등 다양한 정보를 확인할 수 있다.

컴포넌트 트리

Components의 왼쪽 영역은 해당 리액트 페이지의 컴포넌트 트리를 나타낸다. 이름 그대로 트리 구조로 구성돼 있으며, 리액트 애플리케이션 전체의 트리구조를 한눈에 보여준다.

데모

리액트 버전 18.2.0

기명 함수로 선언되어 컴포넌트명을 알 수 있다면 해당 컴포넌트명을 보여주고, 만약 익명 함수로 선언돼 있다면 Anonymous라는 이름으로 컴포넌트를 보여준다.

  • 익명함수를 default로 export 한 AnonymousDefaultComponent의 경우 AnonymousDefaultComponent는 코드 내부에서 사용되는 이름일 뿐, 실제로 default export로 내보낸 함수의 명칭은 추론할 수 없다. 따라서 _default로 표시된다.

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

  • 고차 컴포넌트인 withSampleHOC로 감싼 HOCComponent의 경우 각각 Anonymous, _c5 로 선언돼 있다.

임의로 선언된 명칭으로는 개발 도구에서 컴포넌트를 특정하기 어렵다. 이러한 문제를 해결하기 위해 컴포넌트를 기명함수로 변경하자.

이전보다 훨씬 더 명확하게 컴포넌트 명칭을 확인할 수 있다.
이처럼 컴포넌트를 기명 함수로 선언하는 것은 개발 도구에서 확인하는 데 많은 도움을 준다.

만약 함수를 기명 함수로 바꾸기 어렵다면 함수에 displayName 속성을 추가하는 방법도 있다.

고차 컴포넌트의 경우 이러한 기법을 유용하게 사용할 수 있다.
고차 컴포넌트는 일반적으로 고차컴포넌트와 일반 컴포넌트의 조합으로 구성되므로 displayName을 잘 설정하면 디버깅하는 데 많은 도움이 된다.

displayName 설정 전

displayName 설정 후

displayName과 함수명은 개발 모드에서만 제한적으로 참고하는게 좋다.
-> 넷플릭스 홈페이지의 컴포넌트 트리 처럼 리액트를 빌드한 트리를 확인하는 경우 기명함수로 선언한다 해도 압축 도구 등이 컴포넌트명을 단순하게 난수화 하기 때문에 확인하기가 어려워 진다!

컴포넌트명과 props

컴포넌트명과 Key

컴포넌트의 명칭과 해당 컴포넌트를 나타냄

컴포넌트 도구

컴포넌트 도구에는 3개의 아이콘이 있다. 각 아이콘의 용도는 다음과 같다.

  • 첫 번째 눈 아이콘을 누르면 해당 컴포넌트가 HTML의 어디에서 렌더링 됐는지 확인할 수 있다. 누르는 즉시 크롬 개발 도구의 메뉴 중 하나인 요소(Element) 탭으로 즉시 이동하며, 해당 컴포넌트가 렌더링한 HTML 요소가 선택되는 것을 볼 수 있다.
  • 두 번째 벌레 아이콘을 클릭하면 아무것도 일어나지 않는 것 처럼 보인다. 클릭하느 순간 콘솔(console) 탭에 해당 컴포넌트의 정보가 console.log를 실행해 기록된 것을 확인할 수 있다.
    개발 도구 화면에서 보기에는 복잡한 정보를 확인하거나 또는 해당 정보를 복사하는 등의 용도로 확인하고 싶다면 클릭 후 콘솔 탭을 확인하자. 여기에는 해당 컴포넌트가 받는 props, 컴포넌트 내부에서 사용하는 hooks, 컴포넌트의 HTML 요소인 nodes가 기록된다.

  • 세번째 소스코드 아이콘을 클릭하면 해당 컴포넌트의 소스코드를 확인할 수 있다. 그러나 해당 아이콘을 누르면 조금 당황스러운 화면이 펼처진다(이는 소스코드가 프로덕션 모드에서 빌드되어 최대로 압축돼있기 때문이다.) {} 아이콘을 누르면 난독화돼 있던 코드가 읽기 쉬운 형태로 변한다. (프로덕션 모드에서만 적용되는듯하다.)

컴포넌트 props

해당 컴포넌트가 받은 props를 확인할 수 있다. 단순한 원시값뿐 아니라 함수도 포함돼 있다.
여기서 마우스 오른쪽 버튼을 클릭하면 해당 props 정보를 복사하는 'Copy value to clipboard'와 'Store as blobal variable' 버튼이 나온다.


Store as blobal variable를 선택하고 콘솔로 이동해보면 해당 변수에 대한 정보가 담겨있다.


값이 함수인 props를 누르면 Go to definition이 나타나는데, 이를 클릭하면 해당 함수가 선언된 코드로 이동한다. 해당 값을 원하는 내용으로 수정할 수도 있다.

컴포넌트 hooks

컴포넌트에서 사용 중인 훅 정보를 확인할 수 있다. 여기서 useState는 State와 같이 use가 생략된 이름으로 나타난다.

다음은 리액트 개발자 도구에서 볼 수 있는 훅 목록이다.

  • State: useState
  • Reducer:useReducer
  • Context: useContext
  • Memo: useMemo
  • Callback:useCallback
  • Ref: useRef
  • id: useId
  • LayoutEffect: useLayoutEffet
  • Effect:useEffect
  • Counter와 같이 리액트에서 제공하는 훅이 아닌 경우: 이는 useXXX로 선언돼 있는 사용자 정의 훅이다. Counter라면 useCounter라는 훅이 있다는 듯이다.

훅도 마찬가지로 훅에 넘겨주는 함수를 익명 함수 대신 기명 함수로 넘겨주면 해당 훅을 실행할 때 실행되는 함수의 이름을 확인할 수 있다.

대부분 useEffect에는 익명 함수를 인수로 넘겨주기 때문에 useEffect가 여러 개 선언돼 있다면 어떤 훅인지 구별하기 어렵다. 이 경우 기명 함수로 선언한다면 개발 도구를 더욱 유용하게 이용할 수 있다.

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

rendered by는 해당 컴포넌트를 렌더링한 주체가 누구인지 확인할 수 있다. 프로덕션 모두에서는 react-dom의 버전만 확인할 수 있지만 갭라 모드에서는 해당 컴포넌트를 렌더링한 부모 컴포넌트까지 확인 가능하다.

정리

  • 컴포넌트 메뉴를 활용하면 컴포넌트 트리에 대한 정보뿐만 아니라 해당 컴포넌트에 대한 자세한 정보를 확인할 수 있다.
  • 작성한 리액트 코드가 어떤 컴포넌트 트리로 렌더링 돼 있는지, 또 이 결과가 어떻게 HTML로 반영 됐는지, 렌더링된 컴포넌트가 어떤 props와 훅 등으로 구성돼 있는지 자세히 알고 싶다면 이 컴포넌트 메뉴를 적극 활용하자.

6.3.2 프로파일러(프로덕션 x)

컴포넌트 메뉴가 정적인 현재 리액트 컴포넌트 트리의 내용을 디버깅하기 위한 도구라면 프로파일러는 리액트가 렌더링하는 과정에서 발생하는 상황을 확인하기 위한 도구다.
즉 리액트 애플리케이션이 렌더링되는 과정에서 어떤 컴포넌트가 렌더링됐는지, 또 몇 차례나 렌더링이 일어났으며 어떤 작업에서 오래 걸렸는지 등 컴포넌트 렌더링 과정에서 발생하는 일을 확인할 수 있다.

프로파일러 사용을 위해 준비한 예제코드(의도된 이슈 있음)

설정 변경하기

먼저 가운데 있는 톱니 모양의 설정 버튼을 누르자.

컴포넌트가 렌더링될 때마다 강조 표시를 하고 싶다면 리액트 개발자 도구의 설정에서 해당 옵션을 키바~

  • General 탭의 Highlight updates when components render: 컴포넌트가 렌더링될 때마다 해당 컴포넌트에 하이라이트를 표시한다. 이 기능은 매우 유용한 기능이므로 꼭 켜두자.

  • Debugging 탭의 Hide logs during second render in Strict Mode: 리액트 애플리케이션이 엄격 모드에서 실행되는 경우, 원활한 디버깅을 위해 useEffect등이 두 번씩 작동하는 의도적인 작동이 숨겨져 있다. 이로 인해 useEffect안에 넣은 console.log가 두 번씩 찍히기도 하는데, 이를 막고 싶다면 해당 버튼을 활성화 하면 된다. 프로덕션 모드에서는 해당 옵션과 관계없이 정상적으로 한 번씩 출력된다.

  • Profiler 탭의 Record why each component rendered while profiling: 프로파일링 도중 무엇 때문에 컴포넌트가 렌더링됐는지 기록한다. 애플리케이션 속도가 조금 느려질 수 있지만 디버깅에 도움이 되는 옵션이므로 켜두는것이 좋다.

프로파일링

프로파일링 메뉴

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

  • 첫번째 버튼은 'Start Profiling' (프로파일링 시작) 버튼으로, 이 버튼을 누르면 프로파일링이 시작된다.
    프로파일링이 시작되면 곧바로 적색 동그라미로 바뀌며, 프로파일링 중이라는 메시지가 나타난다. 그리고 다시 누르면 프로파일링 중단되고 프로파일링 결과가 나타난다.

  • 두 번째 버튼은 'Reload and Start profiling' (새로고침 후 프로파일링 시작)은 첫 번째 버튼과 유사하지만 해당 버튼을 누르면 웹페이지가 새로고침되면서 이와 동시에 프로파일링이 시작된다. 이 버튼으로 프로파일링을 시작해도 첫 번째 'Start Profiling' 버튼이 적색으로 바뀐다. 다시 누르면 프로파일링이 중단되고 프로파일링 결과가 나타난다.

  • 세 번쨰 버튼은 'Stop profiling' (프로파일링 종료) 버튼으로, 프로파일링된 현재 내용을 모두 지운다.

  • 네 번째, 다섯 번째 버튼은 각각 'Load Profile' (프로파일 불러오기), 'Save Profile' (프로파일 저장하기) 버튼으로, 프로파일링 결과를 저장하고 불러오는 버튼이다. 프로파일링 결과를 저장하면 사용자의 브라우저에 해당 프로파일링 정보가 담긴 JSON 파일이 다운로드 되며, 이 파일을 다시 로딩해 프로파일링 정보를 불러올 수 있다.
    이 JSON 파일은 단순히 리액트 개발 도구에서 저장하고 불러오는 용도로 사용되므로 개발자가 직접 필요한 정보를 얻기에 매우 복잡하다..!

Flamegraph

불꽃 모양의 아이콘 Flamegraph 탭에서는 렌더 커밋별로 어떠한 작업이 일어났는지 나타낸다.
너비가 넓을 수록 해당 컴포넌트를 렌더링하는 데 오래 걸렸다는 것을 의미한다.

첫번째 렌더 커밋

두번째 렌더 커밋

렌더링이 일어난 컴포넌트의 렌더링 정보, 해당 컴포넌트가 렌더링된 이유, 그리고 전체 렌더링에서 소요된 시간을 확인할 수 있다.

Flamegraph의 오른쪽에 있는 화살표를 누르거나 세로 막대 그래프를 클릭하면 각 렌더 커밋별로 리액트 트리에서 발생한 렌더링 정보를 확인할 수 있다.
여기서는 렌더링이 발생한 횟수도 확인할 수 있어 개발자가 의도한 횟수만큼 렌더링이 발생했는지도 알 수 있다.

Ranked

Ranked는 해당 커밋에서 렌더링하는 데 오랜 시간이 걸린 컴포넌트를 순서대로 나열한 그래프다.
렌더링이 발생한 컴포넌트만 보여준다. 렌더링이 발생한 컴포넌트에 대한 정보만 파악하고 싶다면 Ranked 메뉴를 활용하자.

타임라인

Timeline에서는 시간이 지남에 따라 컴포넌트에서 어떤 일이 일어났는지 확인할 수 있다.
Timeline은 리액트 18이상의 환경에서만 확인할 수 있다. input에 글자를 입력하면서 state의 값이 업데이트되고, 이 값이 동기로 업데이트 됐는지, 또 언제 업데이트가 이뤄졌는지 등을 확인할 수 있다.
Timeline은 시간의 흐름에 따라 리액트가 작동하는 내용을 추적하는 데 유용하다. 시간 단위로 프로파일링 기간 동안 무슨 일이 있었는지, 무엇이 렌더링됐고, 또 어느 시점에 렌더링됐는지, 리액트의 유휴 시간은 어느정도 였는지 자세히 확인할 수 있다.

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

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

이 두 번째 렌더링이 발생하는 원인응ㄹ 파악해 보자.
렌더링 정보에 대해 확인하려면 우측 상단 그래프에서 오른쪽 화살표를 누르거나 보고싶은 커밋을 클릭하면 된다.

우리가 알고 싶은 렌더링은 두 번째 이므로 두 번째 렌더링 커밋을 누른다.

"What caused this update?"의 App을눌러 해당 컴포넌트가 렌더링된 이유를 살펴 본다.


App이 왜 렌더링 됐는지 보면 App 내부에 훅이 변경 됐음을 알 수 있다.
'Hook 1 changed' 라는 내용이 보인다. 이 말은 첫 번째 훅으로 인해 렌더링이 실행된다는 것을 의미한다.

App 의 첫번째 훅을 확인하기 위해 컴포넌트 메뉴로 이동해서 App의 'hooks'를 보면 값이 1000인 1번 훅을 볼 수 있다.
이 정보를 토대로 프로파일링 기간에 useState에 "1000"을 넣는 코드를 찾아보자.

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

디버깅한 결과를 종합해 본다면 사용자가 아무런 작동을 하지 않아도 3초 경에 App의 state를 변경시키는 코드가 있다는 사실을 유추해 볼 수 있다.
이 렌더링을 발생시킨 범인은 useEffect 함수 내부에서 3초 뒤에 실행되는 setTimeout 이다. 이 useEffectf를 제거하고 다시 프로파일링 해 보면 렌더링이 한 번만 일어나는 것을 볼 수 있다.
확인 완료!

  1. input에 입력할 때마다 App 전체가 리렌더링된다.
    숫자 5개를 입력했는데 5번 커밋 전체를 확인해보면 App이 렌더링 되었다.

해당 input을 별도의 컴포넌트로 분리하자.

위 코드를 실행한 결과를 리액트 개발자 도구로 확인해보자. 렌더링이 필요한 컴포넌트만 렌더링 된 것을 알 수 있다. App은 렌더링이 발생하지 않아서 회색으로 처리됐고, 별개의 컴포넌트로 분리한 InputText만 글자를 입력할 때마다 리렌더링되는 것을 확인할 수 있다.

지금까지는 state변경을 최소 컴포넌트 단위로 분리하는게 좋다는 사실을 이론적으로만 숙지하고 이해했지만 프로파일 도구를 활용해 명확히 확인할 수 있게 됐다.

  1. InputText 컴포넌트 내부에 컴포넌트 하나를 추가해보자. 해당 컴포넌트는 문자열을 props로 받는다.

텍스트를 다시 입력해서 프로파일링해 보자.

CopyrightComponent는 고정된 props인 text="all rights reserved"를 갖고 있지만 계속해서 리렌더링이 감지 됐다.
props가 변경되지 않아도 부모 컴포넌트가 리렌더링되면 리렌더링이 같이 일어난다는 사실을 확인하는 것이다.
CopyrightComponent를 memo로 감싸고 다시 한번 확인해 보자.

InputText(부모컴포넌트)가 리렌더링되어도 CopyrightComponent(자식컴포넌트)가 리렌더링되지 않는다.

6.4 정리

애플리케이션을 개발하면서 시간이 날 때마다 틈틈이 리액트 개발 도구로 컴포넌트 트리와 프로파일링을 실행해 원하는 대로 렌더링되고 있는지, 메모이제이션을 활용한 최적화는 잘 되고 있는지 확인하자.
이론적으로 이해하고 있는 사실만 믿고, 실제로 의도대로 작동하고 있는지 확인하지 않고 개발을 이어가다 보면 실수가 나올 수 있다.

꾸준히 리액트 개발 도구를 가까이에 두고 디버깅을 손에 익히고 사전에 버그를 방지하고 꾸준히 성능을 개선하려고 노력하다면 훌륭한 리액트 애플리케이션을 개발하는 데 많은 도움을 얻을 수 있다.

profile
잼나게 코딩하면서 살고 싶어요 ^O^/

0개의 댓글