시작전에 생각해봐야 할 것이 있다.
아래의 함수는 어떻게 동작하는가?
const introduce = (name) => `Hello ${name}`;
// input : name
// output : Hello {name}
introduce는 name을 input으로 받아서 Hello name이라는 string을 리턴하는 함수다.
const sum = (num1, num2) => num1 + num2;
// input : num1, num2
// output : num1 + num2
sum함수는 num1, num2를 인풋으로 받아서 num1 + num2를 리턴하는 함수다.
const Component = (state, props) => JSX
// input : state, props
// output : JSX
render라는 함수를 거쳐 오른쪽의 JSX요소로 리턴한다.
즉, 리액트 컴포넌트를 한문장으로 정의하자면 state, props를 받아서 JSX를 리턴하는 함수이다.
React에서 rendering이란 state, props를 기반으로 UI요소를 그려내는 행위이다.
렌더링의 결과물은 UI요소(화면에 JSX문법으로 무엇이 나타날지 적어둔 컴포넌트 들)이다.
렌더링 결과물에 영향을 주는 요소가 state와 props이다.
input(state & props)에 따른 output(UI)를 표현하는 함수와도 구조적으로 동일하다.
🔤 부작용 = 부수적인 작용
부정적인 단어가 아니다. 함수에서 많이 쓰이는 단어이다.
함수의 본질적 목적(input - output) 이외의 부수적으로 나오는 효과를 뜻한다.
Side Effect
X
const sumOne = (num) => {
return num + 1;
}
// input output
// side effect (x), pure function
Side Effect
O
const sumOne = (num) => {
console.log("num=", num);
// 외부에 있는 어떤 물체
// 접근, 읽어오거나, 수정하거나 => side effect
// 외부에 있는 콘솔 log 접근
return num + 1;
}
Side Effect
O
const plusNum = 30;
const sumNum = (num) => num + plusNum;
//plusNum : 함수 바깥에 있는 값 읽어오기
Side Effect
O
let result = 0
const sum = (num) => {
result = result + num;
}
// 외부의 값을 읽어오고 수정까지 했다.
리액트의 side effect 종류는 Data Fetching, DOM에 직접 접근(ex. Event Listener 등록), 구독(ex. setInterval)등이 있다.
Dom은 컴포넌트 안의 값이 아니라 외부 document object model 이다.
setInterval()
함수는 주기적으로 인자를 실행하는 함수
구독(ex. setInterval)
사물의 변화를 지켜보다가 특정 행위를 취하는 것이다.
시간의 흐름을 구독한다고 표현한다.
몇초마다 어떤 행위 할 때 사용한다.
위와 같은 React 코드가 있다. 이 코드는 state가 바뀌면 리렌더링한다.
up
버튼을 눌러서 state가 바뀔때마다 리랜더링되서 콘솔이 찍히는 걸 볼 수 있다.
doSomeSideEffect()
: side effect를 만드는 함수
side effect를 함수 본문에서 바로 실행시키는 방법(=function을 body안에서 실행)에는 문제점 두가지가 있다.
리턴이 마지막에 실행된다.
근데 리턴 밖에 함수들이 오래걸린다면? 함수가 다 실행될때까지 화면이 안나타난다.
그렇다면 렌더링부터하고 side effect를 수행하는게 어떨까? 그렇다면 화면을 막지는(rendering blocking) 않을 것 이다. react 팀에서 이걸 만들어 준게 useEffect
라는 Hook이다.
useEffect
외부에서 만든것이기 때문에 import로 가져와야한다.
import React, { useEffect } from 'react';
useEffect(callback function, dependency Array)
useEffect의 ()
안에 동작하고 싶은 함수를 넣으면 된다.
rendering blocking을 일으키는 함수를 인자로 넣어보자.
useEffect(doSomeSideEffect)
콘솔 순서가 바뀐걸 볼 수 있다. 렌더링 부터 되고 나서 바깥함수가 실행됐다.
useEffect
의 역할이 이것이다. 렌더링 안막을테니 렌더링이 다 된 뒤에 side effect를 실행하라는 것이다.
💡 즉, useEffect는 rendering blocking 되는걸 막아준다.
doSomeSideEffect() // X
useEffect(doSomeSideEffect) // O
Q. useEffect를 여러개 쓸수도 있나요?
사용가능합니다.
여러개 사용한다면useEffect들끼리는 순서대로 실행됩니다.
state가 바뀔때 마다 (렌더링 될 때마다 ) 실행된다.
매번 실행할 필요 없는 것들이 있다.
예를들어 댓글을 생각해보자
외부 API(backend api)를통해서 등록이 되어있는 댓글들을 가져올텐데(data fetching) 렌더링 할때마다 댓글의 모든 정보를 가져오는 것은 비효율적일 것이다. 댓글이 등록될때 (상태가 바뀔때) 댓글의 정보를(상태가 바뀌는 것만)호출하는게 좋을 것이다.
useEffect( () ⇒ {}, [] )
side effect의 실행여부, 실행 타이밍을 결정한다.
rendering 이전과 이후를 비교해서 배열 안의 값이 변경되었다면 side effect를 실행한다.
두번째인자를 안넣으면 매 렌더링마다 실행된다.
근데 두번째 인자를 넣는다면?
useEffect(doSomeSideEffect, [count])
count
가 변했을때만 effect를 실행시켜줘라는 뜻이다.
useEffect(doSomeSideEffect, [count, input])
count
나 input
값중 하나라도 바뀌면 실행된다.
두개들어갔다고 두개 다 바뀌어야 실행되는건 아니다.
빈배열만 들어간다면?
useEffect(doSomeSideEffect, [])
처음에만 실행된다.
처음에만 실행하고 싶을 때 빈배열을 넣어주면된다.
위에서 봤던 댓글 정보의 경우에 의존성 배열에 빈배열만 넣어주면된다.
처음 한번만 등록된 댓글을 렌더링 하고싶기 때문이다.
🔤 정리 청소 치운다
즉, Effect 청소하는 법이다.
side effect를 한번 실행하고 더 이상 필요없어진다면 정리(clean up)한다.
callback function clean up 하기 위한 함수를 리턴
어떤 side effect가 계속 남아있는지 아닌지로 구분한다.
useEffect(()=>{
setInterval(()=>{
console.log("10ms passed")
}, 10)
}, [])
10ms 마다 콘솔이 찍히는 함수를 만들었다고 해보자.
10ms 마다 콘솔창이 찍히는 걸 볼 수 있다.
유저가 링크를 클릭해서 페이지를 이동했다고 가정해보자.
페이지 이동 후에도 콘솔은 계속 찍힌다.
side effect 발생만 시켜놓고 정리를 안해서 계속 남아있는 것이다. 이런게 하나둘씩 쌓이면 사이트가 점점 느려질 것이다.
그래서 우리는 이걸 정리해줘야한다.
그럼 정리는 어떻게할까?
useEffect(()=>{
setInterval(()=>{
console.log("10ms passed")
}, 10)
return () => {}
}, [])
우선 useEffect 콜백 함수내에서 함수를 return한다.
useEffect(()=>{
const id = setInterval(()=>{
console.log("10ms passed")
}, 10);
return () => {
clearInterval(id)
// clearInterval()이 뭐하는 함수인지는 아직 몰라도되고,
// 이걸로 setInterval()을 해지한다 정도 알고있으면 된다.
}
}, [])
이제 다른페이지로 넘어가면 더 이상 콘솔이 찍히지 않는다.
Q. 실행되는 두가지 방법말고 특정조건을 지정할수도 있나요?
No 두가지 외에는 할 수 없다. 통제범위 밖이다. 호출 시첨은 useEffect에서 정해둔것이라 할 수 없다. 보통 그런 상황이 없기도 하다.
유익한 정보 감사드리고 지적드렸던 오타는 얼른 수정해주세요ㅋㅋㅋ