솔미는 성장중

[모던 리액트 딥다이브 스터디] 리액트 핵심 요소 살펴보기 본문

react

[모던 리액트 딥다이브 스터디] 리액트 핵심 요소 살펴보기

solming 2024. 1. 14. 15:54
728x90

1. JSX

  • 페이스북에서 만든 내장형 구문
  • 트랜스파일러를 필수적으로 거쳐야 자바스크립트 런타임이 이해할 수 있는 JS코드로 변환됨

  • 설계 목적
    : 다양한 트랜스파일러에서 다양한 속성을 가지 ㄴ트리 구조를 토큰화해 ECMAScript로 변환하는 데 초점
    = JSX  내부에 트리 구조로 표현하고 싶은 것을 작성해두고, 트랜스파일 과정을 거쳐 JS(ECMAScript)로 변경하는 것이 목표

 

JSX 컴포넌트

JSXElement

- 가장 기본 요소

- 역할 : HTML element와 유사

- JSXElement의 요소 이름으로 쓸 수 있는 것 (JSXElementName)
  - <$></$> <_></_> 도 가능 (JSXIdentifier)

  - : 를 이용해 서로 다른 식별자를 이어준 것
    ex) <foo:bar></foo:bar>  (JSXNamespacedName) , <foo.bar.baz> </foo.bar.baz>  (JSXMemberExpression)

 

JSXAttributes

- JSXElement에 부여가능한 속성

- 필수 X

 

JSXChildren

- JSXElement의 자식값

- 아래 코드도 가능

export default function App(){
  return <>{(()=>"foo")()}</>
}

 

JSXStrings

- HTML과 JSX 사이는 복붙을 쉽게 하기 위해 HTML에서 사용가능한 문자열은 모두 JSXStrings에서도 가능

- 이스케이프 문자 (\)도 ok

 

 

JSX ▶️ JS 변환

@babel/plugin-transform-react-jsx 플러그인이 이 역할을 해준다!

 

  • JSXElement를 첫 번째 인수로 선언해 요소를 정의
  • 옵셔널인 JSXChildren, JSXAttributes, JSXStrings는 이후 인수로 넘겨주어 처리

🔥 p127 코드 테스트해보기 (리팩토링 예제)

 

 


 

2. 가상 DOM & 리액트 파이버

DOM이란?

웹페이지에 대한 인터페이스로 브라우저가 웹페이지의 콘텐츠와 구조를 어떻게 보여줄지에 대한 정보를 담고 있는  것

 

가상 DOM 등장 배경

사용자의 인터랙션으로 웹페이지가 변경되고 경우에 따라 레이아웃&리페인팅 과정이 많이 발생되며 비용 증가.

렌더링 이후 추가 렌더링 작업은 SPA에서 더욱 많아진다. 하나의 페이지에서 계속 계산하는 작업을 수행해야 하기 때문에.

가상 DOM은 웹페이지가 표시해야 할 DOM을 일단 메모리에 저장하고, 리액트가 실제 변경에 대한 준비가 완료됐을 때 실제 브라우저의 DOM에 반영한다!

 

→ DOM계산을 메모리에서 거치고, 렌더링 과정을 최소화할 수 있다

 

브라우저 렌더링

1. 사용자 요청 주소를 방문해 HTML 파일 다운
2. 브라우저 렌더링 엔진이 HTML 파싱 -> DOM 트리 생성 (눈에 보이는 요소만!)
3. 중간에 CSS파일 만나면 CSS파일 다운
4. 브라우저 렌더링 엔진이 CSS 파싱 -> CSSOM 트리 생성
5. 레이아웃 : 어느 좌표에 나타날지 계산  (레이아웃 거치면 무조건 페인팅 거침)
6. 페인팅 : 실제 모습 그리는 과정 (ex. 색)

 

 

누가 가상 DOM을 만들고 렌더링 과정을 최적화 시켜주지? 

 React Fiber 

(= reconciliation 엔진)

가상 DOM과 실제 DOM을 비교해 변경 사항 수집

차이가 있으면 변경 관련 정보가진 fiber 기준으로 화면에 렌더링 요청

 

 

 

  • fiber는 컴포넌트가 최초로 마운트되는 시점에 생성되어 가급적 재사용된다. (리액트 요소는 렌더링 발생 때마다 새롭게 생성)
  • fiber는 state 변경 / 생명주기 메서드 실행 / DOM 변경 필요 시점 등에 실행된다.
  • 작업을 직접 바로 처리하기도 하고, 스케줄링 하기도 함. (유연한 처리)
  • fiber는 하나의 작업 단위로 구성.
  • 리액트 작업 처리 순서
    : 작업 하나 처리 (비동기) -> finishedWork()로 마무리 -> 커밋해서 브라우저 DOM에 반영해 가시적인 변경사항 만들기 (동기)
    (이래서 리액트를 통한 JS는 싱글스레드 임에도 불구하고 아닌것처럼 사용할 수 있는건가?)

 

리액트 파이버 트리 (React Fiber Tree)

 

2가지가 있다

1. 현재 모습을 담은 파이버 트리
2. 작업중인 상태를 나타내는 workInProgress 트리

 

 

리액트 파이버의 직업이 끝나면 리액트는 포인터만 변경해 workInProgress 트리를 현재 트리로 바꿔줌!! (더블 버퍼링)

이것은 커밋 단계에서 발생한다.

더블 버퍼링이란?
하나만 이용하면 한 번에 모든 작업을 마무리해 그리기 어려워서 다 그리지 못한 모습을 보는 경우 발생. 그래서 완성되면 현재 상태를 새로운 그림으로 바꾸는 기법

 

즉, current 기준으로 작업 시작

-> 업데이트 발생하면 workInProgress 트리 빌드 시작

-> workInProgress 트리 빌드 끝나면 다음 렌더링 때 이 트리 사용

-> workInProgress 트리가 최종적으로 렌더링에 반영되면 이게 current로 바뀜

 

만약 여기서 setState 등으로 업데이트가 발생하면?

workInProgress 트리 다시 빌드 시작 (하지만 이미 존재하는 파이버는 새로 생성하지 않고 기존꺼를 파이버 내부에서 처리&업데이트)

 

새로운 트리를 만드는 작업은 동기식이고 중단될 수 없지만, 리액트는 작업을 파이버 단위로 나눠서 수행하기 때문에 우선순위가 높은 업데이트가 오면 현재 작업을 중단/새롭게 생성/폐기 할 수 있다.

 

 


 

3. 리액트의 렌더링

브라우저가 렌더링할 때 필요한 DOM 트리를 만드는 과정
(props, state, JSX 값을 기반으로 함)

 

 

리액트 렌더링은 언제 일어날까?

1. 최초 렌더링

 

2. 리렌더링

- 함수형 컴포넌트의 useState()의 setter가 실행될 때

- 함수형 컴포넌트의 useReducer()의 dispatch가 실행될 때

- 컴포넌트의 key props가 변경될 때

- props가 변경될 때

- 부모 컴포넌트가 렌더링 될 때 자식 컴포넌트도 무조건 리렌더링

- 클래스형 컴포넌트의 setState가 실행될 때

- 클래스형 컴포넌트의 forceUpdate가 실행될 때

 

key의 역할

리렌더링이 발생할 때 형제 요소들 사이에서 동일한 요소를 식별하는 값.
리렌더링이 필요한 컴포넌트를 최소화하기 위해 꼭 필요하다.

 

- 렌더링 프로세스가 시작되면
  리액트는 컴포넌트 루트에서부터 차근차근 아래쪽으로 내려가며 업데이트가 필요하다고 ㅈ정된 모든 컴포넌트를 찾는다

- 클래스형 컴포넌트인 경우 : 클래스 내부 render() 실행

  함수형 컴포넌트의 경우 : FunctionComponent() 자체를 호출하고 결과물 저장

 

 

렌더 & 커밋

 

렌더

- 컴포넌트를 렌더링하고 변경 사항을 계산하는 모든 작업

- 컴포넌트를 실행해 (render(), retrun) 그 결과와 이전 가상 DOM을 비교하는 과정을 거쳐 변경이 필요한 컴포넌트를 체크

- 비교하는건 type, props, key

 

커밋

- 렌더 단계의 변경사항을 실제 DOM에 적용해 사용자에게 보여주는 과정

 

리액트의 렌더링이 일어난다해서 무조건 DOM업데이트가 일어나는 것은 아니다!

렌더링 수행 but 변경사항이 감지되지 않다면 커밋 필요없으니까 생략될 수 있음.

= 렌더링은 가시적 변경이 없어도 일어날 수 있다

 

렌더링은 동기식으로 작동한다!

비동기 방식이면 사용자는 하나의 상태에 대해 여러 가지 다른 UI를 보게 되기 때문에 안된다.

(ex. A상태 변경시 B,C->B1,C1이 되어야하는데, A변경됐을 때 하나가 늦게 반영되어 B, C1상태인 상황 방지)

- 리액트 18에선 비동기 렌더링(동시성 렌더링)이 도입됨 
  : B 렌더링 작업이 무거워 상대적으로 빠른 C라도 변경해서 보여주고 싶을 때 사용

- 특정 렌더링의 우선순위를 낮추거나, 중단/재시작, 포기 할 때 사용

 


흐름 총 정리

 0. 현재 우리에게 보여지는 화면은 Current React Fiber Tree를 기반으로 만들어져있다.

1. 상태가 변경되거나 이벤트가 발생하면 새로운 가상 DOM트리가 생성되는데 diffing 알고리즘을 통해 두 개의 가상 DOM트리가 비교됩니다.

2. 컴포넌트를 렌더링하고 변경 사항을 계산하는 모든 작업 = 리액트 Render 작업

 

   Diffing 알고리즘이란?

더보기

diffing 알고리즘은 2가지 기준을 가지고 있습니다.

  • 서로 다른 type을 가지는 React Element는, 서로 다른 트리를 생성 할 것이다.
  • 개발자는 'key'프로퍼티를 통해 리액트에게 해당 요소가 바뀌었는지 안바뀌었는지 힌트를 줄 수 있다.

- 루트 엘리먼트가 달라지면 그 트리를 완전히 버리고 새롭게 구축합니다.
- 속성이 바뀌는 경우엔 동일한 컴포넌트 인스턴스에 대해 속성만 업데이트 시켜주고, unmount는 일어나지 않으며 상태는 유지됩니다.
- 자식 컴포넌트가 바꾸는 경우엔 key를 비교해서 동일한 key 프로퍼티에 대해 리액트가 해당 프로퍼티가 이전 가상 DOM과 동일하다면, unmount 시키지 않습니다.


이 방법들을 통해 전체를 리렌더링하는 것이 아니라 변경된 부분에 대해서만 렌더링을 시켜줘서 react가 효율적으로 작동할 수 있게 해줍니다.


어떤 부분이 업데이트가 필요한지 판단이 끝나면
, 두 가상 DOM 트리 간의 차이를 파악하고, 최소한의 업데이트 작업을 수행합니다. , 가상돔의 장점인 효율적인 업데이트의 핵심이 diffing 알고리즘입니다.

 

3. reconciliation 엔진인 react fiber를 통해 변경사항을 반영해 Work In Progress React Fiber Tree를 만든다.

 

3-1.  react fiber의 구체적인 작업 설명 + 단일 스레드인 JS에서 비동기 작업을 가능케!

 

4. Work In Progress React Fiber Tree가 완성되면 포인터에 의해 Current로 바뀐다.

 

5. 변경사항을 확정하여 실제 DOM에 반영하는 Commit 작업 (이때 리플로우, 리페인트.. 등 일어남)

 

6.  Reconciliation
= Render + Commit 과정 모두를 합친 것

= 가상돔과 실제돔을 동기화하는 과정

 

728x90