[React] 클라이언트 Ajax 요청

play·2022년 6월 15일
0

React

목록 보기
4/9
post-thumbnail
  • React의 데이터 흐름
  • 여러 컴포넌트 사이에서 어떤 방식으로 데이터를 다루는지
  • Effect Hook와 Ajax를 사용해 서버로부터 데이터를 받아오는 방법

Chapter1. React 데이터 흐름

1-1 React 데이터 흐름
1-2 State 끌어올리기 (Lifting State Up)

Chapter2. Effect Hook

2-1 Side Effect
2-2 Effect Hook 기본
2-3 Effect Hook 조건부 실행
2-4 컴포넌트 내에서의 Ajax 요청

Chapter1. React 데이터 흐름

📌 React 데이터 흐름

1. 단방향 데이터 흐름(one-way data flow)

  • 먼저 컴포넌트 단위로 만들고, 페이지를 조립하는 상향식(bottom-up)으로 앱 제작 : 테스트 쉽고, 확장성 좋음
  • 컴포넌트는 컴포넌트 바깥에서 props를 이용해 데이터를 마치 전달인자(arguments) 혹은 속성(attributes)처럼 전달받을 수 있다.
    • 즉, 데이터 흐름이 하향식(top-down)
    • 데이터를 전달하는 주체 : 부모 컴포넌트.
  • state : 모든 데이터를 상태로 둘 필요X, 상태는 최소화로.
  • state로 둘 필요가 없는 경우
    1. 부모로부터 props를 통해 전달되는 경우
    2. 시간이 지나도 변하지 않는 경우
    3. 컴포넌트 안의 다른 state나 props를 가지고 계산 가능한 경우

상태 위치 정하기

상태가 특정 컴포넌트에서만 유의미하다 = 특정 컴포넌트에만 두면 됨
만약 하나의 상태를 기반으로 2개의 컴포넌트가 영향을 받는다 = 공통 소유 컴포넌트를 찾아 그곳에 상태를 위치해야함.
즉, 2자식 컴포넌트가 하나의 상태에 접근하려면 2자식의 공통 부모 컴포넌트에 상태를 위치해야함

2. 역방향 데이터 흐름 추가

아깐 단방향이라면서?
-> 상태 위치를 정하고 나니, 부모 컴포넌트의 상태가 하위 컴포넌트에 의해 변하게 됨.
-> 이를 해결하는 방법 "State 끌어올리기"
-> 상태를 변경시키는 함수를 하위 컴포넌트에 props로 전달해서 해결한다. 마치 콜백함수와 비슷

💡정리
데이터를 전달하는 주체 = 부모 컴포넌트
리액트의 데이터 흐름 = 위에서 아래로 흐르는 하향식 
단방향 데이터 흐름으로 아래에서 위로는 전달할 수 없다.

📌 State 끌어올리기

자식 컴포넌트에 의해 부모 컴포넌트의 state가 바뀌는 경우를 해결하는 역할

단방향 데이터 흐름에 따라 하위 컴포넌트는 상위 컴포넌트로부터 전달받은 데이터 형태에 대해서만 알 수 있다. 그게 state로부터 왔는지에 관한 출처는 알 수 없다.
그러므로 하위 컴포넌트에 의해 상위 컴포넌트의 상태가 바뀌는 건 이해가 안 될 수 있다. React는 다음과 같은 해결책을 제시한다

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

예제

  • 부모와 자식 컴포넌트가 하나씩 존재하는 트리 구조
import React, { useState } from "react";

export default function ParentComponent() {
  const [value, setValue] = useState("날 바꿔줘!");

  const handleChangeValue = () => {
    setValue("보여줄게 완전히 달라진 값");
  };

  return (
    <div>
      <div>값은 {value} 입니다</div>

		// handleChangeValue() 함수를 변수에 담아서 하위 컴포넌트에 보내줘야 한다.
      <ChildComponent handleBtnClick={handleChangeValue}/> 
       
    </div>
  );
}

// handleChangeButton 잘 넣어주고
function ChildComponent({handleButtonClick}) // 그 후 하위 컴포넌트에서는 함수를 설정한 변수명으로 받아야 한다.
{
  const handleClick = () => {
    // 이 버튼을 눌러서 부모의 상태를 바꿀 순 없을까?
    
    // 마지막으로 받아온 함수를 실행하기 위해 인자로 받은 상태 변경 함수를 실행하자!
    handleButtonClick();
  };

  return <button onClick={handleClick}>값 변경</button>;
}
  1. 부모의 상태를 변경하는 함수는 handleChangeValue
  2. props로 전달할 것임. 자식 컴포넌트가 버튼을 눌러 부모의 상태를 바꾸려고 한다. handleButtonClick 이름을 만들어서 넣어주자.
    중괄호 잊지말자

Action Item: 예제 분석

function Twittler() { // 최상위 컴포넌트
  const [tweets, setTweets] = useState([
    {생략}
  ]);

  const addNewTweet = (newTweet) => { // 하위 컴포넌트가 상위 컴포넌트에 영향을 주는 변경함수 
    setTweets([...tweets, newTweet]);
  }; // 이 상태 변경 함수가 NewTweetForm에 의해 실행되어야 합니다.

  return (
    <div>
      <div>작성자: {currentUser}</div>


      <NewTweetForm onButtonClick={addNewTweet}/> // 하위 컴포넌트에 props를 활용하여 변경함수를 전달.
    
    
      <ul id="tweets">
        {tweets.map((t) => (
          <SingleTweet key={t.uuid} writer={t.writer} date={t.date}>
            {t.content}
          </SingleTweet>
        ))}
      </ul>
    </div>
  );
}

function NewTweetForm({ onButtonClick }) { //하위 컴포넌트에 props를 활용하여 변경함수를 전달.
  const [newTweetContent, setNewTweetContent] = useState("");

  const onTextChange = (e) => {
    setNewTweetContent(e.target.value);
  };

  const onClickSubmit = () => {
    let newTweet = {
      uuid: Math.floor(Math.random() * 10000),
      writer: currentUser,
      date: new Date().toISOString().substring(0, 10),
      content: newTweetContent
    };
    // TDOO: 여기서 newTweet이 addNewTweet에 전달되어야 합니다.
    onButtonClick(newTweet) // step4 변경함수 활용해서 상태 끌어 올리기
  };

export default Twittler;

💡정리

  • 상위 컴포넌트의 “상태를 변경하는 함수”를 하위 컴포넌트로 전달하여, 하위 컴포넌트에서 해당 함수를 실행하는 방법
  • 하위 컴포넌트의 상태를 참고해 상위 컴포넌트의 상태를 변경하고, props를 이용해 하위 컴포넌트에 전달한다.
  • 영향을 주는 하위 컴포넌트 -> 상위 컴포넌트 상태 변경 -> 하위 컴포넌트로 props 내려 전달
  • 상태를 변경시키는 함수를 하위 컴포넌트로 props 전달
  • 단방향 데이터 흐름

Chapter2. Effect Hook

React 컴포넌트 외부에서 데이터를 처리하고 받아오는 과정

📌 Side Effect(부수 효과)

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

React 컴포넌트에서의 Side Effect

  • 타이머 사용 (setTimeout)
  • 데이터 가져오기 (fetch API, localStorage)
//전역 변수 foo를 bar라는 함수가 수정하는 예제
let foo = 'hello';

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

bar(); // bar는 Side Effect를 발생

Pure Function (순수 함수)

오직 함수의 입력만이 함수의 결과에 영향을 주는 함수.
입력으로 전달된 값을 수정하지 않는다. 예측 가능한 함수

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

upper('hello') // 'HELLO'

📌 Effect Hook 기본

  • 컴포넌트 내에서 Side effect를 실행할 수 있게 하는 hook
  • React 함수 내에서만 호출해야 한다.
  • React Hook은 최상위에서만 호출해야 한다.

API

useEffect(함수)

useEffect 첫번째 인자: 함수, 두번째 인자: 배열
: 컴포넌트 내에서 Side effect를 실행할 수 있게 하는 Hook.
이 컴포넌트에서 실행하는 Side effect는 브라우저 API를 이용하여 타이틀을 변경한다.

다음과 같은 조건에서 실행됨.

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

Hook 사용 주의사항

  • 최상위에서만 Hook을 호출한다
  • React 함수 내에서 Hook을 호출한다.

📌 Effect Hook 조건부 실행

조건부 effect 발생 (dependency array)

API

useEffect(함수, [종속성1, 종속성2, ...])

useEffect의 두 번째 인자는 종속성 배열(Dependency Array)
배열 내의 종속성1, 또는 종속성2의 값이 변할 때, 첫 번째 인자의 함수가 실행된다.
배열 내의 어떤 값이 변할 때에만, (effect가 발생하는) 함수가 실행된다.

단 한 번만 실행되는 Effect 함수

  1. 빈 배열 넣기
    useEffect(함수, [])
    외부 API를 통해 리소스를 받아오고 더 이상 API 호출이 불필요할 때 사용

  2. 아무것도 넣지 않기 (기본 형태)
    useEffect(함수)
    컴포넌트가 처음 생성되거나, props가 업데이트 되거나, 상태(state)가 업데이트될 때 effect 함수 실행됨

📌 컴포넌트 내에서의 Ajax 요청

AJAX 요청을 보내기

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

AJAX 요청이 매우 느릴 경우

외부 API 접속이 느릴 경우를 고려해 로딩화면 구현은 필수적.
여기에도 상태 처리가 필요하다.

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

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

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

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

💡
서버로 네트워크 요청을 보내야 되는 경우 = Ajax 요청 처리
이 때 Side Effect를 최소화 하기 위해 Effect Hook을 사용한다. 만약 훅을 사용하지 않고 네트워크 요청할 경우 페이지가 제대로 작동하지 않을 수 있다.

profile
블로그 이사했습니다 🧳

0개의 댓글