02장 리액트 핵심 요소 깊게 살펴보기

React Deep Dive

  • add table of contents

2.1 JSX란?


  • JS 내부에 표현하기 까다로운 XML Style Tree구문 작성 돕는 문법
  • React 고유 전유물도 아님 (리액트(메타)가 소개하긴 함)
  • 자바스크립트 표준(ECMAScript)구문이 아님

    • SO (babel) 트랜스파일러를 거쳐야 자바스크립트 런타임에서 실행되는 코드로 변환됨
    • @babel/plugin-transform-react-jsx 플러그인을 통해 JSX 구문을 자바스크립트가 이해할 수 있는 형태로 변환한다.
  • HTML 과 구분 위해 대문자로 시작
  1. JSXElement: JSX 가장 기본 구성 요소, (similar to HTML) 아래 중 하나여야 함

    • JSXOpeningElement, JSXClosingElement : (<JSXElement JSXAttributes></JSXElement>)
    • JSXAttributes, JSXChildren 은 선택사항
    • JSXSelfClosingElement : 요소 스스로 종료 (<JSXElement JSXAttributes />), no children
    • JSXFragment : </> 단독 사용 (self closing) 불가능 (<>{JSXChildren}</>)


    • JSXElementName: 요소 이름으로 사용 가능
      • JSXIdentifier: JSX내부에서 사용할 수 있는 식별자
      • JSXNamespacedName: = JSXldentifier:JSXldentifier의 조합: (식별자를 이어주는 것, 한번만 잇기 가능)
      • JSXMemberExpression: JSXldentifier.JSXldentifier의 조합 (여러개 잇기 가능, but JSXNamespacedName와는 못 잇는다)
  2. JSXAttributes: JSXElement에 부여할 수 있는 속성 (없어도 에러 발생 X => 필수X)

  • JSXSpreadAttributes - JS의 전개 연산자와 동일 역할

    <JSXElement {...AssignmentExpression} />
    
    • AssignmentExpression: 키에 흘당할 수 있는 값 (객체, 조건문 표현식, 화살표 함수, 할당식 등 모든 표현식 가능)
    • JSXAttributeName: JSXAttributeValue : 속성을 나타내는 키-값 쌍으로 표현함
  1. JSXChildren: JSXElement의 자식 값

    • JSX는 속성을 가진 트리 구조를 나타내기 위해 만들어졌기 때문에 부모-자식 관계를 표현할 수 있음
  2. JSXStrings: 자바스크립트 코드 안에서 HTML 요소를 표현하는 방식

2.2 가상 DOM과 React Fiber


2.2.1 DOM 과 브라우저 렌더링 과정

  • DOM + CSSOM \(\rightarrow\) Render Tree
  1. 브라우저가 사용자가 요청한 주소를 방문해 HTML파일을 다운로드 한다

    • 브라우저의 렌더링 엔진: HTML 파싱 \(\longrightarrow\) create DOM TREE
  2. CSS 파일도 보이면 해당 CSS 파일도 다운
    • 브라우저의 렌더링 엔진: CSS 파싱 \(\longrightarrow\) create CSSOM TREE
  3. (2)의 DOM 노드 순회 중 사용자가 눈에 보이는 노드만 방문 (ex: display: none은 방문 X)
  4. 해당 노드에 대한 CSSOM 정보 찾고 노드에 적용.

    • a) Layout (reflow): 각 노드가 브라우저 화면의 어느 좌표에 나타나야 하는지 계산하는 과정
    • b) Painting : 레이아웃 단계를 거친 노드에 색과 같은 유효한 모습을 그리는 과정

Render Tree Construction

  1. DOM and CSSOM trees combine to form render tree
  2. Render tree contains only the nodes required to render the page
  3. Layout computes the exact position and size of each object
  4. Painting phase takes final render tree nad renders the pixels to screen

2.2.2 Virtual DOM

모든 렌더링 과정을 브라우저에서 하는 게 아닌 메모리에서 계산하고 최종 결과를 브라우저 DOM에 보여준다면, 렌더링 과정을 최소화하여 비용을 줄일 수 있고, 브라우저와 개발자의 부담을 덜 수 있다.

  • 브라우저가 웹페이지를 렌더링하는 과정은 매우 복잡하고 많은 비용이 든다

    • interaction 추가 -> changes DOM AFTER 렌더링
    • 특히 SPA에서는 하나의 페이지에서 계속해서 요소의 위치를 재계산 -> DOM 변경 비용 UP
  • 변경사항 추적 대신 최종 결과물 하나만 확인 필요

  • Virtual DOM: 웹페이지가 표시해야 할 DOM을 메모리에 저장

    • 실제 변경에 대한 준비가 완료되었을 때 실제 브라우저 DOM에 반영
    • DOM 계산을 브라우저가 아닌 메모리에서 계산하여, 실제로는 여러 번 발생했을 렌더링 과정을 최소화
  • ⚠️ 가상 DOM이 일반 DOM을 관리하는 브라우저보다 빠른 것은 아님!

    • 대부분의 상황에서 웬만한 애플리케이션을 만들 수 있을 정도로 충분히 빠르다는 것

2.2.3 React Fiber: 가상 DOM을 위한 아키텍쳐

🪡 React Fiber: 가상 DOM과 렌더링 과정 최적화 가능케 함

  • 평범한 JS Object임
  • Managed by Fiber Reconciler

    • 가상 DOM과 실제 DOM 비교하여 변경사항 수집
    • 차이 있을 시 -> 변경작업을 보유한 fiber기준으로 화면에 렌더링 요청
  • fiber: 하나의 작업 단위로 구성
  • 리액트는 위 Fiber들을 하나씩 처리 후 (finishedWork())로 마무리
    • finishedWork() Commit 후: 실제 browser DOM에 가시적인 변경사항으로 변경
  • fiber vs react element
    • 리액트 요소: 렌더링 발생 시 새롭게 생성
    • fiber: 컴포넌트 최초 마운트 시점에 생성, 이후는 최대한 재사용됨

React Fiber Tree

  • React 내부에 두 개 존재

    1. 현재 모습을 담은 파이버 트리
    2. 작업 중인 상태를 나타내는 workInProgress 트리
  • double buffering 기술 통해 변경사항을 렌더링에 반영한다

    double buffering: fiber 작업 완료 후 단순히 포인터만 변경하여 workInProress 트리를 현재로 변경하는 기술

  • Fiber 작업 순선

    1. beginWork() : 더 이상 자식이 없는 파이버를 만날 때 까지 (tree-like)
    2. completeWork()로 파이버 작업 완료
    3. 형제가 있다면 형제로 넘어가기
    4. 2, 3번이 return으로 돌아가 본인 작업 완료 표명

2.2.4 Fiber과 가상 DOM

  • 파이버: react component 에 대한 정보를 1:1 보유 (fiber works asyncrhonously within react architecture)
  • but in real browser arch, DOM must be applied synchronously
  • => work in memroy FIRST, and THEN apply in real browser


🎯 Virtual DOM, React’s GOAL: 브라우저의 DOM을 빠르게 draw + 반영이 아니라 값으로 UI 표현하는 것
흐름을 효율적으로 관리하기 위한 메커니즘

2.3 클래스 컴포넌트와 함수형 컴포넌트


2.3.1 클래스 컴포넌트

생명주기 메서드

  • mount \(\rightarrow\) update \(\rightarrow\) unmount

  • render: in mount and update phase에 호출 가능

    • PURE ONLY
    • render에 setState 사용 \(\rightarrow\) infinite loop
  • componentDidMount: mount phase에 호출

    • 함수 내에서 setState 사용 가능
  • componentDidUpdate: update 직후에 바로 실행됨

    • setState 사용 가능, but 조건문으로 감싸지 않으면 infinite loop
  • componentWillUnmount: unmount 또는 더 이상 사용되지 않기 직전에 호출

    • componentDidMount에서 등록한 이벤트 해제, 타이머 해제 등
    • 메모리 누수 등 불필요한 작동을 막기 위한 클린업 함수 호출 여기다 쓰면 됨
    • setState 사용 불가
  • shouldComponentUpdate: update 직전에 호출

    • 컴포 리렌더링 방지 조작 위해 사용하지만 이는 조작이니
    • 성능 최적화 상황에서만 사용 권장
  • static getDerivedStateFromProps: render() 직전에 호출

    • props로부터 state를 동기적으로 업데이트
    • (오고 있는 props 바탕으로 현재의 state를 변경하고 싶을 때 사용)
    • static이라서 this 사용 불가
    • 반환 객체 = state 로 업데이트
  • getSnapshotBeforeUpdate: DOM 업데이트 직전에 호출

    • DOM 업데이트 직전에 호출되어야 하는 작업을 수행 (ex: 스크롤 위치 유지, 윈도우 크기 조절 등)
    • componentDidUpdate 대체 가능
    • 반환값 = componentDidUpdate의 3번째 인자로 전달

Full-width imageMedium: React Version 16.4 (버전별로 차이 있음)

에러 발생 시

  • getDerivedStateFromError(): 자식 컴포넌트에서 에러 발생 시 호출

    • 에러 발생 시 호출되어야 하는 작업을 수행
    • componentDidCatch() 대체 가능
    • react hook으로 구현 안되어 있어서 클래스 컴포넌트에서만 사용 가능 (2023년 9월 기준)
    • state값 반환, side effect X
  • componentDidCatch()

    • getDerivedStateFromError() 에서 에러 잡고 state 결정 한 이후 실행
    • 부수 효과 가능

클래스 컴포넌트의 한계

데이터 흐름 어려움, 내부 로직 재사용 어려움, 코드 복잡성 및 사이즈 증가..etc

2.3.2 함수형 컴포넌트

  • render 내부에서 필요 함수 선언 시 this바인딩 필요 X
    • return 문에서 바로 사용 가능 (props, state 접근 가능)
  • state는 객체가 아닌 각각의 원시값으로 관리 (사용 용이)

Comparison

 클래스 컴포넌트함수형 컴포넌트
lifecyclerender 메소드 있는 React.Component 상속받아 구현props만 받아 React 요소만 반환
useEffectcomponentDidMount, componentDidUpdate, componentWillUnmountuseEffect
렌더링 값시간의 흐름에 따라 변화하는 this기준으로 렌더링props, state기준으로 렌더링

2.4 렌더링은 어떻게 일어나는가?


2.4.1 렌더링

  • React Application Tree 내 모든 컴포넌트들이 가지고 있는 props와 state값을 기반으로 아래 내용을 계산
      1. 어떻게 UI를 구성할 것인가
      1. 어떤 DOM 결과를 브라우저에 제공할 것인가

2.4.2 렌더링 발생 시나리오

  1. 최초 렌더링
  2. 리렌더링

    • 클래스 컴포넌트: setState, forceUpdate
    • 함수 컴포넌트 : useState의 setter, useReducer의 dispatch
    • 컴포넌트의 key prop 변경, props 변경
    • 부모 컴포넌트 리렌더링 (자식도 무조건 리렌더링)

2.4.3 렌더링 프로세스