icon안동민 개발노트

훅을 활용한 간단한 데이터 패치 앱


 이 실습에서는 지금까지 학습한 React 훅들을 종합적으로 활용하여 외부 API에서 데이터를 가져와 표시하는 간단한 애플리케이션을 만들어보겠습니다.

 이 과정에서 useState, useEffect, useContext, 그리고 커스텀 훅을 사용하여 효율적인 데이터 관리와 컴포넌트 구조를 구현할 것입니다.

1단계 : 프로젝트 설정

 먼저 새로운 React 프로젝트를 생성합니다.

npx create-react-app data-fetch-app
cd data-fetch-app
npm start

2단계 : 컴포넌트 구조 설계

 우리의 앱은 다음과 같은 컴포넌트 구조를 가집니다.

  1. App : 최상위 컴포넌트
  2. DataProvider : Context를 제공하는 컴포넌트
  3. DataList : 데이터 목록을 표시하는 컴포넌트
  4. DataItem : 개별 데이터 항목을 표시하는 컴포넌트
  5. SearchBar : 데이터 검색을 위한 입력 컴포넌트

3단계 : Context 생성 및 Provider 구현

 먼저 전역 상태를 관리할 Context와 Provider를 만들겠습니다.

 src/contexts/DataContext.js:

import React, { createContext, useState } from 'react';
 
export const DataContext = createContext();
 
export const DataProvider = ({ children }) => {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const [searchTerm, setSearchTerm] = useState('');
 
  return (
    <DataContext.Provider value={{ data, setData, loading, setLoading, error, setError, searchTerm, setSearchTerm }}>
      {children}
    </DataContext.Provider>
  );
};

4단계 : 커스텀 훅 만들기

 데이터 페칭 로직을 재사용 가능한 커스텀 훅으로 만들겠습니다.

src/hooks/useDataFetch.js
import { useContext, useEffect } from 'react';
import { DataContext } from '../contexts/DataContext';
 
const useDataFetch = (url) => {
  const { setData, setLoading, setError } = useContext(DataContext);
 
  useEffect(() => {
    const fetchData = async () => {
      setLoading(true);
      try {
        const response = await fetch(url);
        if (!response.ok) throw new Error('Network response was not ok');
        const result = await response.json();
        setData(result);
      } catch (error) {
        setError(error.message);
      } finally {
        setLoading(false);
      }
    };
 
    fetchData();
  }, [url, setData, setLoading, setError]);
};
 
export default useDataFetch;

5단계 : 컴포넌트 구현

 이제 각 컴포넌트를 구현해보겠습니다.

src/components/SearchBar.js
import React, { useContext } from 'react';
import { DataContext } from '../contexts/DataContext';
 
const SearchBar = () => {
  const { searchTerm, setSearchTerm } = useContext(DataContext);
 
  return (
    <input
      type="text"
      placeholder="Search..."
      value={searchTerm}
      onChange={(e) => setSearchTerm(e.target.value)}
    />
  );
};
 
export default SearchBar;
src/components/DataItem.js
import React from 'react';
 
const DataItem = ({ item }) => (
  <li>{item.title}</li>
);
 
export default DataItem;
src/components/DataList.js
import React, { useContext } from 'react';
import { DataContext } from '../contexts/DataContext';
import DataItem from './DataItem';
 
const DataList = () => {
  const { data, loading, error, searchTerm } = useContext(DataContext);
 
  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;
 
  const filteredData = data.filter(item => 
    item.title.toLowerCase().includes(searchTerm.toLowerCase())
  );
 
  return (
    <ul>
      {filteredData.map(item => (
        <DataItem key={item.id} item={item} />
      ))}
    </ul>
  );
};
 
export default DataList;

6단계 : App 컴포넌트 구현

 마지막으로 App 컴포넌트에서 모든 것을 조합합니다.

src/App.js
import React from 'react';
import { DataProvider } from './contexts/DataContext';
import SearchBar from './components/SearchBar';
import DataList from './components/DataList';
import useDataFetch from './hooks/useDataFetch';
 
function App() {
  useDataFetch('https://jsonplaceholder.typicode.com/posts');
 
  return (
    <div className="App">
      <h1>Data Fetch App</h1>
      <SearchBar />
      <DataList />
    </div>
  );
}
 
const WrappedApp = () => (
  <DataProvider>
    <App />
  </DataProvider>
);
 
export default WrappedApp;

7단계 : 스타일링 (선택사항)

 간단한 CSS를 추가하여 앱의 외관을 개선할 수 있습니다.

 src/App.css 파일에 다음과 같은 스타일을 추가할 수 있습니다.

.App {
  max-width: 800px;
  margin: 0 auto;
  padding: 20px;
}
 
input {
  width: 100%;
  padding: 10px;
  margin-bottom: 20px;
  font-size: 16px;
}
 
ul {
  list-style-type: none;
  padding: 0;
}
 
li {
  padding: 10px;
  border-bottom: 1px solid #eee;
}
 
li:last-child {
  border-bottom: none;
}

작동 방식 설명

  1.  Context와 전역 상태 관리 : DataProvider는 애플리케이션의 전역 상태를 관리합니다. 여기에는 가져온 데이터, 로딩 상태, 오류 상태, 그리고 검색어가 포함됩니다.

  2.  커스텀 훅을 통한 데이터 페칭 : useDataFetch 훅은 주어진 URL에서 데이터를 가져오고, Context를 통해 상태를 업데이트합니다. 이 훅은 useEffect를 사용하여 컴포넌트 마운트 시 데이터 페칭을 수행합니다.

  3.  검색 기능 : SearchBar 컴포넌트는 사용자 입력을 받아 Context의 searchTerm을 업데이트합니다. DataList 컴포넌트는 이 searchTerm을 사용하여 데이터를 필터링합니다.

  4.  데이터 표시 : DataList 컴포넌트는 Context에서 데이터, 로딩 상태, 오류 상태를 가져와 적절히 표시합니다. 필터링된 데이터는 DataItem 컴포넌트를 사용하여 렌더링됩니다.

  5.  성능 최적화 : Context를 사용함으로써, 상태 변경 시 필요한 컴포넌트만 리렌더링됩니다. 예를 들어, 검색어가 변경되면 DataList만 리렌더링됩니다.

주요 React 훅 활용

  • useState : DataProvider에서 전역 상태를 관리하는 데 사용됩니다.
  • useEffect : useDataFetch 훅에서 데이터 페칭을 수행하는 데 사용됩니다.
  • useContext : 각 컴포넌트에서 Context에 접근하는 데 사용됩니다.
  • Custom Hook : useDataFetch는 데이터 페칭 로직을 재사용 가능한 형태로 추상화합니다.

 이 실습을 통해 우리는 React 훅을 사용하여 효율적이고 재사용 가능한 방식으로 데이터를 관리하고 표시하는 애플리케이션을 구현했습니다.

 Context API를 통한 전역 상태 관리, 커스텀 훅을 통한 로직 재사용, 그리고 함수형 컴포넌트와 내장 훅을 활용한 선언적 프로그래밍 방식을 경험할 수 있었습니다.

 이 애플리케이션은 기본적인 기능만을 구현했지만, 여기에 더 많은 기능을 추가할 수 있습니다.

 예를 들어, 페이지네이션, 데이터 정렬, 개별 항목 상세 보기 등의 기능을 추가하여 더 복잡하고 실용적인 애플리케이션으로 확장할 수 있습니다.

 이러한 추가 기능을 구현하면서 React 훅의 더 고급 사용법과 상태 관리 기법을 학습할 수 있을 것입니다.