- 렌더링이란? -
리액트가 컴포넌트에게 UI 영역이 어떻게 보이기를 원하는지 props 와 state 를 바탕으로 요청하는 프로세스
- 렌더링 프로세스 개요 -
리액트는 컴포넌트 트리의 루트 컴포넌트부터 시작해서 아래로 순회하면서 업데이트가 필요한 모든 컴포넌트를 찾는다
컴포넌트에 플래그를 지정해 두고 렌더 함수를 호출하여 렌더 출력을 저장한다.
컴포넌트 트리 전체에서 렌더 출력을 수집한 후에 리액트는 새로운 객체 트리(가상 DOM)와 실제 DOM 을 비교하며 UI가 원하는 대로 보이도록 변경 사항을 수집한다.
참고1) 렌더 출력은 보통 JSX 문법으로 작성된다.
자바스크립트가 컴파일 될 때는 React.createElement() 함수로 변환된다.
JSX를 이용해서 사람이 보기 편하도록 HTML 태그와 비슷하게 작성하지만 실제로 작동하는 때에는 JavaScript 문법대로 작동한다.
참고2) 리액트팀은 "가상 DOM" 이라는 말을 조심해서 사용했다.
리액트가 렌더를 DOM 노드를 바탕으로 생성한다고 생각하게 되기 때문이다.
그러나 리액트는 "값 UI"다.
이것의 핵심 원칙은 UI 가 문자열이나 배열처럼 "값"이라는 점이다.
자바스크립트처럼 값을 변수에 저장하고, 전달인자르 주고 받고, 흐름 제어에서 사용할 수 있다.
요점은 DOM 변경을 최소화하려는 데에 있지 않다.
실제로 리액트 요소가 항상 DOM 요소를 나타내는 것도 아니다.
<Message recipientId={10} />
=> 개념적으로 이는 지연 함수 호출이다
Message.bind(null, {recipientId: 10});
- 렌더와 커밋 -
리액트팀은 렌더링을 두 단계로 나눈다.
"렌더 단계"와 "커밋 단계"이다.
렌더 단계는 컴포넌트로부터 렌더 결과를 수집하고 변경 사항을 계산한다.
커밋 단계에서는 계산된 변경 사항을 DOM에 적용한다.
리액트는 커밋 단계에서 DOM을 업데이트 한 후에 DOM 요소와 컴포넌트 인스턴스를 가리키는 모든 참조를 알맞게 업데이트한다.
그다음 componentDidMount 나 componentDidUpdate 같은 라이프사이클 메소드들을 동기적으로 실행한다.
리액트는 브라우저가 중간에 이벤트를 처리할 수 잇도록 작업을 일시 중지하기도 한다.
나중에 가서 작업 재개하거나 작업을 페기하거나 처음부터 다시 계산하기도 한다.
여기서 핵심은 "렌더링'과 "DOM 업데이트"는 항상 같지는 않다는 점이다.
컴포넌트들을 렌더링 했음에도 결과적으로 어떤 UI 변경도 일어나지 않을 수 있다.
또는 렌더링을 여러 번 실행했을 수도 있지만 다른 업데이트로 인해서 수행 중이던 작업이 무효화되기도 하고 렌더 출력을 버리기도 한다.
- 리액트에서 렌더링 동작 방식 -
/ 렌더링을 유발하는 몇 가지 방법
> 함수 컴포넌트
- useSate : setter
- useReducer : dispatch
> 클래스 컴포넌트
- this.setState()
- this.forceUpdate()
/ 공통적인 렌더링 동작
리액트의 기본 동작은 상위 컴포넌트가 렌더링될 때 그 내부의 모든 하위 컴포넌트를 순회하며 렌더링한다는 것이다.
예를 들어 컴포넌트 트리가 아래처럼 있다.
A > B > C > D > E
여기에서 C 컴포넌트에서 카운터를 증가시키는 버튼을 클릭했다.
> C 에서 setState() 를 호출해 C를 렌더링 큐에 넣는다.
> 리액트는 컴포넌트 트리 최상단에서 렌더 패스를 시작한다.
> A는 업데이트가 필요하다고 표시되어 있지 않음을 확인한 후에 그냥 지나친다.
> B는 업데이트가 필요하다고 표시되어 있지 않음을 확인한 후에 그냥 지나친다.
> C는 업데이트가 필요하다고 표시되어 있음을 확인하고 렌더링을 한다. C 는 <D /> 를 반환한다.
> D는 업데이트가 필요하다고 표시되어 있지 않지만 상위 컴포넌트인 C가 렌더링되었기 때문에 리액트는 D도 다시 렌더링한다. D는 <E /> 를 반환한다.
> E도 D와 마찬가지로 업데이트가 필요하다고 표시되어 있지 않지만 상위 컴포넌트인 C가 렌더링되었기 때문에 리액트는 E도 다시 렌더링한다.
즉, 어떤 컴포넌트를 렌더링하면 기본적으로 그 모든 하위 컴포넌트를 렌더링한다.
리액트는 'props 가 변경되었는지' 여부는 신경 쓰지 않는다.
상위 컴포넌트가 렌더링되면 무조건 하위 컴포넌트를 렌더링한다.
컴포넌트 트리에서 대부분의 컴포넌트는 이전과 동일한 렌더 출력을 반환할 가능성이 높다.
그렇다고 해서 렌더링이 '나쁜 것'은 아닏.
렌더링은 리액트가 '실제로 DOM 을 변경해야 하는지 여부를 아는 방법'일 뿐이다.
- 렌더링 유의사항 -
리액트에서 렌더링은 순수해야 하며 어떠한 사이드 이펙트도 없어야 한다.
이는 조금 혼란스러울 수 있는데, 렌더링은 결과적으로 어떤 것도 망가뜨리지 않기 때문이다.
예를 들어 console.log() 도 엄밀히 말하면 사이드이펙트이지만 실제로 아무 영향도 없다.
렌더링 도중 AJAX 를 호출하는 것도 분명 사이드 이펙트이며 에기치 못한 앱 동작이 발생할 수도 있다.
렌더링에서는 다음 사항에 유의해야 한다.
> 기존에 있던 변수나 객체를 변경해서는 안 된다.
> Math.random() 이나 Date.now() 같은 임의의 값을 생성하면 안 된다.
> 네트워크 요청을 해서는 안 된다.
> state 업데이트를 해서는 안 된다.
> 렌더링 도중에 새로 생성된 객체나 값은 변경해도 된다.
> 오류를 발생시켜도 된다.
> 캐시된 값과 아직 생성되지 않은 데이터에 대해 "지연 초기화"를 할 수 있다.
- "React 렌더링 동작에 대한 (거의) 완벽한 가이드" 글을 읽고 정리한 글 -
- 230113
'React' 카테고리의 다른 글
row 번호 자동으로 뽑아주는 함수 만들기 (0) | 2023.04.04 |
---|---|
datagrid 에 rowData 만들어서 넣어주기 - 고정 출력 행 있을 때 (0) | 2023.03.28 |
[React] store 에 있는 객체를 view 에 바인딩하여 출력할 때 주의할 점 - setProps (0) | 2023.01.30 |
[React.js] 클래스형 setState 안 될 때 - 덮어쓰기 되는 경우 (0) | 2023.01.23 |
[React] onChange 값이 한 박자씩 늦게 들어갈 때 / observable 변수 렌더링 뒤늦게 반영될 때 (0) | 2023.01.02 |