본문 바로가기
CodeIt_Sprint/React로 데이터 다루기

(5)React로 데이터 다루기_5_"불러오기" 버튼 없이, 웹페이지 열릴때 자동으로 데이터 fetch하기 => useEffect() 메소드

by 코잼민 2024. 12. 31.

● 이론1_ "불러오기" 버튼을 없애고, 자동으로 웹페이지 열리자마자 fetch하게 하기

 실습 구현 코드 : 

- App.js :

import { getReviews } from "../api";
import ReviewList from "./ReviewList";
import { useState } from "react";

function App() {
  const [items, setItems] = useState([]);

  const handleFetchReviews = async () => {
    const { reviews } = await getReviews();
    setItems(reviews);
  };

  const [order, setOrder] = useState("createdAt");

  const sortItems = items.sort((a, b) => {
    return b[order] - a[order];
  });

  const handleLatestSort = () => {
    setOrder("createdAt");
  };
  const handleRatingSort = () => {
    setOrder("rating");
  };

  const handleDeleteClick = (id) => {
    const deleteAfterItems = items.filter((item) => item.id !== id);

    setItems(deleteAfterItems);
  };

  //여기서 getReview를 실행
  handleFetchReviews();

  return (
    <div>
      <button onClick={handleLatestSort}>최신순</button>
      <button onClick={handleRatingSort}>베스트순</button>
      <ReviewList items={sortItems} onDelete={handleDeleteClick} />
      //불러오기 버튼 삭제
    </div>
  );
}

export default App;

 

 

 출력 결과 :

- "F12"를 눌러서 개발자 도구 창의 "Network"창을 보면, 계속 api에 fetch를 하게 되어 무한루프를 돌게 된다. 

=> 해결해야할 점 : 딱 한번만 fetch를 하도록 해야한다.

 해결 코드 => useEffect()메소드 핸들링에 fetch 메소드 작동

▶ import { useState , useEffect } from 'react'; //import에 useEffect 추가

useEffect(()=>{ // fetch}, []);// 추가

 

import { getReviews } from "../api";
import ReviewList from "./ReviewList";
import { useEffect, useState } from "react";

function App() {
  const [items, setItems] = useState([]);

  const handleFetchReviews = async () => {
    const { reviews } = await getReviews();
    setItems(reviews);
  };

  const [order, setOrder] = useState("createdAt");

  const sortItems = items.sort((a, b) => {
    return b[order] - a[order];
  });

  const handleLatestSort = () => {
    setOrder("createdAt");
  };
  const handleRatingSort = () => {
    setOrder("rating");
  };

  const handleDeleteClick = (id) => {
    const deleteAfterItems = items.filter((item) => item.id !== id);

    setItems(deleteAfterItems);
  };

  //여기서 getReview를 실행
  //+ useEffect() 메소드 사용
  useEffect(() => {
    handleFetchReviews();
  }, []);

  return (
    <div>
      <button onClick={handleLatestSort}>최신순</button>
      <button onClick={handleRatingSort}>베스트순</button>
      <ReviewList items={sortItems} onDelete={handleDeleteClick} />
      //불러오기 버튼 삭제
    </div>
  );
}

export default App;

● 이론2_ useEffect() 메소드의 원리

기존 코드에서, 정렬을 할 때, 문제점 :

=> fetch 한 데이터는 Api에서 불러온 데이터 중 일부분인데,  기존 코드에서 sort를 하게되면, 전체 데이터가 아닌 불러온 일부 데이터 내에서만 정렬하게 된다.

=> 해결방법 : Api에서 불러오기 전 데이터를 정렬한 후, fetch()를 하기 => useEffect() 메소드가 활용된다.

useEffect()메소드의 Argument와 원리 :

 

=> 그래서, 이전 코드에서 "[ ]" 빈배열이기에, Fetch가 딱 한번밖에 일어나지 않는다.

useEffect(콜백함수, [ 내용물 ]); 에서, 내용물 : order를 넣어서, 정렬된 데이터를 불러와보기

- useState를 설정해놨던, 정렬 변수 'order'를 useEffect의 내용물에 넣기

=> 베스트순, 최신순 버튼을 누르면, order의 값이 달라지므로, fetch()가 발생한다.

 

이를 이용해, 아예 서버에 원하는 기준의 정렬된 데이터를 요청하기

=> useEffect( fetch메소드 , [order]) + fetch()에 쿼리 사용

◆순서1_ fetch메소드의 매개인자를 order 초기값으로 매개인자

◆순서2_ fetch메소드의내에 쿼리 문자변수 <= `order=${원하는 기준 order 속성명}`;

◆순서3_fetch()내에 url에 쿼리 삽입 => `?${query}`;

//api.js내 fetch 메소드 수정 후, 코드
export async function getReviews(order='createdAt')
{
	const query = `order=${order}`;
    
    const response = await fetch(`https://learn.codeit.kr/api/film-reviews?${query}`);
    //맨 마지막 url 끝에 ? + query
    
    const body = await response.json();
    
    return body;
}

 

◆순서4_ 비동기 load메소드 콜백 매개변수에 orderqurey 추가

//콜백 매개인자에 orderQuery 추가
const handleLoad = async (orderQuery)=>{
	const {reviews} = await getReviews(orderQuery);
    
    setItems(reviews);
};


//useEffect()메소드에 적용
useEffect(()=>{
	handleLoad(order);
},[order]);

 

◎ 연습문제 :

실습 설명

이번엔 정렬을 선택할 때마다 다른 리퀘스트를 보내서 서버에서 정렬된 데이터를 받아 오도록 해 봅시다.

다음을 참고해서 App.js 파일을 수정해 주세요.

  • api.js 의 getFoods 함수는 파라미터로 order 값을 받습니다.
  • useEffect 에서 order 스테이트 값이 바뀔 때마다 실행하도록 구현해 주세요.

 

○ 코드 : 

- api.js

export async function getFoodLists(order = "createdAt") {
  const query = `order=${order}`;

  const response = await fetch(`https://learn.codeit.kr/0000/foods?${query}`);

  const body = await response.json();

  return body;
}

 

- App.js

import FoodList from "./FoodList";
import { getFoodLists } from "../api";
import { useState, useEffect } from "react";

function App() {
  const [items, setItems] = useState([]); // 음식 목록 상태
  const [order, setOrder] = useState("createdAt"); // 정렬 기준 상태

  // 칼로리 기준으로 정렬
  const handleCalorieSort = () => {
    setOrder("calorie");
  };

  // 최신순으로 정렬
  const handleLatestSort = () => {
    setOrder("createdAt");
  };

  // 음식 삭제
  const handleDelete = (id) => {
    const deleteAfterData = items.filter((item) => item.id !== id);
    setItems(deleteAfterData);
  };

  // API로부터 음식 목록을 불러오기
  const handleFetchFoods = async (orderQuery) => {
    const { foods } = await getFoodLists(orderQuery);

    setItems(foods);
  };

  // 정렬된 음식 목록 (불변성을 유지하기 위해 배열 복사 후 정렬)
  const sortedItems = [...items].sort((a, b) => {
    return b[order] - a[order];
  });

  useEffect(() => {
    handleFetchFoods(order);
  }, [order]);

  return (
    <div>
      <button onClick={handleLatestSort}>최신순</button>
      <button onClick={handleCalorieSort}>칼로리순</button>
      <FoodList items={sortedItems} onDelete={handleDelete} />
    </div>
  );
}

export default App;

 

○ 출력 결과 :

● 이론3_ useEffect() 메소드 원리 잘 이해했는지 연습

import { useEffect, useState } from 'react';

function App() {
  const [first, setFirst] = useState(1);
  const [second, setSecond] = useState(1);

  const handleFirstClick = () => setFirst(first + 1);

  const handleSecondClick = () => setSecond(second + 1);

  useEffect(() => {
    console.log('렌더링 이후', first, second);
  }, []);

  console.log('렌더링', first, second);

  return (
    <div>
      <h1>
        {first}, {second}
      </h1>
      <button onClick={handleFirstClick}>First</button>
      <button onClick={handleSecondClick}>Second</button>
    </div>
  );
}

export default App;

 

○ 아래의 코드 관찰 결과 :

정리:

  • [] : 컴포넌트 최초 렌더링 후 한 번만 실행됩니다.
  • [first] : first가 변경될 때마다 실행됩니다.
  • [first, second] : first 또는 second가 변경될 때마다 실행됩니다.