[React]심화

🔆 Virtual DOM

Virtual DOM

Virtual DOM이 나오게 된 배경🤔
실제 DOM은 스크립팅 언어가 접근 및 탐색하는 속도가 빨라 탐색 후 변경 및 업데이트하는 속도는 역시 빠르다. 하지만 JavaScript로 조작하는 DOM의 요소가 많을수록 모든 DOM 업데이트에 대하여 리플로우를 해야 하므로 DOM의 업데이트에 대한 비용 증가한다. 이를 보완하고자 Virtual DOM이 등장하였다.

  • Real DOM(DOM): JavaScript와 같은 스크립팅 언어들이 태그들에 접근 및 조작할 수 있도록 태그를 트리 구조로 만든 객체 모델
  • DOM의 조작 속도가 느려지는 이유
    • DOM이 변경 시마다 브라우저의 렌더링 엔진이 리플로우
      → JavaScript로 DOM을 직접 조작하는 요소가 많을수록 브라우저는 리플로우 및 리페인트 과정을 반복하므로 속도 저하
      → 극단적으로 프레임 드랍(frame drop)과 같은 문제 발생

Virtual DOM🌳
Virtual DOM이란 실제 DOM의 가벼운 사본 개념으로 React는 Virtual DOM에 접근하여 변화 전후를 비교한 뒤 바뀐 부분을 적용시킨다. Virtual DOM 객체가 실제 DOM 객체와 같이 실제로 브라우저 화면에 그려지는 것은 아니기 때문에 속도 측면에서 유리하다.

React Diffing Algorithm

트리 조작 방식 알고리즘의 가정(휴리스틱 알고리즘 기반)

  1. 각기 서로 다른 두 요소는 다른 트리를 구축할 것이다
  2. 개발자가 제공하는 key 프로퍼티를 가지고, 여러 번 렌더링을 거쳐도 변경되지 말아야 하는 자식 요소가 무엇인지 알아낼 수 있을 것이다

🏷 React의 비교 알고리즘(React Diffing Algorithm)

  • React의 DOM 트리 탐색 방법
    : 트리의 레벨 순서대로 순회하는 방식으로 탐색
    너비 우선 탐색(BFS)의 일종
  • 비교 전후 다른 타입의 DOM 앨리먼트인 경우
    • 부모 태그가 달라지는 경우 이전 트리를 버리고 새로운 트리 구축
      이전의 DOM 노드 파괴
      → 새로운 컴포넌트가 실행되며 기존 컴포넌트는 완전히 해제(Unmount)
      → 기존 컴포넌트의 state 역시 파괴
  • 비교 전후 같은 타입의 DOM 앨리먼트인 경우
    • 최소한의 변경 사항만 업데이트
    • 업데이트 사항 발생 시 Virtual DOM 내부의 프로퍼티만 수정
      → 모든 노드에 걸친 업데이트 종료 시 단 한번 실제 DOM 렌더링 시행
  • 자식 앨리먼트의 재귀적 처리
    • React는 변경 사항 발생 시 하나의 DOM 노드 처리 후 밑의 자식들을 순차적으로 비교하며 순회하여 차이 발견 시 변경
    • 만약 기존 앨리먼트가 유지되는 상태에서 새로운 앨리먼트를 리스트의 맨처음 삽입하는 경우, React는 순차적으로 비교하므로 기존 노드를 전부 버린 뒤 새롭게 렌더링하는 비효율적인 방식으로 동작
      → 이를 위해 key라는 속성 지원
    • key: key 속성을 통해 기존 트리 노드와 새로운 트리 노드들의 일치 여부 비교 가능
      • key 속성에는 유일한 값을 부여
        (단, 형제 앨리먼트 사이에서만 유일하면 되며 전역적으로 유일할 필요는 없음)

🔆 React Hooks

: 함수형 컴포넌트에서도 더 직관적인 함수를 이용하여 작업할 수 있게 만든 기능으로 뛰어난 재사용성과 직관성을 갖는 함수

Component와 Hook

함수형 컴포넌트의 사용이 증가한 이유🧐
클래스형 컴포넌트는 복잡해질수록 이해가 어렵고 컴포넌트 사이의 상태 로직 재사용이 어렵다는 단점과 JavaScript 문법을 정확하게 인지하지 못하는 경우 this키워드의 동작 방식을 이해하기 어렵다는 단점이 있다. 이에 반해 함수형 컴포넌트는 클래스형에 비해 직관적이고 보기 쉽다는 특징과 함께 Hook의 등장으로 state를 추가할 수 있다는 큰 장점까지 얻게 되어 함수형 컴포넌트의 사용이 증가하였다.

  • Hook
    : 함수 컴포넌트가 상태 조작 및 최적화 기능 등 다른 여러 기능을 사용하기 편리하게 해주는 메소드(클래스형 컴포넌트에서는 동작x)
    • Hook 사용 규칙
      1. 리액트 함수의 최상위에서만 호출해야 함
      2. 오직 리액트 함수 내에서만 사용되어야 함

  • useCallback, useMemo의 필요성😎
    • 컴포넌트는 기본적으로 상태가 변경되거나 부모 컴포넌트가 렌더링 될 때마다 리렌더링하는 구조를 가진다. 잦은 리렌더링이 일어날 경우 앱에는 좋지 않은 성능을 끼치게 되는데 이를 방지하기 위한 Hook, 즉 렌더링 최적화를 위한 Hook이 useCallback과 useMemo이다.

💡useMemo💡

: 특정 값을 재사용하고자 할 때 사용하는 Hook으로 복잡한 연산의 중복을 피하고 React 앱의 성능을 최적화시키는 메서드

  • 메모이제이션(Memoization) 개념과 밀접한 관계
  • Memoiation
    : 기존에 수행한 연산의 결과값을 메모리에 저장해두고 동일한 입력이 들어오면 재활용하는 프로그래밍 기법

💡useCallback💡

: 함수의 재사용을 위해 사용하는 Hook으로 그 함수가 의존하는 값들이 바뀌지 않는 한 기존 함수를 계속해서 반환하는 메서드

  • 메모이제이션(Memoization) 개념과 밀접한 관계
  • 자식 컴포넌트의 props로 함수를 전달 시 용이
  • 단순히 컴포넌트 내에서 함수를 반복 생성하지 않기 위해 사용하는 것은 바람직하지 않음

useCallback과 참조 동등성❓
: useCallback은 참조 동등성에 의존한다

  • React는 JavaScript 언어로 만들어진 라이브러리
    → 기본적으로 JavaScript의 문법을 따름
    → JavaScript에서 함수는 객체
    → 메모리에 저장 시 객체는 값을 저장하는 것이 아니라 주소를 저장
  • 기존 함수는 리렌더링 시 함수를 새롭게 만들어 호출
    → 새로운 함수와 기존 함수는 참조 값이 다름
  • useCallback은 함수의 메모리 주소 값을 저장했다 다시 사용하는 것과 같은 개념
    • 불필요한 리렌더링 방지 가능

💡Custom Hooks💡

: 개발자가 스스로 커스텀한 Hook으로 반복되는 로직을 함수로 뽑아내 재사용이 가능하게 하는 메서드

  • 장점
    1. 상태관리 로직의 재활용 가능
    2. 클래스 컴포넌트보다 적은 양의 코드로 동일한 로직 구현 가능
    3. 함수형으로 작성하므로 명료함
  • Custom Hook 정의 시 필요한 규칙
    • 함수 이름 앞에 use를 붙인다
    • 대게 프로젝트 내의 hooks 디렉토리에 Custom Hook을 위치시킨다
    • Hook 함수는 조건부 함수가 아니어야 한다
      • return 값이 조건부이면 안됨
  • 특징
    • 서로 다른 컴포넌트에서 같은 Custom Hook을 사용한다고 해서 같은 state를 공유하는 것은 아님
      • 로직만 공유할 뿐, state는 컴포넌트 내에서 독립적으로 정의됨

🔆 React의 주목해야 할 기능

코드 분할(Code Spliting)

: 런타임 시 여러 번들을 동적으로 만들고 불러오는 것

  • 코드 분할 시 코드를 분리시켜 필요할 때 불러와 사용 가능
  • 페이지 로딩 속도 개선

React에서의 코드 분할🤖
: dynamic import(동적 불러오기) 사용하기

  • 구문 분석 및 컴파일 해야하는 스크립트의 양을 최소화
    • Static import(정적 불러오기)
      : 코드 파일 가장 최상위에서 import 지시자를 이용해 사용하고자 하는 라이브러리 및 파일을 불러오는 방법
    • Dynamic import(동적 불러오기)
      : 코드 중간에 필요한 파일 및 라이브러리를 불러오는 방법
      • then 함수를 사용해 필요한 코드만 가져옴
      • 가져온 코드에 대한 모든 호출은 해당 함수 내부에 있어야 함
      • React.lazy와 함께 사용

React.lazy()와 Suspense

React.lazy()
: dynamic import를 사용해 컴포넌트를 렌더링할 수 있도록 하는 메서드

  • 초기 렌더링 지연시간 감소 가능
  • React.lazy로 감싼 컴포넌트는 React.suspense 컴포넌트의 하위에서 렌더링해야 함

Suspense
: 아직 렌더링이 준비되지 않은 컴포넌트가 있을 때 로딩 화면을 보여주고, 로딩이 완료되면 렌더링이 준비된 컴포넌트를 보여주는 기능

  • fallback prop: 컴포넌트가 로드될 때까지 기다리는 동안 로딩화면으로 보여줄 React 앨리먼트를 받아들이는 속성
  • Suspense 컴포넌트 하나로 여러 개의 lazy 컴포넌트를 보여줄 수 있음

🔆 이 외에 새롭게 알게 된 것

🗺 location

window.loaction 프로퍼티에 접근하면 현재 document의 위치에 대한 정보를 담고 있는 Location 객체를 얻어올 수 있다. window.locationlocation으로도 많이 쓰인다.

  • 관련 메서드
    • location.reload()
      : 현재 URL의 리소스를 다시 불러오는 메서드로 매개변수에 true를 설정 시 브라우저 캐시를 무시하고 서버에서 새로 불러올 수 있음(기본값: false)
    • location.assign()
      : 주어진 URL의 리소스를 불러오는 메서드
    • location.replace()
      : 현재 리소스를 주어진 URL에 있는 리소스로 대체하는 메서드
      • assign()과의 차이점
        assign()를 사용하여 현재 페이지에서 주어진 URL로 이동 시 session history에 이전 기록이 남기 때문에 뒤로 가기가 가능하나 replace() 메서드 사용하여 현재 페이지에서 주어진 URL로 대체될 경우 session history에 이전 기록이 남지 않기 때문에 뒤로 가기 기능이 불가능하다.

<오늘의 일기>
useState와 useEffect 이외에 다른 여러 hook들을 경험해보고 custom hook을 만들어보았던 이번 유닛은 비교적 재미있게 진행할 수 있었다.
최근 스스로가 정체되어 있다는 느낌이 들어 고민이 많아지고 조급한 마음만 늘었는데 그 시기가 지나가면서 능동적으로 공부할 수 있는 힘이 다시금 생겨나고 있는 것 같다. 누군가 나를 잡기 위해 쫓아오고 있는 것도 아니니 조급하게 생각하지 말고 묵묵히 갈 길을 가보자!

0개의 댓글