👩‍💻
Megaptera Frontend
  • 주차별 학습
    • megaptera-front
    • 1. 개발 환경
      • 1. 개발 환경
      • 2. TypeScript
      • 3. React
      • 4. Testing Library
      • 5. Parcel & ESLint
      • 한 주를 마치며
    • 2. JSX
      • 1. JSX
      • 한 주를 마치며
    • 3. React로 사고하기
      • 1. React Component
      • 2. React State
      • 한 주를 마치며
    • 4. React Hooks
      • 1. Express
      • 2. Fetch API & CORS
      • 3. React의 Hook
      • 4. useRef & Custom Hook
      • 5. usehooks-ts
      • 한 주를 마치며
    • 5. 테스트
      • 1. TDD
      • 2. React Testing Library
      • 3. MSW
      • 4. Playwright
      • 한 주를 마치며
    • 6. External Store
      • 1. External Store
      • 2. TSyringe
      • 3. Redux 따라하기
      • 4. usestore-ts
      • 한 주를 마치며
    • 7. React Router
      • 1. Routing
      • 2. Routes
      • 3. Router
      • 4. Navigation
      • 한 주를 마치며
    • 8. CSS in JS
      • 1. Design System
      • 2. Style Basics
      • 3. CSS in JS
      • 4. styled-components
      • 5. props와 attrs
      • 6. Global Style & Theme
      • 한 주를 마치며
    • 9. 쇼핑몰 목록, 상품 페이지
      • 1. 개발하기 전 준비
      • 2. 목록 보기
      • 3. 상품 상세 보기
      • 4. 장바구니 보기
      • 5. 장바구니에 상품 담기
      • 한 주를 마치며
    • 10. 사용자 인증, 인가
      • 1. 로그인
      • 2. 로그아웃
      • 3. 회원가입
      • 4. 주문 목록 & 주문 상세
      • 한 주를 마치며
    • 11. 주문, 결제
      • 1. 배송 정보 입력
      • 2. 포트원 결제 요청
      • 3. 배송 및 결제 정보 전달
      • 한 주를 마치며
    • 12. 어드민
      • 1. 관리자 웹 사이트 개발 시작
      • 2. 로그인, 사용자 목록
      • 3. 카테고리 관리
      • 4. 주문 관리
      • 5. 상품 관리
      • 한 주를 마치며
Powered by GitBook
On this page
  • 1. 카테고리 목록
  • CategoryListPage 생성
  • useFetchCategories hook 생성
  • 2. 카테고리 추가
  • React Hook Form
  • CategoryNewPage 구현
  • 3. 카테고리 수정
  • useFetchCategory hook 생성
  1. 주차별 학습
  2. 12. 어드민

3. 카테고리 관리

1. 카테고리 목록

🎯 데이터를 변경하는 사례 만들어 보기

카테고리는 이름과 표시 여부만 관리하면 돼서 매우 간단

CategoryListPage 생성

  • 기존과 매우 유사

  • 추가, 변경 등의 링크가 추가됨

export default function CategoryListPage() {
  const { categories, loading, error } = useFetchCategories();

  // loading, error 처리 

  return (
    <Container>
      <h2>Categories</h2>
      <table>
        <thead>
          <tr>
            <th>이름</th>
            <th>표시</th>
            <th>행동</th>
          </tr>
        </thead>
        <tbody>
          {categories.map((category) => (
            <tr key={category.id}>
              <td>{category.name}</td>
              <td>{category.hidden ? '감춤' : '보임'}</td>
              <td>
                <Link to={`/categories/${category.id}/edit`}>수정</Link>
              </td>
            </tr>
          ))}
        </tbody>
      </table>
      <Link to="/categories/new">
        카테고리 추가
      </Link>
    </Container>
  );
}

useFetchCategories hook 생성

createCategory 함수를 제공하게 하는데, 여기서 SWR의 캐시 초기화(사실상 refetch 요청)를 위해 mutate 함수를 호출

import useFetch from './useFetch';

import { apiService } from '../services/ApiService';

import { Category } from '../types';

export default function useFetchCategories() {
  const {
    data, error, loading, mutate,
  } = useFetch<{
    categories: Category[];
  }>('/categories');

  return {
    categories: data?.categories ?? [],
    error,
    loading,
    async createCategory({ name }: {
      name: string;
    }) {
      await apiService.createCategory({ name });
      mutate();
    },
  };
}

2. 카테고리 추가

React Hook Form

Performant, flexible and extensible forms with easy-to-use validation 사용하기 쉬운 유효성 검사와 함께 성능이 뛰어나고 유연하며 확장 가능한 폼

💡 React Hook Form의 useForm 훅과 Controller 컴포넌트를 잘 활용하면 됨 Controller 쓰는 법이 약간 복잡하지만, 예전에 했던 것처럼 TextBox 등으로 추출(extract)하면 깔끔하게 사용할 수 있음

CategoryNewPage 구현

카테고리 추가하기

export default function CategoryNewPage() {
  const navigate = useNavigate();

  const { createCategory } = useFetchCategories();

  type FormValues = {
    name: string;
  };

  const { handleSubmit, control } = useForm<FormValues>();

  const onSubmit = async (data: FormValues) => {
    await createCategory({
      name: data.name,
    });
    navigate('/categories');
  };

  return (
    <Container>
      <h2>New Category</h2>
      <form onSubmit={handleSubmit(onSubmit)}>
        <Controller
          control={control}
          name="name"
          defaultValue=""
          render={({ field: { onChange, value } }) => (
            <div>
              <label htmlFor="input-name">이름</label>
              <input
                id="input-name"
                value={value}
                onChange={onChange}
              />
            </div>
          )}
        />
        <Button type="submit">
          등록
        </Button>
      </form>
    </Container>
  );
}

3. 카테고리 수정

기존 카테고리를 수정할 때는 기존 데이터를 활용해야 하기 때문에 처리할 것이 조금 많음

  • 여기서는 카테고리 표시 여부를 수정할 때만 조작 가능하도록 구현

import { useNavigate, useParams } from 'react-router-dom';

import { Controller, useForm } from 'react-hook-form';

import styled from 'styled-components';

import Button from '../components/ui/Button';

import useFetchCategory from '../hooks/useFetchCategory';

const Container = styled.div`
  h2 {
    margin-bottom: 2rem;
    font-size: 2rem;
  }

  [type=submit] {
    display: block;
    margin-block: 1rem;
  }
`;

export default function CategoryEditPage() {
  const params = useParams();

  const categoryId = String(params.id);

  const navigate = useNavigate();

  const {
    category, loading, error, updateCategory,
  } = useFetchCategory({ categoryId });

  type FormValues = {
    name: string;
    hidden: boolean;
  };

  const { handleSubmit, control } = useForm<FormValues>();

  const onSubmit = async (data: FormValues) => {
    await updateCategory({
      name: data.name,
      hidden: data.hidden,
    });
    navigate('/categories');
  };

  if (loading) {
    return (
      <p>Loading...</p>
    );
  }

  if (error || !category) {
    return (
      <p>Error!</p>
    );
  }

  return (
    <Container>
      <h2>Edit Category</h2>
      <form onSubmit={handleSubmit(onSubmit)}>
        <Controller
          control={control}
          name="name"
          defaultValue={category.name}
          render={({ field: { onChange, value } }) => (
            <div>
              <label htmlFor="input-name">이름</label>
              <input
                id="input-name"
                value={value}
                onChange={onChange}
              />
            </div>
          )}
        />
        <Controller
          control={control}
          name="hidden"
          defaultValue={category.hidden}
          render={({ field: { onChange, value } }) => (
            <div>
              <label htmlFor="input-hidden">감춤</label>
              <input
                id="input-hidden"
                type="checkbox"
                checked={value}
                onChange={onChange}
              />
            </div>
          )}
        />
        <Button type="submit">
          수정
        </Button>
      </form>
    </Container>
  );
}

useFetchCategory hook 생성

import useFetch from './useFetch';

import { apiService } from '../services/ApiService';

import { Category } from '../types';

export default function useFetchCategory({ categoryId }: {
  categoryId: string;
}) {
  const url = `/categories/${categoryId}`;

  const {
    data, error, loading, mutate,
  } = useFetch<Category>(url);

  return {
    category: data,
    error,
    loading,
    async updateCategory({ name, hidden }: {
      name: string;
      hidden: boolean;
    }) {
      await apiService.updateCategory({ categoryId, name, hidden });
      mutate();
    },
  };
}
Previous2. 로그인, 사용자 목록Next4. 주문 관리

Last updated 2 years ago

SWR - Bounce Mutate
useForm
Controller