본문 바로가기
코드스테이츠

2/2 일일정리 React 데이터흐름과 Effect Hook

by 강물둘기 2023. 2. 2.

React 데이터 흐름

React로 앱을 만들 때, 컴포넌트는 각각의 기능들을 만든 다음 조립하는 방식인 상향식(bottom-up)으로 만든다.

 

부모 컴포넌트에서 자식 컴포넌트로 props를 이용하여 전달인자(argument)처럼 데이터를 내려줄 수 있다.

이것은 데이터의 흐름이 햐향식(Top-down) 이라는 것을 의미한다.

이 원칙은 매우 중요한데, 단방향 데이터 흐름(one-way data flow)이라는 키워드가 React를 대표하는 키워드중 하나일 정도다.

 

두 개 이상의 컴포넌트에서 같은 상태를 공유해야 하는 경우 그 컴포넌트들의 공통 부모(조상) 컴포넌트를 찾아 상태를 지정해주고 상태를 props로 하위 컴포넌트에게 전달하면 된다.

그런데 이렇게 설정하고 보니 마치 자식 컴포넌트가 부모 컴포넌트의 데이터에 영향을 주는 역방향 데이터 흐름이 나타나는 것 처럼 보인다. 이것을 해결하기 위해 React는 상태 끌어올리기(Lifting State Up)을 제시한다.

 

상위 컴포넌트의 '상태를 변경하는 함수' 자체를 props로 하위 컴포넌트에 전달하고, 이 함수를 하위 컴포넌트가 실행한다.

이것을 상태 끌어올리기라고 한다. 이 방식은 단방향 데이터 흐름을 해치지 않는다.

출처 : codestates

 

Effect Hook

Side effect

함수 내 코드가 함수 외부에 영향을 끼치는 경우 해당 함수는 Side Effect가 있다고 이야기한다.  React에서는 컴포넌트 내에서 fetch를 사용해 API 정보를 가져오거나 이벤트를 활용해 DOM 직접 조작할 때 Side Effect가 발생했다고 한다.

let foo = 'hello';

function bar() {
  foo = 'world';
}

bar(); // bar는 Side Effect를 발생시킵니다!

 

Pure function(순수 함수)

순수 함수란, 오직 함수의 입력만이 함수의 결과에 영향을 주는 함수를 의미한다.

또한 순수 함수는, 입력으로 전달된 값을 수정하지 않는다.

function upper(str) {
  return str.toUpperCase(); // toUpperCase 메소드는 원본을 수정하지 않는다 (Immutable)
}

upper('hello') // 'HELLO'

 

React의 함수 컴포넌트는 Side Effect가 없는 순수 함수로 작동한다.

하지만 보통 React 애플리케이션을 작성할 때에는, AJAX 요청이 필요하거나, LocalStorage 또는 타이머와 같은 React와 상관없는 API를 사용하는 경우 Side effect가 발생할 수 있다.

React는 Side Effect를 다루기 위한 Hook인 Effect Hook을 제공한다.

 

UseEffect

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

 

useEffect의 첫 번째 인자로 함수를 넣어준다. 이 함수 내에서 side effect를 실행하면 된다.

useEffect(() => {
    console.log("Hello", count);
  });

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

출처 : codestates

 

useEffect의 두 번째 인자로 배열을 넣을 수 있다. 이 배열은 조건을 담고 있는데 여기서 조건은 boolean 형태의 표현식이 아닌, 어떤 값의 변경이 일어날 때를 의미한다. 이 배열을 특별히 종속성 배열(dependency array)이라고 부른다.

종속성 배열에 담긴 값의 변화가 일어날 때 마다 effect 효과가 나타난다.

useEffect(() => {
    console.log("Hello", count);
  }, [count]);
// count 값이 변할때마다 effect 효과가 나타난다.

 

두번째 인자로 빈 배열을 넣으면 처음 렌더링될 때 한 번만 실행된다.

useEffect(() => {
    console.log("Hello");
  }, []);
// 처음 렌더링 될때 한 번만 실행된다.

 

두번째 인자를 생략하면 렌더링 될 때 마다 effect가 실행된다.

useEffect(() => {
    console.log("Hello");
  });

 

Ajax 요청

컴포넌트 내에서 Ajax 요청을 할 때 useEffect를 활용할 수 있다.

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

filter 값이 바뀔 때 마다 서버에 데이터를 요청하여 알맞는 데이터를 받아 setStates함수를 이용해 states 변수에 담는다.

 

데이터 로딩이 오래걸릴 수 있기 때문에 로딩 화면을 띄워줘야 하는데, 그럴 때는 useEffect 내부에 로딩 state값을 바꿔주는 함수를 넣어주면 로딩중일때는 로딩화면을 렌더링하고 로딩이 다 되면 로딩 완료 화면을 보여줄 수 있다. 

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

useEffect(() => {
  setIsLoading(true);
  fetch(`http://서버주소/proverbs?q=${filter}`)
    .then(resp => resp.json())
    .then(result => {
      setStates(result);
      setIsLoading(false);	// 로딩완료되면 isLoading을 false로 바꿔준다.
    });
}, [filter]);

// 생략, LoadingIndicator 컴포넌트는 별도로 구현했음을 가정합니다
return {isLoading ? <LoadingIndicator /> : <div>로딩 완료 화면</div>}

 

 

댓글