함수의 로컬 상태를 함수 외부에서 변경하는 경우, side effect가 발생했다고 한다.
이 개념을 react에 적용하면, function component 외부에서 로컬 상태의 값이 변경되는 것이 side effect라고 할 수 있다.
흔한 경우로 비동기 처리가 있을 것이다.
즉, react에서 side effect란, 리액트 컴포넌트가 화면에 렌더링 된 이후에 비동기적으로 처리되어야하는 부수적인 효과들을 side effect라고 일컫는다.
React 마운트, 업데이트 과정 / 라이프 사이클
- Render phrase에서 리액트는 새로운 props와 state를 바탕으로 Virtual DOM에 반영할 부분을 계산하며, Virtual-DOM을 생성한다.
(추가로 갱신의 경우에는 어떤 차이가 있는지 파악한다.)
한마디로 state와 props를 바탕으로 어떤 화면이 나타나야 하는지 리액트가 파악하는 과정이다. - 컴포넌트를 DOM에 마운트하는 과정을 거친다.(트리에 삽입)
- 클래스 컴포넌트에서는 componentDidMount(한번만)가, function 컴포넌트에서 useEffect 안에서 state값이 업데이트 되었다면, 변경된 state를 바탕으로 다시 render와 commit 단계를 거쳐 데이터를 갱신하는 과정
Side Effect
함수 내에서 어떤 구현이 함수 외부에 영향을 끼치는 경우 해당 함수는 Side Effect가 있다고 이야기한다.
- 함수 외적인 것에 영향을 주면 안된다
- 항상 동일한 값을 가져와야 한다(예측가능해야한다)
Pure Function 순수 함수
순수 함수란, 오직 함수의 입력만이 함수의 결과에 영향을 주는 함수를 의미한다. 함수의 입력이 아닌 다른 값이 함수의 결과에 영향을 미치는 경우, 순수 함수라고 부를 수 없다. 또한 순수 함수는, 입력으로 전달된 값을 수정하지 않는다.
순수 함수에는 네트워크 요청과 같은 Side Effect가 없다. 순수 함수의 특징 중 하나는, 어떠한 전달 인자가 주어질 경우, 항상 똑같은 값이 리턴됨을 보장한다. 그래서 예측 가능한 함수이기도 하다.
- 함수 외적인 것에 영향을 주면 안된다
- 항상 동일한 값을 가져와야 한다 (예측 가능해야한다)
React에서 Side-Effect 처리
side effect가 있는 함수처리
function UserProfile({ name }) { const message = `${name}님 환영합니다!`; //함수 반환 값 생성 // BAD document.title = `${name}의 개인정보`; //함수 외부와 상호작용하는 Side-effect 코드 return <div>{message}</div>; }
위의 코드는 함수형 컴포넌트가 실행되고 결과를 생성하는 것과 무관한 document.title을 수정하고 있다.
위의 코드를 useEffect()로 분리하면 다음과 같다.
function UserProfile({ name }) { const message = `${name}님 환영합니다!`; //Side-Effect 코드를 UseEffect로 분리 useEffect(() => { document.title = `${name}의 개인정보`; }, [name]); return <div>{message}</div>; }
1. fetch API는 왜 순수함수가 아니고, Side Effect가 있는가?
우선, Side Effect이란 무엇인가.
- Side Effect는 함수 실행이 함수 외부에 영향을 미치는 것이다.
- 예를 들면, 함수 외부의 변수에 영향을 주는 것, 다른 함수를 실행시키는 것, 동일한 결과값을 리턴하지 않는 것, 예상 가능하지 않은 결과를 리턴하는 것, 외부로부터 데이터를 받아오는 것 등이 있을 것이다.
순수함수는 오직 함수의 입력만이 함수의 결과에 영향을 주는 함수이다.
즉, 정리하면 순수함수는 다음과 같다.
- 동일한 인자가 들어갈 경우 항상 같은 값이 나와야 한다.(예측이 가능해야 한다.)
- 부수적인 효과가 일어나면 안 된다.
- return 값으로만 소통한다.
그렇다면, fetch의 경우는 어떠한가?
fetch는 첫번째 인자로 받은 URL로 해당 데이터를 반환받게 되는데,
- 네트워크 요청을 하는 경우 응답받는 속도가 다를 수 있고, 응답이 다를 수 있기 때문에, 순수함수가 아닌 것이다.
- 또한, 외부로부터 데이터를 받아오기 때문에, side effect가 있는 것이고, react에서는 보통 fetch로 받아온 데이터로 state값을 변경해 준다. 그렇다면 함수 외부의 setState를 호출하고 state를 변경해 준 것이기 때문에, side effect가 있다고 말하는 것이다.
때문에, fetch는 useEffect안에 써주게 되는 것이다.
그렇다면 왜 Side Effect가 발생하면 useEffect 안에 써주는 것일까?
2. Side Effect는 왜 useEffect 안에서 실행시켜야 하는가?
외부에서 데이터를 받아와(Side Effect) state, prop이 업데이트가 되면 컴포넌트가 생성될 때의 초기값을 바탕으로 Virtual-DOM을 생성하고(render phrase) 실제 DOM에 마운트 하는 과정을 거치게 된다. 만약 변경된 state가 있다면 다시 render와 commit 단계를 거쳐 데이터를 갱신하는 과정을 수행한다.
그러면, 아예 render 전에 데이터를 받아오면(부수효과를 만들고) 되는데 왜 굳이 두번 수행하는 것일까?
결론부터 이야기하면 성능이 저하된다.
가상돔을 그리는 과정에서는 오직 컴포넌트의 "순수한 부분"만 포함되어야 한다. 그래야 리액트의 핵심인 이전 렌더링 결과와 이번 렌더링 결과를 비교해 바뀐 것만 비교해 컴포넌트를 업데이트 할 수 있기 때문이다.
Side Effect를 야기하는 과정을 가상돔을 만드는 과정(render phrase)에 포함되면 부수 효과가 발생할 때 마다 Virtual-DOM을 다시 그려야 한다. 또한 데이터 요청의 경우에는 동기적으로 네트워크 응답을 받아야만 다음 과정이 진행될 수 있다.
따라서 리액트는 컴포넌트 내부의 순수한 부분을 사용해 화면을 그리고 난 후 부수효과를 발생시킨다. 이후에 컴포넌트 갱신이 필요하다면 다시 모든 과정을 거쳐 다시 렌더링하는 과정을 거쳐도 가상돔을 활용해 필요한 부분만 업데이트 하여 성능에 큰 영향을 주지 않는다.
3. 여러개의 useEffect는 어떤 순서로 실행되는가?
이 질문의 시작은 useEffect가 마치 비동기적으로 실행되는 모습을 보여서였다. 하지만, useEffect가 비동기로 동작하는 것이 아니라, life cycle에 따라 랜더링 된 후에 시작되는 hook인 것이다.
즉, useEffect는 랜더링 후 실행되고, 여러 리액트가 있는 경우 동기적으로 실행된다.
'React' 카테고리의 다른 글
use Effect (0) | 2021.12.26 |
---|---|
리액트 훅이란? (0) | 2021.12.26 |