공식 문서에 따르면 리액트의 데이터 흐름은 하향식(top-down)” 또는 “단방향식”으로만 진행된다. 그 이유는 모든 state는 항상 특정 컴포넌트가 소유하고 있으며, 그 state로부터 파생된 데이터는 오직 자신의 하위 컴포넌트에만 영향을 미칠 수 있기 때문이다. 이를 코드를 통해 보면 다음과 같다.
<Search onSearch={list}>
list라는 props를 받아 검색할 수 있는 Search 컴포넌트가 있다고 하자. 이러한 컴포넌트는 상위 컴포넌트에서 list를 받고 있지만, 이것이 정확히 어디에서 온 것인지는 알지 못한다. 이처럼 리액트의 모든 데이터는 위에서 아래로 한 방향으로만 흐르기 때문에 단방향
이라고 볼 수 있다. 그렇다면 이러한 단방향 데이터 흐름을 가지는 이유는 무엇일까? 이는 state를 소유하거나 설정하는 컴포넌트 이외의 외부 컴포넌트 영향을 원천적으로 차단하는 캡슐화를 위한 것이다.
Side Effect란 함수 내부의 어떠한 기능이 외부에 영향을 미치는 것을 말한다. 이러한 Side Effect 없이 온전히 입력만이 함수의 결과에 영향을 준다면 이를 순수 함수(pure function)라고 부른다.
리액트에서 발생 가능한 대표적인 Side Effect가 바로 fetch API를 이용한 AJAX 요청으로, 함수의 실행 결과가 서버에 달려있기 때문에 이는 순수 함수라고 볼 수 없다. 또한 타이머와 같이 리액트 외부의 API 또한 Side Effect로 볼 수 있으며 이러한 기능을 처리하기 위해 useEffect라는 Hook을 이용한다.
useEffect는 앞서 state에서 살펴보았던 useState와 같은 리액트 Hook이다. Hook은 공식 문서에서 다음과 같이 정의하고 있다.
Hook은 함수 컴포넌트에서 React state와 생명주기 기능(lifecycle features)을 “연동(hook into)“할 수 있게 해주는 함수입니다.
쉽게 말하면 기존 함수형 컴포넌트에서 할 수 없었던 상태관리
를 가능하게 해주는 기능이다.
useEffect는 다음 3가지 경우에 실행된다.
실제 useEffect를 사용한 코드를 작성해보자. 다음과 같이 fetch API를 이용한 AJAX요청을 보내는 코드가 있다고 하자.
useEffect(() => {
fetch(`http://서버주소/item?q=${filter}`)
.then(resp => resp.json())
.then(result => {
setItem(result);
});
}, [filter]);
이러한 요청은 useEffect를 사용하여 해결할 수 있으며 fetch 이후 .then
을 이용해 받은 결과를 처리한다. 여기서 주의할 점은 바로 두 번째 인자인 [filter]로, 이는 종속성 배열이며 이 값이 변할 때 마다 useEffect를 실행하게 된다. 이러한 처리를 아예 생략하게 되면 기본적으로 위의 3가지 조건이 변경될 때 실행된다. 그러나 종속성 배열을 사용하면 특정 인자의 변경에만 반응하므로 더욱 정확하게 사용할 수 있다. 또한 빈 배열([])을 설정하면 컴포넌트가 처음 생성될 때만 실행되며, 특정 상황에서 유용하게 사용될 수 있다.
참고
리액트 공식문서