State 끌어올리기, Effect Hook

be kid·2022년 1월 26일
0

BEB - 웹 개발 과정

목록 보기
19/28

다시 재밌는 React 시간이 왔다.
오늘 학습한 내용은 State 끌어올리기(Lifting State Up)Effect Hook에 대한 내용이다.

State 끌어올리기는 이미 실습을 통해 사용해보며 알고 있던 내용이었다. 다만 그게 이렇게 불리는 것을 몰랐을 뿐,,

단방향 데이터 흐름이라는 원칙에 따라 하위 컴포넌트는 상위 컴포넌트로부터 전달받은 데이터의 형태, 타입이 무엇인지만 알 수 있고 state로부터 왔는지, 하드코딩으로 입력한 내용인지는 알지 못한다. 그래서 하위 컴포넌트에서의 어떤 이벤트로 인해 상위 컴포넌트의 상태가 바뀌는 것은 마치 역방향 데이터 흐름과 같이 보일 수 있다.

React는 이를 상위 컴포넌트의 상태를 변경하는 함수 그 자체를 하위 컴포넌트로 전달하고 이 함수를 하위 컴포넌트가 실행하도록 해서 단방향 데이터 흐름 원칙에 부합하는 해결방법을 제시하였고 이것이 상태 끌어올리기인 것이다.

Side Effect와 Pure Function

Effect Hook을 하기전에 우선 2가지 개념을 보았다.

Side Effect (부수 효과)

함수 내에서 어떤 구현이 함수 외부에 영향을 끼치는 경우 해당 함수는 Side Effect가 있다고 말한다.

let foo = "hello";
function bar() {
  foo = "world";
}
bar();

이런 경우 bar는 Side Effect를 발생시킨다고 한다.

Pure Function (순수 함수)

오직 함수의 입력만이 함수의 결과에 영향을 주는 함수를 의미하며 함수의 입력이 아닌 다른 값이 함수의 결과에 영향을 미치는 경우에는 순수 함수라고 부를 수 없다. 또한 순수 함수는 입력으로 전달된 값을 수정하지 않는다.

function upper(str){
  return str.toUpperCase();
}
upper("hello");

순수 함수에는 네트워크 요청과 같은 Side Effect가 없다. 순수 함수의 특징 중 하나는 어떤 전달 인자가 주어진 경우 항상 똑같은 값이 리턴됨을 보장해 예측 가능한 함수이기도 하다는 것이다.

React의 함수 컴포넌트

React의 함수 컴포넌트는 props가 입력으로, JSX Element가 출력으로 나간다. 여기에는 그 어떤 Side Effect도 없으며 순수 함수로 작동한다. 하지만 보통 React 앱을 작성할 때는 AJAX 요청이 필요하거나 LocalStorage 또는 타이머와 같은 React와 상관없는 API를 사용하는 경우가 발생할 수 있다. 이는 React의 입장에서는 전부 Side Effect이다.
React는 Side Effect를 다루기 위한 Hook인 Effect Hook을 제공한다.

Effect Hook

useEffect는 컴포넌트 내에서 Side Effect를 실행할 수 있게 하는 Hook이다.

API

useEffect(함수)
useEffect의 첫번째 인자는 함수이다. 해당 함수 내에서 Side Effect를 실행하면 된다. 이 함수가 실행되는 조건은 다음과 같다.

  • 컴포넌트 생성 후 처음 화면에 렌더링(표시)
  • 컴포넌트에 새로운 props가 전달되며 렌더링
  • 컴포넌트에 상태(state)가 바뀌며 렌더링

이와 같이 매번 새롭게 컴포넌트가 렌더링될 때 Effect Hook이 실행된다.

Hook을 쓸 때 주의할 점

  • 최상위에서만 Hook을 호출한다.
    • 반복문, 조건문 혹은 중첩된 함수 내에서 Hook을 호출하지 말고 early return이 실행되기 전에 항상 React 함수의 최상위(at the top level)에서 Hook을 호출해야 한다.
    • 이 규칙을 따르면 컴포넌트가 렌더링될 때마다 항상 동일한 순서로 Hook이 호출되는 것이 보장된다.
    • React가 useState와 useEffect가 여러 번 호출되는 중에도 Hook의 상태를 올바르게 유지할 수 있도록 해준다.
  • React 함수 내에서 Hook을 호출한다.
    • Hook을 일반적인 JavaScript 함수에서 호출하면 안된다.
    • React 함수 컴포넌트에서 Hook을 호출하거나 Custom Hook에서 Hook을 호출한다.

Effect Hook 조건부 실행

조건부 effect 발생 (dependency array)

useEffect의 두 번째 인자는 배열인데, 이 배열은 조건을 담고 있다. 조건이라고는 하지만 이는 boolean 형태의 표현식이 아닌 어떤 값의 변경이 일어날 때를 의미한다. 따라서 배열은 어떤 값의 목록인 것이다. 이 배열을 종속성 배열이라 한다.

API

useEffect(함수, [종속성1, 종속성2, ...])
두 번째 인자인 종속성 배열 내의 종속성1 또는 종속성2, 3 ... 등의 값이 변할 때 첫 번째 인자의 함수가 실행되는 것이다. 즉, 배열 내의 어떤 값이 변할 때에만 Effect가 발생하는 함수가 실행된다.

만일 종속성 배열에 아무런 종속성이 없다면, 즉 배열을 빈 배열 [ ] 로 둘 경우에는 컴포넌트가 처음 생성될 때만 effect함수가 실행된다. 대표적으로 처음 단 한 번, 외부 API를 통해 리소스를 받아오고 더이상 API호출이 필요하지 않을 때에 사용할 수 있다. 주의할 점은 아예 아무것도 적지 않은, 첫 번째 인자만 적은 것과 껍데기만 있는 빈 배열을 넣은 것은 다르게 동작한다는 점.

컴포넌트 내에서 AJAX 요청하기

Data Fetching

목록 내 필터링을 구현하기 위해서는 다음과 같은 두가지 접근이 있을 수 있다.
1. 컴포넌트 내에서 필터링 : 전체 목록 데이터를 불러오고, 목록을 검색어로 filter하는 방법
2. 컴포넌트 외부에서 필터링 : 컴포넌트 외부로 API 요청을 할 때, 필터링한 결과를 받아오는 방법 (보통 서버에 매번 검색어와 함께 요청하는 경우)

각 방식의 장단점

컴포넌트 내부에서 처리

  • 장점 : HTTP 요청의 빈도를 줄일 수 있다.
  • 단점 : 브라우저(클라이언트)의 메모리 상에 많은 데이터를 갖게 되어 클라이언트의 부담이 늘어난다.

컴포넌트 외부에서 처리

  • 장점 : 클라이언트가 필터링 구현을 생각하지 않아도 된다.
  • 단점 : 빈번한 HTTP 요청이 일어나게 되며, 서버가 필터링을 처리하므로 서버가 부담을 가져간다.

(데이터를 얼마나 불러오는지, 얼마나 자주 요청하는지에 따라 방법을 잘 선택해야할 것 같다.)

AJAX 요청 보내기

useEffect(()=>{
	fetch(`http://서버주소/datas?q=${filter}`)
  		.then(resp => resp.json())
  		.then(result => {
      		setData(result);
    });
}, [filter]);

임의의 서버에 데이터를 요청해서 받는다고 했을 때 위와 같이 할 수 있다.

AJAX 요청이 매우 느릴 경우

모든 네트워크 요청이 항상 즉각적인 응답을 가져다주는 것은 아니기 때문에 외부 API 접속이 느릴 경우를 고려해 로딩화면의 구현은 필수다.
기본적으로 Loading indicator의 구현에도 상태 처리가 필요하다.

fetch 요청의 전후로 setIsLoading을 설정해주어 보다 나은 UX를 구현할 수 있다.

const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
  setIsLoading(true);
  fetch(`http://서버주소/datas?q=${filter}`)
    .then(resp => resp.json())
    .then(result => {
      setProverbs(result);
      setIsLoading(false);
    });
}, [filter]);

// 생략, LoadingIndicator 컴포넌트는 별도로 구현했음을 가정
return {isLoading ? <LoadingIndicator /> : <div>로딩 완료 화면</div>}
profile
개쩌는 개발자가 되고 싶다 !

0개의 댓글