35, 36일차 React 데이터 흐름의 이해와 비동기 요청

2021. 9. 7. 18:00
반응형

2021. 09. 07 화요일

35일차와 36일차에 같은 내용을 공부했기 때문에 한번에 포스팅 한다.

1. Today's Key points!🔑

  • State 끌어올리기
  • Side Effect, Effect Hook

2. 정리해보자!🧹

React에서 데이터 흐름을 아는 것이 중요하다. 그럼 데이터의 흐름이 어떻게 되냐? 데이터는 아래로 흐른다. 컴포넌트는 컴포넌트 바깥에서 props를 이용해 데이터를 마치 인자(arguments) 혹은 속성(attributes)처럼 전달받을 수 있다. 즉, 데이터를 전달하는 주체는 부모 컴포넌트가 된다.

그럼 이게 왜 중요한건가? 각 컴포넌트마다 state를 만들어서 관리하면 그만큼 비효율적인 것은 없을 것이다. 데이터가 어떻게 흐르는지를 이해하고 그것을 한곳에서만 관리한다면 일일이 state를 만들어줄 필요가 없을 것이고 애플리케이션이 복잡해지는 것을 방지할 수 있다.

그럼 데이터가 아래로 흐른다고 했는데, State 끌어올리기가 무엇이냐? 하위 컴포넌트에서 어떤 이벤트로 인해 상위 컴포넌트의 상태가 바뀌는 것을 말한다. 상위 컴포넌트의 '상태를 변경하는 함수' 그 자체를 하위 컴포넌트로 전달하고, 이 함수를 하위 컴포넌트에서 실행해서 상태를 바꿔준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import React, { useState } from "react";
 
//부모 컴포넌트
export default function ParentComponent() {
  const [value, setValue] = useState("날 바꿔줘!");
 
  const handleChangeValue = (newValue) => { //자식 컴포넌트한데 넘겨줄 함수
    setValue(newValue);
  };
  const handleReset = () => { //자식 컴포넌트한데 넘겨줄 함수
    setValue("날 바꿔줘!");
  }
 
  return (
    <div>
      <div>값은 {value} 입니다</div>
      <ChildComponent handleChangeValue={handleChangeValue}
                      reSet = {handleReset}/> //이렇게 자식 컴포넌트한데 넘겨준다
    </div>
  );
}
 
//자식 컴포넌트
function ChildComponent({handleChangeValue, reSet}) {//부모 컴포넌트한데 받아온 함수
  const handleClick = (e) => {
    handleChangeValue('자식이 넘겨주는 값'); //자식 컴포넌트에서 부모컴포넌트에 인자를 넘겨줄 수 있다.
    if(e.target.textContent === '초기화'){
      reSet();
    }
  };
 
  return <div>
         <button onClick={handleClick}>값 변경</button>
         <button onClick={handleClick}>초기화</button>
        </div>
}
 
cs

위 코드의 결과

다음은 Effect Hook에 대해서 정리해보자.

Side Effect(부수 효과)가 무엇인가? 함수 내에서 어떤 구현이 함수 외부에 영향을 끼치는 경우 해당 함수는 Side Effect가 있다고 이야기 한다.

let foo = 'hello';

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

bar(); // bar는 Side Effect를 발생시킨다

bar라는 함수가 외부의 foo값을 바꿔주는 영향을 주고있기 때문에 Side Effect가 있다고 할 수 있다.

Pure Function(순수 함수)이 무엇인가? 오직 함수의 입력만이 함수의 결과에 영향을 주는 함수이다. 항상 똑같은 값이 리턴됨을 보장한다. 또한, 입력으로 전달된 값을 수정하지 않는다.

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

upper('hello') // 'HELLO'

upper라는 함수는 str을 입력받아서 함수의 결과에만 영향을 준다. upper에 'hello'를 넣었을 때 결과는 'HELLO' 이지만 넣은 'hello'는 그대로 'hello'이다. 이게 직접 문자열을 넣어서 와닿지 않을 수 있다. let greet = 'hello'를 선언하고 upper 함수에 greet을 넣었다고 가정해보자. upper(greet)의 결과는 'HELLO'가 된다. 근데 그렇다고 greet가 'HELLO'가 되지는 않는다는 것이다. greet은 그대로 'hello'이다. 이렇게 순수함수에는 Side Effect가 없다.

그런데 React 애플리케이션을 작성할 때, AJAX 요청이 필요하거나, LocalStorage 또는 타이머와 같은 React와 상관없는 API를 사용하는 경우가 있을 수 있다. 이러한 것들은 React 입장에서는 전부 Side Effect이다. 그래서 React는 Side Effect를 다루기 위한 Hook인 Effect Hook을 제공한다.

그것이 바로 useEffect이다. useEffect는 컴포넌트 내에서 Side effect를 실행할 수 있게 하는 Hook이다.
useEffect의 첫번째 인자는 함수이다. useEffect함수 내에서 side effect를 실행하면 된다. useEffect함수는 컴포넌트 생성 후 처음 화면에 렌더링 될 때, 컴포넌트에 새로운 props가 전달되며 렌더링 될 때, 컴포넌트 상태(state)가 바뀌며 렌더링 될 때 실행된다.
useEffect의 두번째 인자는 종속성 배열이다. 이 배열은 조건을 담고있는데 배열 내의 값이 변할 때, 첫 번째 인자의 함수가 실행된다. 빈 배열을 넣게 되면 컴포넌트가 처음 생성될때만 effect 함수가 실행된다.

다시 한번 정리 해보자면, 리액트 내에서 Side effect를 실행하려면 useEffect를 사용하면 되고, 매 번 새롭게 컴포넌트가 렌더링될 때 Effect Hook이 실행되는데, 무분별하게 실행되는 것을 막아주려면 2번째 인자에 배열안에 어떤 값을 넣어 그 값이 변할 때만 실행되는 조건을 만들어줄 수 있다.

3. Sprint과제 복기!🧐

👉🏻첫번째 수행해야하는 과제는 끌어올리기이다.

Main 컴포넌트 내 search 함수는 검색 조건을 담고 있는 상태 객체 condition을 업데이트 해야한다.

//생략
const [condition, setCondition] = useState({
    departure: 'ICN'
  })

  const search = ({ departure, destination }) => {
    if (condition.departure !== departure || condition.destination !== destination) {
      console.log('condition 상태를 변경시킵니다')
      setCondition({departure, destination})
    }
  }
 //생략

setCondition을 활용해서 출발지나 도착지의 값이 초기값과 달라지면 condition 상태를 {departure : departure, destination: destination}으로 업데이트 해준다.

그리고 이 search 함수를 Search 컴포넌트에 props로 전달해주고 , Search 컴포넌트에서 search 함수를 실행시켜 Main에서 condition의 상태를 업데이트 시켜준다.

function Search({onSearch}) { //search 함수 받아준다.
  const [textDestination, setTextDestination] = useState('')

  const handleChange = (e) => {
    setTextDestination(e.target.value.toUpperCase())
  }

  const handleKeyPress = (e) => {
    if (e.type === 'keypress' && e.code === 'Enter') {
      handleSearchClick()
    }
  }

  const handleSearchClick = () => {//검색 버튼을 누를 때 마다 condition이 업데이트 된다.
    console.log('검색 버튼을 누르거나, 엔터를 치면 search 함수가 실행됩니다')
    //search함수에 인자를 넣어 실행시켜준다. 이 부분이 끌어올리기가 된다.
    onSearch({departure : "ICN", destination: textDestination})
  }
  //생략

👉🏻두번째는 AJAX 요청이다.

컴포넌트 내 필터 함수(filterByCondition)가 아닌, api/FlightDataApi.js에 담긴 getFlight(condition) 함수를 이용한다. 이 함수는 React 컴포넌트와는 별개로 작동하므로, Side Effect로 볼수있다. 그래서 useEffect를 사용해 주어야 한다.

  useEffect(()=> {
    getFlight(condition)
      .then(filterd => setFlightList(filterd));
  },[condition])

  //중략

  <FlightList list={flightList} /> //getFlight 함수에서 필터링이 된 list를 받기 때문에 그냥 flightList를 넘겨주면 된다.

getFlight함수에서 필터가 된 상태를 리턴해주기 때문에 filterByCondition이 필요없게된다.

그리고 로딩 상태에 따라 LoadingIndicator 컴포넌트를 표시해야 한다.

const [isLoading, setIsLoading] = useState(false); //isLoading state를 만들어준다.

//중략

 useEffect(()=> {
    setIsLoading(true); //list를 받아오기 전까지 loading
    getFlight(condition)
      .then(filterd => setFlightList(filterd))
      .then(() => setIsLoading(false)); //다 받아오면 loading 그만
  },[condition])

  //중략
  {isLoading ? <LoadingIndicator /> : <FlightList list={flightList} />}
  //로딩이면? 로딩 컴포넌트 화면에 띄우고, 아니면 목록을 보여줘라

마지막으로 getFlight 함수를 기존 구현 대신에 REST API 호출로 대체해야 한다.

export function getFlight(filterBy = {}) {
  let url = 'http://ec2-13-124-90-231.ap-northeast-2.compute.amazonaws.com:81/flight?departure=ICN';
  if(filterBy.destination){
    url += `&destination=${filterBy.destination}`
  }
  return fetch(url).then(res => res.json());
}

fetch로 AJAX요청을 할 때 받아오는 데이터를 필터된 상태로 받아오기 위해서 "/flight?departure=ICN&destination=CJU" 처럼 API를 사용한다. 근데 destination 부분은 고정값이 아니기 때문에 조건을 걸어줘서 뒤에 붙여주는 형식으로 url을 만들고 그 url대로 요청을 받아서 json처리 해준다.

마무리 Comment😳

이렇게 과제를 마무리했는데, 사실 모든 개념에 대한 것을 확실하게 인지하고 문제를 푼 것 같은 느낌은 들지않는다. 그래서 계속 공부하면서 확실하게 채워지지 않은 개념에 대해서 조금씩 채워가야할 필요성을 느끼고 있다. 우선은 이 과제를 통해서는 끌어올리기, REST API 호출을 하는 방법을 배울 수 있었다.

반응형
LIST

BELATED ARTICLES

more