Effect
Effects
What is Effect?
React에서 가장 중요한 3가지를 뽑으라 한다면, 아래와 같을 것이다.
- State : Component의 상태를 저장하는 놈
- Prop : Component간의 데이터를 전송하는 놈
- Effect : Component의 렌더링을 제어하는 놈
Component의 렌더링을 제어한다? 라는 말이 무엇일까?
또한 Effect라는 이름은 또 무엇일까?
State는 상태, Prop은 속성이라는 것이라 말만 들어도 대충 어떤 개념인지 짐작할 수 있다.
그러나 Effect는 영향이라는 말인데, 왜 이게 감자기 갱신을 제어하는 놈이라 하는 걸까?
이를 알아보기 위해서 우선 Pure Function에 대해 탐구해보자.
Pure Function
순수 함수란 함수형 프로그래밍에서 기인한 개념인데, 부수효과(side effect)가 없는 함수를 말한다.
다시 말해
순수 함수란 동일한 input에 대하여 항상 같은 output을 내고(함수의 정의)
외부의 상태와 교류하지 않는(side effect가 없음) 함수를 칭한다.
순수 함수의 예를 생각해보자.
const square = (x) => x * x;
이 square() 함수는 동일한 input에 대하여 언제나 같은 output을 생성하고, 외부의 상태를 변경하지 않으므로 순수함수이다.
반면 순수 함수가 아닌 예를 알아보자.
const random = () => Math.random();
이 random() 함수는 동일한 input이여도 실행될 때마다 다른 output을 내게 되므로, 순수함수가 아니다.
또한, 아까 순수함수였던 square() 을 조그만 만져서
const square = (x) => {
console.log("Hello");
return x * x;
};
이렇게 콘솔에 출력하는 문구를 출력하도록 하면, 이건 더 이상 순수함수가 아니다!!
왜냐하면 콘솔의 상태를 변경하는 side effect가 발생하였기 때문.
자 이쯤하면 순수 함수의 정의를 이해했을 것이다.
그렇다면 다시 Effect에 대한 이야기로 돌아가자.
Effect
Effect는 Component가 최대한 순수함수가 되도록 하는 놈이다.
다시 말해 어떤 외부의 변화가 감지돼도, 최대한 이 외부의 변화를 무시하게 해준다.
원래는 Component의 state가 하나만 바뀌어도 Component의 전체 코드를 실행하게 되는데,
Effect는 딱 특정한 state만 바뀔때 Component의 코드를 실행하게 한다.
When use Effect?
Effect는 특정 코드들이 첫번째 component rendering에서만 실행되게 하거나, 나중에 state가 바뀌어도 그 코드는 실행되지 않게 할 때 사용한다.
즉, 너무 잦은 코드의 호출을 막기 위해 사용한다.
그리고 이는 통신, API 호출시에 사용한다.
생각해보라. 만약 주식 API를 불러다 쓰는 API가 있는데, 주가는 실시간으로 몇 ms마다 오르락 내리락 하는데, 이 때마다 Component가 끊임없이 re-rendering된다면 우리의 RAM이 힘들어 할 것이고 우리의 혈압도 올라갈 수가 있다.
Why using Effect?
Effect를 왜 쓰는지 알아보기 위해서 Effect가 사용되지 않았을 경우 발생하는 불상사에 대해 알아보자.
간단히 위와 같은 화면을 만들었고, 무슨 동작을 할지는 다들 예상되었으리라 믿는다.
버튼을 누르면 버튼 안의 숫자가 증가하고, 검색창에 입력하면 검색창이 검색하려는 단어로 입력된다.
function App() {
const [counter, setCounter] = useState(0);
const [keyword, setKeyword] = useState(0);
console.log("I'm rendered!");
const onChange = (event) => {
setKeyword(event.target.value);
};
const onClick = () => {
setCounter((prev) => prev + 1);
};
return (
<div>
<h1>Counter</h1>
<button onClick={onClick}>{counter}</button>
<h1>Search</h1>
<input
type="text"
placeholder="Input"
value={keyword}
onChange={onChange}
></input>
</div>
);
}
그런데, 문제가 만일 counter를 증가시키면 App() Component 전체가 실행되면서 굳이 실행이 필요하지 않은 부분인 검색창에 대한 코드도 실행되고
반대로 검색창에 숫자를 입력해도 App() Component 전체가 실행되면서 불필요한 Counter가 렌더링을 다시하게 된다.
콘솔창은 위와 같이 계속 새로 렌더링 되었다고 도배가 된다…
이러한 불필요한 코드의 실행(Component의 무분별한 re-rendering)을 막기 위해서, 외부의 변화를 딱 필요할 때만 이용하기 위해 useEffect()를 사용한다.
How to use Effect?
useEffect()
Effect는 React.useEffect()로 제어할 수 있고, 형식은 다음과 같다.
useEffect(function, [effect]);
첫번째 인자인 funcntion은 effect가 변화함에 따라 실행할 함수, 두번째 인자인 effect는 React.js가 지켜보는 변수(변화가 일어나는 놈)를 넣는 배열이다.
예를 들어 아래와 같이 작성했다면
useEffect(() => {
console.log("I'm updated!");
}, [counter]);
I’m updated!라는 콘솔 출력은 counter 변수에 변화(effect)가 일어났을 경우에만 출력되게 한다.
두번째 인자에 아무것도 넣지 않으면, 지켜볼 변수가 없으므로 변수의 변화(effect) 또한 없을 것이며, 따라서 처음 렌더링 되었을 경우에만 실행되고 그 이후에는 실행되지 않을 것이다.
또한 2개 이상의 변수를 넣으면 2가지 변수중 어느 하나라도 변화하게 될 때 실행될 것이다.
cleanup function
아래 코드의 useEffect()는 지켜볼 변수가 없으므로(두번째 인자에 변수가 없음) 오직 Component가 생성될 때만 실행될 것이다.
useEffect(() => {
console.log("I'm created!");
}, []);
그렇다면, 반대로 Component가 소멸되었을 때 코드를 실행시키는 방법은 없을까?
그게 바로 cleanup function이다.
사용 방법은 간단한데, useEffect() 함수의 return 값으로 cleanup function을 넘겨주기만 하면 끝이다.
useEffect(() => {
console.log("I'm created!");
return ()=>console.log("I'm distroyed..");
}, []);
생각해보면 Component가 소멸하게 된다면 Component의 모든 정보가 소멸, useEffect()함수도 종료하게 되는데, 그때 return에 있던 함수가 실행되는 것은 cleanup function이 기재된 위치가 나름 합리적인 것을 알 수 있을 것이다.
그렇다면 마지막으로 주제에 벗어나지만 언제 Component가 소멸하는지 간단하게 알아보자.
{
mode ? <Counter /> : <Search />;
}
위와 같이 조건부 렌더링이 될때, 선택받지 못한 Component는 소멸하게 된다. (단순히 디스플레이에서 안보이는 차원이 아니라 아예 소멸)
Mode를 변경하면 Counter, Search중 하나가 렌더링 된다. (다른 것은 소멸한다)
전체 코드는 아래와 같다.
import { useState, useEffect } from "react";
const Counter = () => {
const [counter, setCounter] = useState(0);
const onClick = () =>
setCounter((prev) => prev + 1);
useEffect(() => {
console.log("counter is updated!");
return () =>
console.log("counter is destroyed...");
}, [counter]);
return (
<div>
<h1>Counter</h1>
<button onClick={onClick}>{counter}</button>
</div>
);
};
const Search = () => {
const [keyword, setKeyword] = useState(0);
const onChange = (event) => {
setKeyword(event.target.value);
};
useEffect(() => {
console.log("keyword is updated!");
return () =>
console.log("keyword is destoryed...");
}, [keyword]);
return (
<div>
<h1>Search</h1>
<input
type="text"
placeholder="Input"
value={keyword}
onChange={onChange}
></input>
</div>
);
};
function App() {
const [mode, setMode] = useState(true);
return (
<div>
<h1>Mode</h1>
<button
onClick={() => setMode((prev) => !prev)}
>
Change Mode
</button>
{mode ? <Counter /> : <Search />}
</div>
);
}
export default App;
Review
- React는 Prop을 통해 데이터를 전달받을 수 있으며 이 데이터를 State에 저장해 관리할 수 있다.
- React는 똑똑해서 State에 변화가 있을 경우 Component를 re-rendering하여 자동으로 변경 사항을 반영한다.
- 그렇지만 React 존나 똑똑해서 State가 변화가 있으나, 특정 State가 바뀔 경우만 re-rendering시킬 수 있고 이게 Effect이다.
Leave a comment