TIL [React] 비동기 요청 처리

김은혁·2021년 6월 29일
0

React 데이터 흐름

React 개발 방식의 가장 큰 특징은 컴포넌트 단위로 개발이 된다는 것이다. 먼저 컴포넌트를 만들고 그 컴포넌트를 조립해나가며 페이지를 만들게 된다. 즉 상향식으로 어플리케이션을 만든다는 이야기다. 이렇게 했을 때의 장점은 테스트가 쉽고 확장성이 좋다.

단방향 데이터 흐름

위의 사진처럼 각 컴포넌트는 트리형태로 연결되어 구성이 된다. 그렇다면 데이터는 어떤 컴포넌트에 위치해야할까? 컴포넌트는 컴포넌트 바깥에서 props를 이용하여 데이터를 마치 인자나 속성처럼 전달을 받는다. 즉 데이터를 전달하는 주체는 상위 컴포넌트(부모 컴포넌트)가 되는 것이다. 이는 데이터의 흐름이 하향식으로 흘러간다는 고정된 흐름이 있음을 명시한다.

단방향 데이터 흐름이라는 키워드는 React를 대표하는 설명 중 하나이다. 이러한 데이터 흐름 속에서 컴포넌트는 props를 통해 전달받은 데이터가 어디서 왔는지도 전혀 알 수가 없다.

변하는 값, 즉 상태(state)는 최소화시키는 것이 좋다. 상태가 많아질수록 어플리케이션은 복잡해지기 때문이다. 아래 세 가지 질문에 '그렇다' 라는 대답을 할 수 있다면 그것은 상태가 아니다.

  • 부모로부터 props를 통해 전달되는가?
  • 시간이 지나도 변하지 않는가?
  • 컴포넌트 안의 다른 state나 props를 가지고 계산 가능한가?

그렇다면 상태는 어느 곳에 위치해야 할까? 만약 상태가 한 컴포넌트에서만 의미를 갖는다면 그 컴포넌트에 위치를 하면 되겠지만, 하나의 상태로 두 컴포넌트가 영향을 받는다면 공통으로 속하는 컴포넌트에 위치를 해야 할 것이다. 즉, 두 개의 자식 컴포넌트가 하나의 상태에 접근하고자 할 때는 두 자식의 부모 컴포넌트에 상태를 위치시켜야 한다.

Lifting state up (상태 끌어올리기)

상태의 위치가 전부 정해지고 나면, 부모 컴포넌트에서의 상태가 하위 컴포넌트에 의해 변하는 것을 알 수가 있다. 하지만 데이터의 흐름이 단방향으로 흐른다면 어떻게 이러한 동작이 가능한 것일까? 이때 필요한 것이 상태 끌어올리기이다.

아래 코드에서는 부모와 자식 컴포넌트가 하나씩 존재하는 트리 구조에서 상태를 변경시킬 수 있는 메서드가 존재한다.

import React, { useState } from 'react';

export default function 부모컴포넌트() {
  const [, 값바꾸기] = useState(기본값);
  
  const 상태변경메서드 = () => {
    값바꾸기(새로운 값);
  };
  
  return (
    <div>
    	<div>{}</div>
    	<자식컴포넌트 />
    </div>
  )
}

function 자식컴포넌트() {
  const handleClick = () => {
    // 부모의 상태를 바꾸고 싶다.
  }
  
  return <button onClick={handleClick}></button>;
}

위 코드에서 상태를 변경하는 함수는 상태변경메서드 이다. props를 이용하여 하위 컴포넌트에 전달을 할 수가 있다. 아래 코드를 참조하자.


export default function 부모컴포넌트() {
// 생략
	<자식컴포넌트 handleButtonClick={상태변경메서드} />
// 생략
}
function 자식컴포넌트( {handleButtonClick}) {
  const handleClick = () => {
    // 부모의 상태를 바꾸고 싶다.
    handleButtonClick();
  }
  
  return <button onClick={handleClick}></button>;
}

Effect Hook

Pure Function (순수 함수)

순수 함수란, 오직 함수의 입력만이 함수의 결과에 영향을 주는 함수를 의미한다. 아래 코드와 같은 순수 함수에서는 네트워크 요청과 같은 side effect가 없다. 순수 함수는 어떤 전달 인자가 주어질 경우, 항상 똑같은 값이 리턴됨을 보장하기 때문에 예측이 가능한 함수이다.

function 함수(매개변수) {
  return 매개변수.메서드();
}
함수(인자);

Side Effect (부수 효과)

그렇다면 side effect는 무엇일까? 함수 내에서 어떤 구현이 외부에 영향을 끼치는 경우를 side effect가 있다고 이야기한다.

let 변수 =;

function 함수() {
  변수 = 새 값;
}
함수();

Effect Hook

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

useEffect(()=> {});

useEffect의 첫번째 인자는 함수이다. 이 함수 안에서 side effect를 실행하면 된다. 이 함수는 다음 세 가지 조건에서 실행이 된다.

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

조건부 effect발생 (dependency array)

useEffect(()=> {}, [종속성1, 종속성2, ...]);

useEffect의 두번째 인자는 배열이다. 이 배열은 조건을 담고 있다. 이 조건은 boolean 형태의 표현식이 아닌 어떤 값의 변경이 일어날 때를 의미한다. 따라서, 해당 배열엔 어떤 값의 목록이 들어간다. 이 배열을 종속성 배열이라고 부른다. 배열 내의 종속성 요소들의 값이 변할 때, 첫번째 인자의 함수가 실행된다.

만약 종속성 목록에 아무런 종속성도 없다면 (빈 배열이라면) 두번째 인자로 아무것도 주지 않은 것과 무엇이 다를까? 빈 배열을 인자로 사용할 경우에는 위의 세 가지 조건 중에서 컴포넌트가 처음 생성될 때에만 effect 함수가 실행된다. 이런 방식은 외부 API를 통해 리소스를 받아오고 더이상 API 호출이 필요하지 않은 경우에 사용할 수 있다.

위의 두 가지 경우에서 각각 어떤 장단점이 있을까?
빈 배열을 주고 컴포넌트 내부에서 필터링을 하는 경우(처음 한 번만 렌더링)

  • HTTP 요청의 빈도를 줄일 수 있다 (+)
  • 브라우저(클라이언트)의 메모리 상에 많은 데이터를 갖게 되므로, 클라이언트의 부담이 늘어남 (-)

컴포넌트 외부에서 필터링 (검색어가 바뀔 때마다 외부 API 호출)

  • 클라이언트가 필터링 구현을 생각하지 않아도 됨 (+)
  • 빈번한 HTTP 요청이 일어나게 되며, 서버가 필터링을 처리하므로 서버가 부담 (-)

0개의 댓글