React - 검색 기능 구현, Loading, Error 상태 추가, 등록구현, 공통 컴포넌트, dayjs 라이브러리

김명원·2025년 1월 9일
1

learnReact

목록 보기
18/26

검색 기능 구현 & 라이브러리 버전 관련 🔍📦

이번 포스트에서는 검색 기능 구현과 관련된 내용을 다루고, json-server 라이브러리의 버전 변경에 대해 알아보았습니다. 교안을 바탕으로 코드의 주요 변경사항과 그에 대한 설명을 포함하여 오늘 배운 내용을 정리해보겠습니다.

실습 🛠️


json-server 버전 변경 🔄

json-server 라이브러리의 버전을 0.17.4로 변경하여 호환성 문제를 해결했습니다.

npm uninstall json-server
npm install json-server@0.17.4

코드 설명:

  • 기존에 설치된 json-server를 제거한 후, 특정 버전인 0.17.4로 재설치합니다.

추가 설명:
라이브러리의 버전을 명시적으로 지정함으로써 예기치 않은 버전 업데이트로 인한 호환성 문제를 예방할 수 있습니다. 이는 안정적인 개발 환경을 유지하는 데 중요한 역할을 합니다.

src/api/canvas.js 수정 📄

getCanvases 함수의 구현을 변경하여 검색 기능을 강화했습니다.

import { canvases } from './http';

**export function getCanvases(params) {
  return canvases.get('/', { params });
}**

코드 설명:

  • getCanvases 함수는 전달된 params를 사용하여 캔버스 데이터를 가져옵니다.

추가 설명:
검색 기능을 구현하기 위해 params를 통해 제목에 특정 키워드가 포함된 데이터를 필터링할 수 있도록 했습니다. 이를 통해 사용자가 원하는 캔버스를 보다 쉽게 찾을 수 있습니다.

src/pages/Home.jsx 수정 🏠

Home 컴포넌트에 검색 기능을 추가하고, 관련 상태 관리를 구현했습니다.

 import ViewToggle from '../components/ViewToggle';
 import { getCanvases } from '../api/canvas';
 function Home() {
**+  const [searchText, setSearchText] = useState();**
   const [isGridView, setIsGridView] = useState(true);
   const [data, setData] = useState([]);
 
**+  async function fetchData(params) {
+    const response = await getCanvases(params);**
     setData(response.data);
   }
 
   useEffect(() => {
**+    fetchData({ title_like: searchText });
+  }, [searchText]);**
 
   const handleDeleteItem = id => {
     setData(data.filter(item => item.id !== id));
   };
 
   return (
     <div className="container mx-auto px-4 py-16">
       <div className="mb-6 flex flex-col sm:flex-row items-center justify-between">
 ... 생략 ...
         <ViewToggle isGridView={isGridView} setIsGridView={setIsGridView} />
       </div>
       <CanvasList
**+        filteredData={data}**
         isGridView={isGridView}
         searchText={searchText}
         onDeleteItem={handleDeleteItem}
 ... 생략 ...
 )
}

코드 설명:

  • searchText 상태를 추가하여 사용자의 검색어를 관리합니다.
  • fetchData 함수를 통해 검색어에 맞는 데이터를 불러옵니다.
  • useEffect 훅을 사용하여 searchText가 변경될 때마다 데이터를 다시 가져옵니다.
  • CanvasList 컴포넌트에 filteredData를 전달하여 필터링된 데이터를 표시합니다.

추가 설명:
검색 기능을 통해 사용자가 원하는 캔버스를 빠르게 찾을 수 있게 되었습니다. 상태 관리를 통해 검색어 입력 시 자동으로 데이터가 갱신되도록 구현하여 사용자 경험을 향상시켰습니다.

참고 📚


배운 내용 요약 📝

json-server 라이브러리의 버전 변경을 통해 호환성 문제를 해결하는 방법과, 검색 기능을 구현하여 사용자 경험을 향상시키는 방법에 대해 배웠습니다. 특히, getCanvases 함수의 파라미터 활용과 Home 컴포넌트에서의 상태 관리 방식을 통해 효율적인 데이터 필터링을 구현할 수 있었습니다. 이러한 내용을 통해 프론트엔드 개발 시 라이브러리 관리와 사용자 친화적인 기능 구현의 중요성을 다시 한번 확인할 수 있었습니다.


검색 기능 구현 & 라이브러리 버전 관련 🔍📦

이번 포스트에서는 검색 기능 구현과 라이브러리 버전 변경에 대해 다루었습니다. 교안을 바탕으로 코드의 주요 변경사항과 그에 대한 설명을 포함하여 내용을 정리해보겠습니다.

실습 🛠️


json-server 버전 변경 🔄

json-server 라이브러리의 버전을 0.17.4로 변경하여 호환성 문제를 해결했습니다.

npm uninstall json-server
npm install json-server@0.17.4

코드 설명:

  • 기존에 설치된 json-server를 제거한 후, 특정 버전인 0.17.4로 재설치합니다.

추가 설명:
라이브러리의 버전을 명시적으로 지정함으로써 예기치 않은 버전 업데이트로 인한 호환성 문제를 예방할 수 있습니다. 이는 안정적인 개발 환경을 유지하는 데 중요한 역할을 합니다.

src/api/canvas.js 수정 📄

getCanvases 함수의 구현을 변경하여 검색 기능을 강화했습니다.

import { canvases } from './http';

**export function getCanvases(params) {
  return canvases.get('/', { params });
}**

코드 설명:

  • getCanvases 함수는 전달된 params를 사용하여 캔버스 데이터를 가져옵니다.

추가 설명:
검색 기능을 구현하기 위해 params를 통해 제목에 특정 키워드가 포함된 데이터를 필터링할 수 있도록 했습니다. 이를 통해 사용자가 원하는 캔버스를 보다 쉽게 찾을 수 있습니다.

src/pages/Home.jsx 수정 🏠

Home 컴포넌트에 검색 기능을 추가하고, 관련 상태 관리를 구현했습니다.

import { useEffect, useState } from 'react';
+import { getCanvases } from '../api/canvas';

import CanvasList from '../components/CanvasList';
import SearchBar from '../components/SearchBar';
import ViewToggle from '../components/ViewToggle';
+import Loading from '../components/Loading';
+import Error from '../components/Error';
function Home() {
  const [searchText, setSearchText] = useState();
  const [isGridView, setIsGridView] = useState(true);
  const [data, setData] = useState([]);
+  const [isLoading, setIsLoading] = useState(true);
+  const [error, setError] = useState(null);

  async function fetchData(params) {
+    try {
+      setIsLoading(true);
+      setError(null);
+      await new Promise(resolver => setTimeout(resolver, 2000));
+      const response = await getCanvases(params);
+      setData(response.data);
+    } catch (err) {
+      setError(err);
+    } finally {
+      setIsLoading(false);
+    }
  }

  useEffect(() => {

    // ... 생략 ...

          <SearchBar searchText={searchText} setSearchText={setSearchText} />
          <ViewToggle isGridView={isGridView} setIsGridView={setIsGridView} />
        </div>
+      {isLoading && <Loading />}
+      {error && (
+        <Error
+          message={error.message}
+          onRetry={() => fetchData({ title_like: searchText })}
+        />
+      )}
+      {!isLoading && !error && (
+        <CanvasList
+          filteredData={data}
+          isGridView={isGridView}
+          searchText={searchText}
+          onDeleteItem={handleDeleteItem}
+        />
+      )}
     </div>
   );
 }

코드 설명:

  • searchText, isLoading, error 등의 상태를 추가하여 검색어 입력, 로딩 상태, 오류 상태를 관리합니다.
  • fetchData 함수에서 데이터를 가져올 때 로딩 상태를 설정하고, 오류 발생 시 오류 상태를 업데이트합니다.
  • useEffect 훅을 사용하여 searchText가 변경될 때마다 fetchData를 호출하여 데이터를 다시 가져옵니다.
  • 로딩 중일 때는 Loading 컴포넌트를, 오류 발생 시 Error 컴포넌트를, 정상적으로 데이터를 가져왔을 때는 CanvasList 컴포넌트를 표시합니다.

추가 설명:
로딩과 오류 상태를 관리함으로써 사용자에게 현재 데이터 요청 상태를 명확히 전달할 수 있게 되었습니다. Loading 컴포넌트를 통해 데이터 로딩 중임을 시각적으로 표시하고, Error 컴포넌트를 통해 오류 발생 시 재시도할 수 있는 기능을 제공합니다. 이는 사용자 경험을 향상시키는 중요한 개선 사항입니다.

Loading, Error UI 구현 🎨

LoadingError UI 컴포넌트를 추가하여 사용자에게 데이터 요청 상태를 명확히 전달합니다.

src/components/Loading.jsx

import { AiOutlineLoading3Quarters } from 'react-icons/ai';

function Loading() {
  return (
    <div className="flex items-center justify-center ">
      <div className="text-center">
        <AiOutlineLoading3Quarters className="animate-spin text-6xl text-blue-500 mx-auto mb-4" />
        <p className="text-xl font-semibold text-gray-700">Loading data...</p>
      </div>
    </div>
  );
}

export default Loading;

코드 설명:

  • AiOutlineLoading3Quarters 아이콘을 사용하여 로딩 스피너를 표시합니다.
  • 중앙 정렬된 텍스트로 "Loading data..." 메시지를 제공합니다.

src/components/Error.jsx

function Error({ message, onRetry }) {
  return (
    <div className="flex items-center justify-center">
      <div className="text-center p-8">
        <p className="text-xl font-semibold text-red-600 mb-4">{message}</p>
        <button
          onClick={onRetry}
          className="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600 transition duration-300 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-opacity-50"
        >
          재시도
        </button>
      </div>
    </div>
  );
}

export default Error;

코드 설명:

  • 오류 메시지를 표시하는 텍스트와 재시도 버튼을 제공합니다.
  • 버튼 클릭 시 onRetry 함수를 호출하여 데이터를 다시 요청할 수 있습니다.

SearchBar 기본값 할당 (오류 제거) 🛠️

SearchBar 컴포넌트에서 searchText의 기본값을 할당하여 초기 렌더링 시 발생할 수 있는 오류를 방지했습니다.

import { FaSearch } from 'react-icons/fa';
+function SearchBar({ searchText = '', setSearchText }) {
   return (
     <div className="relative w-full sm:w-64 mb-4 sm:mb-0">
       <input
  // ...생략...

코드 설명:

  • searchText의 기본값을 빈 문자열로 설정하여, 값이 undefined일 경우에도 정상적으로 동작하도록 합니다.

Home Fragment 사용 🧩

Home 컴포넌트에서 React Fragment를 사용하여 불필요한 DOM 요소 생성을 방지했습니다.

function Home() {
  // ...생략...

  return (
+    <>
      <div className="mb-6 flex flex-col sm:flex-row items-center justify-between">
        <SearchBar searchText={searchText} setSearchText={setSearchText} />
        <ViewToggle isGridView={isGridView} setIsGridView={setIsGridView} />
      </div>
+      {isLoading && <Loading />}
+      {error && (
+        <Error
+          message={error.message}
+          onRetry={() => fetchData({ title_like: searchText })}
+        />
+      )}
+      {!isLoading && !error && (
+        <CanvasList
+          filteredData={data}
+          isGridView={isGridView}
+          searchText={searchText}
+          onDeleteItem={handleDeleteItem}
+        />
+      )}
    </>
  );
}

코드 설명:

  • React Fragment (<> </>)를 사용하여 여러 요소를 그룹화하면서 불필요한 래퍼 DOM 요소 생성을 방지합니다.

참고 📚


배운 내용 요약 📝

이번 실습을 통해 로딩 및 오류 상태 관리를 추가하여 사용자 경험을 향상시키는 방법을 배웠습니다. Loading 컴포넌트를 통해 데이터 요청 중임을 시각적으로 표시하고, Error 컴포넌트를 통해 오류 발생 시 재시도 기능을 제공함으로써 보다 안정적인 애플리케이션을 구현할 수 있게 되었습니다. 또한, SearchBar의 기본값 할당과 React Fragment 사용을 통해 코드의 안정성과 효율성을 높일 수 있었습니다.


등록구현, 공통 컴포넌트, dayjs 라이브러리 📚

강의를 들으며 등록구현, 공통 컴포넌트, 그리고 dayjs 라이브러리에 대해 배웠습니다. 이번 포스트에서는 교안을 바탕으로 배운 내용을 정리하고, 코드의 주요 변경사항과 그에 대한 설명을 포함하여 오늘 배운 내용을 되새겨보겠습니다.

실습 ✨


등록, 조회 🔍

src/api/canvas.js 파일에서 캔버스 데이터를 등록하고 조회하는 기능을 구현했습니다. 주요 변경사항은 uuiddayjs 라이브러리를 도입하여 새로운 캔버스를 생성할 때 고유한 ID와 현재 시간을 추가하는 것입니다.

import { canvases } from './http';
**+import { v4 as uuidv4 } from 'uuid';
+import dayjs from 'dayjs';**
 
export function getCanvases(params) {
**+  const payload = Object.assign(
+    {
+      _sort: 'lastModified',
+      _order: 'desc',
+    },
+    params,
+  );
+  return canvases.get('/', { params: payload });
+}
+
+export function createCanvas() {
+  const newCanvas = {
+    title: uuidv4().substring(0, 4) + '_새로운 린 캔버스',
+    lastModified: dayjs().format('YYYY-MM-DD HH:mm:ss'),
+    category: '신규',
+  };
+  return canvases.post('/', newCanvas);**
 }

코드 설명:

  • uuidv4를 사용하여 새로운 캔버스의 고유한 ID를 생성하고, 이를 제목에 일부 포함시켰습니다.
  • dayjs를 활용하여 현재 시간을 lastModified 필드에 포맷팅하여 저장합니다.
  • getCanvases 함수에서는 요청 시 정렬 옵션을 추가하여 최근에 수정된 캔버스가 먼저 조회되도록 했습니다.

추가 설명:
이러한 변경을 통해 캔버스의 생성과 조회 시 더 체계적인 데이터 관리가 가능해졌습니다. 특히, dayjs를 사용함으로써 시간 관련 처리가 간편해졌습니다.

버튼 공통 컴포넌트 🔘

공통으로 사용할 버튼 컴포넌트를 src/components/Button.jsx에 구현했습니다. 이 컴포넌트는 로딩 상태를 관리하며, onClick 이벤트 핸들링 시 유효성 체크를 추가하여 에러를 방지합니다.

import { FaSpinner } from 'react-icons/fa';
function Button({ loading = false, onClick, className, children }) {
  const clazz = [
    'bg-blue-500 hover:bg-blue-600 text-white font-bold py-1.5 px-4 rounded transition-colors duration-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50',
    className,
  ].join(' ');

  const handleClick = () => {
  	**// onClick이 undefined 일 경우 아래 로직을 수행하지 않으면 에러를 방지할 수 있어요!**
  	if (!onClick) {
  	  return;
  	}
    if (loading) {
      return;
    }
    onClick();
  };
  return (
    <button className={clazz} onClick={handleClick} disabled={loading}>
      <span className="flex items-center justify-center">
        {loading && <FaSpinner className="animate-spin mr-2" />}
        {children}
      </span>
    </button>
  );
}

export default Button;

코드 설명:

  • 버튼 클릭 시 onClick이 정의되지 않은 경우를 체크하여 에러를 방지합니다.
  • 로딩 상태일 때는 버튼 클릭을 비활성화하고, 스피너 아이콘을 표시합니다.

추가 설명:
이 공통 버튼 컴포넌트를 사용하면 여러 곳에서 일관된 스타일과 기능을 유지할 수 있으며, 로딩 상태 관리가 용이해졌습니다.

Home 컴포넌트 적용 🏠

src/pages/Home.jsx 파일에서 공통 버튼 컴포넌트를 활용하여 캔버스 등록 기능을 추가했습니다. 또한, createCanvasgetCanvases 함수를 불러와 데이터를 관리합니다.

import { useEffect, useState } from 'react';
+import { createCanvas, getCanvases } from '../api/canvas';
 
import CanvasList from '../components/CanvasList';
import SearchBar from '../components/SearchBar';
import ViewToggle from '../components/ViewToggle';
import Loading from '../components/Loading';
import Error from '../components/Error';
+import Button from '../components/Button';

function Home() {
  const [searchText, setSearchText] = useState();
  const [isGridView, setIsGridView] = useState(true);
 // ...생략...
    try {
      setIsLoading(true);
      setError(null);
+      await new Promise(resolver => setTimeout(resolver, 1000));
      const response = await getCanvases(params);
      setData(response.data);
    } catch (err) {
 // ...생략...
     setData(data.filter(item => item.id !== id));
   };

   const [isLoadingCreate, setIsLoadingCreate] = useState(false);
   const handleCreateCanvas = async () => {
     try {
       setIsLoadingCreate(true);
       await new Promise(resolver => setTimeout(resolver, 1000));
       await createCanvas();
       fetchData({ title_like: searchText });
     } catch (err) {
       alert(err.message);
     } finally {
       setIsLoadingCreate(false);
     }
   };

   return (
     <>
       <div className="mb-6 flex flex-col sm:flex-row items-center justify-between">
         <SearchBar searchText={searchText} setSearchText={setSearchText} />
         <ViewToggle isGridView={isGridView} setIsGridView={setIsGridView} />
       </div>
       <div className="flex justify-end mb-6">
         <Button onClick={handleCreateCanvas} loading={isLoadingCreate}>
           등록하기
         </Button>
       </div>
       {isLoading && <Loading />}
       {error && (
         <Error
 // ...생략...
 )
}

코드 설명:

  • createCanvasgetCanvases 함수를 불러와 데이터를 관리합니다.
  • Button 컴포넌트를 사용하여 캔버스 등록 버튼을 구현했습니다. 버튼 클릭 시 handleCreateCanvas 함수가 호출되며, 로딩 상태를 관리합니다.
  • 데이터 조회 시 1초의 지연을 추가하여 로딩 상태를 테스트할 수 있습니다.

추가 설명:
공통 버튼 컴포넌트를 활용함으로써 버튼의 일관된 스타일과 로딩 상태 관리가 가능해졌습니다. 또한, 데이터 관리 로직이 더욱 명확해졌습니다.

배운 내용 요약 📝

dayjs 라이브러리를 활용하여 날짜와 시간을 효과적으로 관리하는 방법과, 공통 컴포넌트를 만들어 코드의 재사용성을 높이는 방법에 대해 배웠습니다. 또한, 캔버스 등록 및 조회 기능을 구현하며 프론트엔드 개발의 기본적인 데이터 관리 흐름을 이해할 수 있었습니다. 이러한 내용을 통해 더욱 효율적이고 유지보수가 쉬운 코드를 작성할 수 있게 되었습니다.

profile
개발자가 되고 싶은 정치학도생의 기술 블로그

0개의 댓글