let foo = 'hello';
function bar(){
foo = 'world';
}
bar(); // bar는 Side Effect를 발생시킵니다.
function upper(str){
return str.toUpperCase(); //toUpperCase 메소드는 원본을 수정하지 않습니다.(immutable)
}
upper('hello') // 'HELLO'
순수 함수에는 네트워크 요청과 같은 Side Effect가 없습니다. 순수 함수의 특징 중 하나는, 어떠한 전달 인자가 주어질 경우, 항상 똑같은 값이 리턴됨을 보장합니다. 그래서 예측 가능한 함수이기도 합니다.
Q. Math.squrt()
는 순수 함수인가요?
A. Math.squrt()
는 전달 인자 x
의 제곱근 값을 구하는 메서드입니다. 주어진 인자에 대해 항상 동일한 결과값을 리턴하므로 순수함수입니다.
Q. Math.random()
은 순수 함수인가요?
A.
다음 예제를 생각해봅시다.
Math.random(); // => 0.4011148700956255
Math.random(); // => 0.8533405303023756
Math.random(); // => 0.3550692005082965
함수를 호출할 때 우리가 아무런 인자도 넘기지 않았음에도 불구하고, 이 함수들은 각각 다른 출력 값을 만들어냅니다. 이 사실이 의미하는 바는 Math.random() 함수는 순수하지 않다는 것을 의미합니다.
Q. 어떤 함수가 fetch API를 이용해 AJAX 요청을 한다고 가정해 봅시다. 이 함수는 순수 함수가 아닙니다. 왜일까요?
A. Ajax 요청은 외부 상태를 바꾸기 때문에 해당 기능을 가진 함수는 순수 함수가 아닙니다.
function SingleTweet({writer, body, createdAt}){
return <div>
<div>{writer}</div>
<div>{createdAt}</div>
<div>{body}</div>
</div>
}
하지만 보통 React 애플리케이션을 작성할 때는 AJAX 요청이 필요하거나, LocalStorage 또는 React와 상관없는 API를 사용하는 경우가 발생할 수 있습니다. 이는 React의 입장에서는 전부 Side Effect입니다.
React 컴포넌트의 Side Effect
순수 함수의 출력값에 영향을 미치는 작업들이 바로 Side Effect라고 할 수 있습니다. 따라서 Side Effect를 최소화하거나 따로 분리하여 함수로 묶어주는 작업은 해당 프로젝트나 소프트웨어의 유지보수를 좀 더 수월하게 해줍니다.
Effect Hook
은 다음의 경우처럼 매번 새롭게 컴포넌트가 렌더링 될 때 Effect Hook이 실행됩니다.
Hook을 쓸 때 주의할 점은 다음과 같습니다,.
최상위에서만 Hook을 호출합니다.
그래야만 컴포넌트가 렌더링 될때마다 동일한 순서의 Hook 호출이 보장됩니다.
React 함수 내에서 Hook을 호출합니다.
useEffect
useEffect
는 컴포넌트 내에서 Side Effect를 실행할 수 있게 하는 Hook입니다.
어떤 컴포넌트가 Mount
(화면에 첫 렌더링)되었을 때, Update
(다시 렌더링)될 때 혹은 Unmount
(화면에서 사라질 때) 특정 작업을 처리할 코드를 실행시키고자 할 때 useEffect()
를 사용하면 됩니다.
useEffect(()=>{})
useEffect
Hook은 인자로 콜백 함수를 받습니다.useEffect
Hook은 두 가지 형태가 있습니다.
첫 번째 형태는 인자로 콜백 함수만 받습니다.
실행은 컴포넌트가 렌더링 될때마다 매번 실행됩니다.
useEffect(()=>{
// 작업..
});
두 번째 형태는 첫 번째 인자로 콜백 함수를 받고 두 번째 인자로 배열을 받습니다. 이 배열은 다른 이름으로 dependency array(종속성 배열)
라고도 합니다.
실행은 화면에 첫 렌더링(Mount
) 될때, value 값이 바뀔때마다 실행됩니다.
useEffect(()=>{
// 작업..
}, [value]);
dependency array(종속성 배열)은 조건을 담고 있습니다. 여기서 조건은 boolean 형태의 표현식이 아닌, 어떤 값의 변경이 일어날 때 를 의미합니다. 따라서 해당 배열엔 어떤 값 의 목록이 들어갑니다.
만약 두 번째 인자로 빈 배열이 전달된다면 실행은 화면에 첫 렌더링(Mount) 될때만 실행됩니다.
useEffect(()=>{
// 작업..
}, []);
const Timer = (props)=>{
useEffect(()=>{
// 타이머 컴포넌트가 맨 처음 브라우저에 렌더링(Mount) 됐을 때 실행
const timer = setInterval(()=>{
console.log('타이머, 돌아가는 중..')}, 1000)
// 타이머 컴포넌트가 화면에서 사라질 때(Unmount) 실행됩니다.
return ()=>{
ClearInterval(timer)
}
}, []);
}
목록 내 필터링을 구현하기 위해서는 다음과 같은 두 가지 접근을 할 수 있습니다.
컴포넌트 내에서 필터링 : 전체 목록 데이터를 불러오고, 목록을 검색어로 filter하는 방법(처음 단 한 번, 외부 API로부터 목록 데이터를 받아오는 경우가 해당합니다.)
컴포넌트 외부에서 필터링 : 컴포넌트 외부로 API 요청을 할 때, 필터링 한 결과를 받아오는 방법(서버에 매번 검색어와 함께 요청하는 경우가 이에 해당합니다.)
두 방식의 차이점은 다음과 같습니다.
방식 | 장점 | 단점 |
---|---|---|
컴포넌트 내부에서 처리 | HTTP 요청의 빈도를 줄일 수 있다. | 브라우저(클라이언트)의 메모리 상에 많은 데이터를 갖게 되므로, 클라이언트의 부담이 늘어난다. |
컴포넌트 외부에서 처리 | 클라이언트가 필터링 구현을 생각하지 않아도 된다. | 빈번한 HTTP 요청이 일어나게 되며, 서버가 필터링을 처리하므로 서버가 부담을 가져간다. |
Q. React에서 Ajax 요청을 보낼 때 Effect Hook을 사용하는 것이 좋나요?
A. 웹 앱을 구성할 때, 서버로의 네트워크 요청을 보내야 되는 경우가 있습니다. React에서는 이러한 Ajax 요청을 처리할 때, Side Effect를 최소화하기 위해서 Effect Hook을 사용합니다. 만약 Hook을 사용하지 않고 네트워크 요청을 하면 그 동안에 페이지가 멈추거나 깜빡할 수 있습니다.