React rendering으로 알아보는 side Effect

jimmy neutron·2022년 9월 10일
2
post-thumbnail

프로그래밍을 공부하다보면 side effect라는 단어를 많이 보게 된다. 함수형 프로그래밍에서는 side effect를 없애는 것이 중요하기 때문에 부정적인 단어로 인식될 수도 있지만 사실 개발자와 뗄 수 없는 관계이다.
이 귀여운 녀석을 리액트와 함께 자세히 알아보자.

side effect란?

컴퓨터 과학에서는 함수가 결과값 이외에 다른 상태를 변경시킬 때 side effect가 있다고 말한다.

쉽게 말해서 함수에 입력값을 넣었을 때 리턴값에 영향을 미치는 모든 작업들을 일컫는다.

함수형 프로그래밍에서는 순수함수(pure function)를 지향하는데 순수함수는 같은 입력값에 대해 같은 출력값이 나오는 함수를 말한다. 바꿔 말하자면 side effect가 발생하지 않는것이다.
side effect는 작업의 출력값을 예상할 수 없기 때문에 작업을 더 어렵게 만드는 경우가 많다.
단적인 예로

let greeting = "Hello";

function sayGreeting(name) {
  return `${greeting} ${name}`;
}

sayGreeting이라는 함수는 외부 변수를 참조하고 있기때문에 만약 greeting이 변경되면 예전과 같은 입력값을 넣어도 다른 출력값이 나오게 된다.
이에 유지보수 측면에서 함수형 프로그래밍은 굉장히 중요한데 React에서도 함수 컴포넌트를 이용한 함수형 프로그래밍을 지향한다.
React는 우선적으로 함수형 프로그래밍 방식을 기본으로 사용하고 컴포넌트간의 의존성을 줄이고(순수함수) 컴포넌트 재사용에 목숨 거는 라이브러리라고 생각한다.

React에서의 side effect

React는 앞서 말했듯이 함수형 프로그래밍을 전제로 하고 있는데 어떠한 경우에도 side effect는 발생할 수밖에 없다.

React에서의 side effect는 외부 세계와 함께 수행되는 모든 행위를 일컫는다. 그렇기 때문에 예측할 수 없다.

예를 들어 서버에 데이터를 요청한 경우 데이터 대신 500상태 코드를 받을 수도 있다.
아주 단순한 어플리케이션을 제외한 사실상 모든 어플리케이션은 side effect에서 벗어날 수 없다.

일반적인 side effect는 다음과 같다.

  • 백엔드 서버에 API로 데이터 요청하기
  • 브라우저 API와 상호 작용(document, window 직접 사용하기 ex) eventListener 등록)
  • 예측할 수 없는(unpredictable 한) timing function 사용하기(setTimeout, setInterval 등)

예시에서 알 수 있듯이 side라는 단어가 어울리지 않을 정도로 중요한 기능들이 많이 들어가있다. 이에 React에서는 side effect를 처리하기 위해 useEffect훅을 제공한다.

리액트는 순수한 함수를 지향하기 때문에 render시 순수한 컴포넌트만을 render해주어야하는데 useEffect는 DOM객체가 이미 업데이트가 되었음을 보장하는 시점에 수행된다.

useEffect는 side effect가 렌더링에 영향을 주지않도록 설계된 것이다.

React 렌더링 과정

render phase & commit phase

리액트의 공식문서를 보면 컴포넌트의 생명주기(컴포넌트의 생성, 갱신, 소멸)를 render phasecommit phase로 나누고 있다.

리액트가 컴포넌트들을 렌더링시키는 과정이 render phasecommit phase로 이루어져있다고 생각하면 되는데 그 과정이 굉장히 재미있다.

1. Render phase

우선 컴포넌트가 렌더되는 경우를 크게 두 가지로 나눌 수 있는데 첫번째는 initial render이고 두번째는 re-render이다.

1-1. initial rendering 과정

1. 우선 컴포넌트가 파싱되고 JSX가 React.createElement를 이용해서 
React elements로 컨버트된다. 
컨버트된 React elements는 메모리에 저장된다.

2. 그 후 React elements를 이용해서 Virtual DOM이 생성되고 
commit phase에 보내진다.

React element란??

여기서 React element란 메모리에서 DOM객체를 나타내는 자바스크립트 객체이다.

const App = () => {
  return <h1>Hello World!</h1>;
}

위의 컴포넌트를 babel transpiler에 넣어보면 이렇게 바뀐다.

const App = () = {
  return /*#_PURE_*/React.createElement("h1", null, "Hello World!");
}

즉 JSX파일은 React.createElement를 통해 React element로 메모리에 저장되고 이를 이용해 VDOM을 생성해 commit phase로 넘어가게 되는 것이다.

1-2. re-rendering 과정

리렌더링은 특정 컴포넌트의 상태가 업데이트 되거나 props가 바뀌면 실행된다.
리렌더링 되는 컴포넌트에는 flag가 세워지고 해당 컴포넌트와 모든 자식 컴포넌트들이 리렌더링 된다.

다음은 리렌더링되는 과정이다.

1. 상태가 바뀐 컴포넌트에 flag를 세운다.

2. initial rendering과 마찬가지로 flag가 있는 컴포넌트와 자식 
컴포넌트들이 파싱되고 JSX가 React element로 바뀌면서 메모리에 
저장된다.

3. 메모리에 저장된 React element로 Virtual DOM을 생성하고 
diffing 알고리즘을 이용해 이전 VDOM과 차이점을 비교한다.
4. 차이점을 commit phase로 넘긴다.

2. Commit Phase

commit phase는 리액트가 실제 DOM을 조작하고 변화를 만들어내는 단계이다.

여기서 변화를 만들어낸다는 것은 state나 props를 변경하고 sideEffect를 발생시킨다는 것이다.(api를 불러온다거나)

이 단계에서는 render phase 에서 diffing 알고리즘을 이용해서 발견한 이전 VDOM과 새로운 VDOM의(initial render에서는 이전 VDOM이 없지만) 차이점을 넘겨받게 된다.

그 후 발견된 변경점을 React DOM 라이브러리를 이용해 실제 DOM에 적용시킨다. DOM에 마운트 시키는것이지 화면에 paint하는 것은 아니다.

이제 useEffect안의 로직같은 sideEffect들이 실행되고 리액트는 다시 한 번 render phase와 commit phase를 거치게 된다.

정리하자면

결국 외부에서 데이터를 받아오면(side effect) 컴포넌트가 생성될 때의 

초기값을 바탕으로 Virtual DOM을 생성하고(render phase) 실제 DOM에 

마운트 하는 과정(commit phase)를 거쳐 변경된 state를 바탕으로 다시 

render와 commit phase를 거쳐 데이터를 갱신하는 과정이라는 것이다.

여기서 드는 의문점..

render phase에서 side effect를 발생시키면 안되나?

commit phase 말고 render phase에서 데이터를 받아오면(side effect) 굳이 두 번 수행할 필요가 없는데 왜 commit phase에서 받아올까?

가장 중요한 이유는 성능이 저하되기 때문이다.

가상돔의 가장 큰 장점은 리렌더링 할때 변경된 부분만 업데이트 할 수 있어서 성능에 큰 영향을 주지 않는다는 것이다.
이것이 리액트의 엄청난 장점 중 하나인데 만약 render phase에 side effect를 야기하는 과정을 포함시키면 side effect가 발생할 때마다 매번 Virtual DOM을 다시 그려야 한다. 또한 데이터 요청의 경우 동기적으로 네트워크 응답을 받아야만 다음 단계로 넘어갈 수 있다.

따라서 리액트는 컴포넌트 내부의 순수한 부분만을 사용해 화면을 그리고 난 후 side effect를 발생시킨다. 이후에 컴포넌트 갱신이 필요하여 다시 모든 과정을 거친다고 해도 Virtual DOM을 이용해 필요한 부분만 업데이트 하기 때문에 성능에 큰 영향이 없다.

마치며

리액트의 렌더과정을 어렴풋이 알고있었는데 side effect를 정리하면서 그 부분까지 공부하게 될줄은 몰랐다. 역시 공부는 다 이어져있나보다. 프론트엔드로 지구정복하는 그 날까지..

참고

profile
프론트엔드로 지구정복

0개의 댓글