본문 바로가기

개발 공부/React

Next.js 에서 모달 띄우기와 서버작업을 통한 유저 정보 받기 및 등록 (1)

코드 출처 : https://github.com/Cluster-Taek/next14-boilerplate

 

GitHub - Cluster-Taek/next14-boilerplate

Contribute to Cluster-Taek/next14-boilerplate development by creating an account on GitHub.

github.com

 

users/page.tsx

'use client';

import Button from '@/components/common/button';
import UserCreateFormModal from '@/components/user/user-create-form-modal';
import { MODAL } from '@/constants/modal-key-constants';
import useModals from '@/hooks/use-modals';
import { useUsers } from '@/lib/user';
import { sva } from '@/styled-system/css';
import { Box } from '@/styled-system/jsx';

const Page = () => {
  const pageStyle = PageSva();
  const { openModal } = useModals();

  const { data: users } = useUsers({
    _page: 1,
    _per_page: 10,
  });

  const handleClickCreateButton = () => {
    openModal({
      id: MODAL.USER_CREATE,
      component: <UserCreateFormModal />,
    });
  };

  return (
    <Box className={pageStyle.wrapper}>
      <Box className={pageStyle.list}>{users?.data?.map((user) => <Box key={user.id}>{user.name}</Box>)}</Box>
      <Button onClick={handleClickCreateButton}>Create</Button>
    </Box>
  );
};

export default Page;

const PageSva = sva({
  slots: ['wrapper', 'list', 'form', 'input', 'error'],
  base: {
    wrapper: {
      display: 'flex',
      width: 'full',
      flexDirection: 'column',
      alignItems: 'center',
      justifyContent: 'center',
      height: 'full',
    },
    list: {
      width: 'full',
      display: 'flex',
      flexWrap: 'wrap',
      gap: '4',
    },
    form: {
      width: 'full',
      marginTop: '4',
    },
    input: {
      width: 'full',
      padding: '2',
      marginTop: '2',
    },
    error: {
      color: 'red',
      marginTop: '2',
    },
  },
});

 

사용자 목록을 표시하고 새로운 사용자를 추가하기 위한 모달을 열 수 있는 React 클라이언트 구성 요소입니다.

 

모듈 구성

이 코드의 주요 목적은 사용자 목록을 렌더링하고 새 사용자 추가를 위한 모달을 열 수 있는 기능을 제공합니다.

주요 흐름

  1. useUsers 훅을 통해 데이터를 가져옵니다.
  2. openModal 훅을 사용해 모달을 엽니다.
  3. PageSva로 스타일링을 관리하며, Box 컴포넌트를 통해 레이아웃을 구성합니다.

'use client'

이 문구는 Next.js에서 사용되며, 이 파일이 클라이언트에서 렌더링되는 React 컴포넌트임을 나타냅니다.

즉, 서버에서 실행되지 않고 브라우저에서 실행됩니다.

import 문

여러 컴포넌트, 훅, 스타일링 함수 등을 가져옵니다:

  • Button: 공통 버튼 컴포넌트.
  • UserCreateFormModal: 사용자를 생성하는 폼을 포함한 모달 컴포넌트.
  • MODAL: 모달 키를 상수로 정의한 파일.
  • useModals: 모달을 열고 닫는 커스텀 훅.
  • useUsers: 사용자 데이터를 가져오는 커스텀 훅.
  • sva와 Box: 스타일링 및 레이아웃 구성을 위한 함수 및 컴포넌트.

Page 컴포넌트

주요 구성 요소:

  1. pageStyle
    • PageSva를 호출하여 스타일 객체를 생성합니다.
    • 이 객체는 페이지에 필요한 스타일링 정보를 담고 있습니다.
  2. useUsers
    • _page와 _per_page를 쿼리 파라미터로 사용하여 사용자 데이터를 가져옵니다.
    • users?.data는 사용자 데이터를 배열로 반환합니다.
  3. handleClickCreateButton
    • 모달을 열기 위한 함수입니다.
    • openModal 함수에 id와 component를 전달하여 특정 모달을 엽니다.
  4. JSX 반환
    • Box 컴포넌트를 사용하여 스타일링된 페이지 레이아웃을 렌더링합니다.
    • users?.data를 반복하여 사용자 이름을 리스트로 표시합니다.
    • Button 컴포넌트를 사용하여 "Create" 버튼을 렌더링하고, 클릭 시 handleClickCreateButton을 호출합니다.

PageSva 함수

  • slots 스타일 슬롯 이름 배열. 각 슬롯은 CSS 스타일 객체를 나타냅니다.
  • base 각 슬롯에 대한 기본 스타일 정의:
    • wrapper: 페이지의 최상위 컨테이너 스타일. 중앙 정렬과 전체 화면 높이를 사용합니다.
    • list: 사용자 리스트를 래핑하는 컨테이너. 가로와 세로 모두 유연하게 배치되며, 요소 간의 간격(gap)이 정의됩니다.
    • form, input, error: 사용자 입력 폼 및 오류 메시지와 관련된 스타일.

 

 

user.ts

import { fetchApi } from './base';
import { IPageable } from '@/types/pageable';
import { IUser } from '@/types/user';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';

export interface IUsersParams {
  // paging params of json-server
  _page: number;
  _per_page: number;
}


export const useUsers = (params: IUsersParams) => {
  return useQuery<IPageable<IUser>>({
    queryKey: [`/api/users`, params],
  });
};

 

useUsers 훅은 React Query를 사용하여 사용자 데이터를 가져오는 커스텀 훅입니다.

useUsers는 특정한 파라미터를 기반으로 사용자 데이터를 서버에서 가져오고, 이를 React 컴포넌트에서 쉽게 사용할 수 있도록 데이터 상태를 관리합니다.

  • useQuery: React Query에서 제공하는 함수로, 데이터를 가져오는 요청을 관리하며 로딩 상태, 오류, 캐싱 등을 처리합니다.
  • IPageable<IUser>: 사용자 데이터의 페이징 정보를 포함하는 타입 정의입니다.

 

(params: IUsersParams)

  • params: JSON Server에서 페이징 요청을 처리하기 위한 파라미터 객체입니다.
    • _page: 가져올 페이지 번호.
    • _per_page: 페이지당 항목 수.

useQuery를 호출한 결과를 반환하며, 다음 상태와 데이터를 제공합니다:

  • data: 서버에서 가져온 사용자 데이터.
  • isLoading: 데이터가 로딩 중인지 나타내는 불리언 값.
  • isError: 요청이 실패했는지 나타내는 불리언 값.
  • refetch: 데이터를 다시 가져오는 함수.

 

내부 동작

return useQuery<IPageable<IUser>>({
  queryKey: [`/api/users`, params],
});

 

  • queryKey: 캐싱 및 데이터를 식별하기 위한 고유 키입니다.
    • URL과 파라미터(params)를 조합해 지정합니다.
    • 동일한 키를 사용하면 캐싱된 데이터를 재사용합니다.
  • IPageable<IUser>: 데이터의 타입을 명시적으로 지정해 TypeScript가 반환 데이터를 올바르게 추론할 수 있도록 합니다.

 

 

useUsers의 사용 부분 from users/page.tsx

const { data: users } = useUsers({
  _page: 1,
  _per_page: 10,
});

 

  • _page: 1: 첫 번째 페이지 데이터를 요청합니다.
  • _per_page: 10: 한 페이지당 10명의 사용자를 요청합니다.

결과

useUsers 훅은 React Query의 useQuery를 활용하여 사용자 데이터를 가져오고, 이를 data라는 이름으로 반환합니다.

  • 반환된 data를 users라는 이름으로 구조 분해 할당하였습니다.

데이터를 사용하는 부분

users 데이터는 아래와 같이 JSX에서 사용됩니다

<Box className={pageStyle.list}>
  {users?.data?.map((user) => (
    <Box key={user.id}>{user.name}</Box>
  ))}
</Box>

 

 

  • users?.data
    • users 객체의 data 속성은 사용자 목록 배열입니다.
    • 이 배열은 IPageable<IUser> 타입으로 정의되었으며, 각 요소는 IUser 타입(사용자 정보 객체)입니다.
  • map을 이용한 반복 렌더링
    • users?.data 배열을 순회하며 각 사용자 데이터를 TSX로 렌더링합니다.
    • user.id는 각 사용자 객체의 고유 ID이며, React에서 리스트를 렌더링할 때 key로 사용됩니다.
    • user.name은 사용자 이름을 표시합니다.
  • 안전한 옵셔널 체이닝
    • 데이터가 아직 로딩되지 않았거나 users가 null인 경우를 대비해 users?.data로 접근합니다.
    • 이로 인해 런타임 에러를 방지합니다.

 

사용 흐름

  1. 데이터 요청: useUsers 훅을 호출하여 1페이지의 10명의 사용자 데이터를 요청합니다.
  2. 데이터 수신 및 캐싱: React Query가 데이터를 서버에서 가져와 캐싱합니다.
  3. 사용자 이름 렌더링: users?.data 배열을 순회하며 사용자 이름을 화면에 표시합니다.