211122 TIL useEffect
useEffect
Side Effect
Side Effect는 함수가 결과값 이외에 다른 상태를 변경시킬 때를 의미한다.
아래 예시가 있다.
- 함수 외부에 존재하는
count
변수를 읽어서 조작한다. 때문에 Side Effect다.
let count = 0;
function sayHello(name){
count = count + 1; // side effect
return `${name}님 안녕하세요 🥰`;
}
sayHello('소소'); // '소소님 안녕하세요 🥰'
count; // 1
console.log
는 브라우저의 콘솔 창에 출력하는 함수이기 때문에sayHello
에서 값을 반환하는 기능과 별개의 외부 행동을 한다. 때문에 Side Effect다.
function sayHello(name){
console.log(name); // side effect
return `${name}님 안녕하세요 🥰`;
}
sayHello('소소'); // '소소님 안녕하세요 🥰'
- 외부의 값을 변경하지 않고, 전달된 인수로
num
을 받아 내부의 값2
를 더했기 때문에 Side Effect가 아니다.
let count = 0;
function printNum(num){
const result = num + 2; // side effect가 아니다.
return result;
}
printNum(1); // 3
이처럼 외부 값을 읽거나 변화시키면 side effect다.
리엑트에서 컴포넌트 렌더링
Rendering이란 쉽게 말하면 화면에 그리는 것을 의미한다.
리엑트는 MVP에서 View를 담당하는 즉, User Interface를 만들 수 있는 라이브러리다.
이 UI는 컴포넌트 단위로 이뤄져 있는데 컴포넌트가 가진 props
와 state
를 가지고 상태가 변할 때마다 render
함수를 호출하며 그려준다.
즉, props
와 state
가 Input으로 들어가 UI를 표현하는 Output으로 나오는 함수 구조로 볼 수 있다.
컴포넌트를 만들다보면 UI를 그리는 것과 관련 없는 행위들이 존재한다.
예를 들어 Data Fetching, DOM에 직접 접근, setInterval 같은 행위들이 있다.
UI를 그린다는 리엑트의 목표 외에 다른 행동이 일어나는 이것을 리엑트에서는 side Effect라고 부른다.
즉, 리엑트에서 props
와 state
외 다른 행동을 하면 side effect이다.
하지만 props
와 state
이외에 API를 통해 데이터를 받아온다던지, 브라우저를 직접적으로 조작해야하는 등 다양한 행동이 필요하다.
이를 보완하기 위해 나온게 바로 useEffect
hook다.
useEffect
useEffect
는 말그대로 Effect를 쓰겠다, side Effect를 사용하겠다는 의미로 사용한다.
이 useEffect
에 대해 배우기 전 리엑트에서 side Effect를 짚고 넘어가보자.
리엑트에서 sideEffect 문제점
1. 렌더링을 막는 sideEffect
아래와 같이 컴포넌트 안에서 만약 side Effect를 일으키고 UI 렌더링을 반환할 경우 어떤 문제가 생길까?
function Component(){
// sideEffect...
return //UI rendering
}
바로 렌더링을 막는 문제가 생긴다.
UI를 rendering을 하려면 결국 JSX를 return해야하는데
return 전 side Effect 예시로 API를 받아오는 동작을 함수가 처리한다면 그리고 이 처리가 1분 이상이 걸린다면?
그동안 UI렌더링이 되지 않고 사용자는 빈화면만 막연히 보게 된다. 심지어 무슨 문제가 있는지 설명도 보지 못한다.
사용자들이 우리 웹사이트와 빠빠이하는걸 볼 수 있다 👋
2. 계속 trigger되는 sideEffect
리엑트는 상태가 변할 때마다 리렌더링이 실시된다.
리렌더링이 된다는 것은 이 함수형 컴포넌트가 다시 호출된다는 뜻이다.
즉, state
와 props
가 바뀔 때 마다 함수형 컴포넌트가 다시 호출된다.
그럼 이 안에 있는 API를 통한 데이터 호출도 다시 실행되는 것이다.
변할 때마다 데이터를 패칭이 된다면 아주 최악이다.
1분 걸렸던 패칭을 호출될 때마다 걸린다면 서버의 부화와 클라이언트가 필요없는 연산을 계속해야 한다.
우리는 데이터 패칭을 시작할 때 딱 1번 필요하지 계속해서 받아오려고 연산할 필요가 없다.
sideEffect를 해결하는 useEffect
이러한 리엑트의 sideEffect를 해결하기 위해 나온 것이 useEffect다.
useEffect(effect, dependency array);
useEffect
는 리엑트에서 만든 hook이기 때문에 쓰려면import
하는 것을 잊지 말아야한다.
1. 렌더링이 종료되면 실행되는 useEffect
아래 예시를 보면 useEffect가 console.log("function exe");
보다 위에 있음에도 불구하고 콘솔창에는 "function exe"
이 먼저 출력되는 것을 확인할 수 있다.
즉, useEffect
는 렌더링이 종료되고 난 이후 실행됨을 알 수 있다.
2. 매번 바뀔 필요 없이, [Dependency Array]
에 있는 얘들이 바뀔 때만 실행하는 useEffect
useEffect
의 두 번째 인자로 특정 값을 넣으면 이 특정 값이 바뀔 때만 해당 함수를 다시 실행할 수 있다.
// count state가 바뀔 때마다 console.log()가 실행된다.
useEffect(()=>{
console.log('effect trigger');
},[count])
이 useEffect
를 통해 디버깅을 할 수 있다.
[Dependency Array]
- 랜더링 이후 특정 시점(특정 값이 변할 때)에 함수 실행
useEffect(()=>{
console.log('effect trigger');
},[count])
이런 경우 해당 값이 변할 때마다 console.log
가 찍히기 때문에 해당 값이 제대로 동작되는지 확인하는 디버깅용으로도 사용할 수 있다.
- 컴포넌트 마운트 이후 첫 렌더링때만 실행
useEffect(()=>{
console.log('empty dependency');
},[])
- 항상 실행
useEffect(()=>{
console.log('effect trigger');
});
만약 [count, keyState]
같이 배열에 여러 개가 들어있다면 하나만 바뀌어도 실행되기 때문에 만약 따로 실행해야 한다면 분리해서 하나씩만 설정하는 게 좋다.
이는 유지보수 측면에서도 내가 하고자 하는 동작 하나에만 useEffect
적용하는 것이 좋다.
이를 관심사 분리라고 한다. 하나의 동작을 처리한다.
clean up Effect
말 그대로 이펙트를 치운다는 뜻이다.
이전에 내가 발생시킨 이펙트가 단발성이면 괜찮지만 addEventListener()
처럼 구독형 이펙트를 만들었고 해제를 하지 않으면 사용자가 탭을 닫기 전까지 그 이벤트는 계속 일어난다.
리엑트는 SPA기 때문에 불필요한게 계속 돌아가고, 만약 백엔드와 API를 콜이 계속 발생하는 현상이 나타난다.
useEffect(()=>{
// 마운트 시점 동작
const count = setInterval(()=>{
console.log('count');
}, 1000)
// 언마운트 시점 클린 업
return () =>{
clearInterval(count);
}
}, [])
clean up 이펙트가 발생하는 정확한 시점은 다음 이펙트가 발생하기 즉, 전 새로운 이펙트가 발생하기 전에 clean up을 시키고 함수를 발생시킨다.
+) 버튼에 붙은 이벤트는 버튼 자체가 화면에서 사라지기 때문에 clean up을 할 필요가 없다.
Author And Source
이 문제에 관하여(211122 TIL useEffect), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@sozero/211122-TIL-useEffect저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)