Skip to content

Directory Structure

BangDori edited this page May 18, 2024 · 9 revisions

전체 디렉터리 구조


📦src
 ┣ 📂app/
    ┣ 📂providers/
    ┣ 📂routers/
    ┗ 📜index.tsx
 ┣ 📂pages/
 ┣ 📂widgets/
 ┣ 📂features/
 ┣ 📂entities/
 ┗ 📂shared/
    ┣ 📂axios/
    ┣ 📂styles/
    ┣ 📂ui/
    ┗ 📂util/

📂app

애플리케이션 로직이 초기화되는 곳입니다. 프로바이더, 라우터, 전역 스타일, 전역 타입 선언 등이 여기에서 정의됩니다. 애플리케이션의 진입점 역할을 합니다.

  • providers: RouterProvider, QueryClientProvider 와 같은 프로바이더들이 위치하는 디렉터리입니다.
// app/providers/query-client.ts

import {
  MutationCache,
  QueryCache,
  QueryClient,
  QueryClientConfig,
} from "@tanstack/react-query";

export const queryClientOptions: QueryClientConfig = {
  defaultOptions: {},
  queryCache: new QueryCache({}),
  mutationCache: new MutationCache({}),
};

export const queryClient = new QueryClient(queryClientOptions);
  • routers: React Router에 의해 렌더링되는 페이지의 라우터가 정의됩니다.
// app/routers/index.tsx

import { createBrowserRouter, RouteObject } from "react-router-dom";

import RootLayout from "@pages/RootLayout";
import ErrorLayout from "@pages/ErrorLayout";

const routes: RouteObject[] = [
  {
    path: "/",
    element: <RootLayout />,
    errorElement: <ErrorLayout />,
  },
];

const router = createBrowserRouter(routes);
export default router;
  • index.tsx: 애플리케이션의 진입점 역할을 합니다.

📂pages

페이지에 해당하는 컴포넌트가 여기에서 정의됩니다. 페이지 진입시 데이터를 가져오는 커스텀 훅도 여기에서 정의합니다.

// pages/feeds/FeedsPage.tsx

import { useGetFeeds } from "./useFeeds";

const FeedsPage = () => {
  const { feeds } = useFeeds();

  return <Feeds feeds={feeds} />;
};

export default FeedsPage;
// pages/feeds/useFeeds.tsx

async function getFeeds(): Promise<Feed[]> {
  const { data } = await axiosInstance.get("/feeds");

  return data;
}

export function useGetFeeds(): Feed[] {
  const fallback: Feed[] = [];

  const { data = fallback } = useQuery({
    queryKey: [queryKeys.feeds],
    queryFn: getFeeds,
  });

  return data;
}

export function usePrefetchFeeds(): void {
  const queryClient = useQueryClient();

  queryClient.prefetchQuery({
    queryKey: [queryKeys.feeds],
    queryFn: getFeeds,
  });
}

📂widgets

하위의 레이어들을 이용해 페이지에 사용되는 독립적인 UI 컴포넌트를 만드는 단계입니다.

feeds/

  • FeedMainHeader.tsx

  • FeedMain.tsx

...

📂features

비즈니스 로직을 포함하는 View들이 이 단계에서 정의됩니다.

feeds/

  • hooks/useLike.tsx

  • Like.tsx

  • FeedItem.tsx

...

📂entities

비즈니스 엔티티를 정의합니다. 사용자, 리뷰 등이 포함됩니다.

  • users/
  • feeds/

📂shared

재사용 가능한 컴포넌트와 유틸리티 함수, axios 설정 등이 여기에서 정의됩니다.

  • axios: axios 설정이 포함된 디렉터리입니다.
// shared/axios/config.ts

import axios from "axios";

const SERVER_URL = process.env.REACT_APP_SERVER_URL;

export const axiosInstance = axios.create({
  baseURL: '',
  timeout: 20000,
  headers: {
    "Content-Type": "application/json",
    accept: "application/json",
  },
});

axiosInstance.interceptors.request.use(onRequest);
axiosInstance.interceptors.response.use(onResponse, onError);
  • styles: scss의 전역 변수와 초기 설정이 위치하는 디렉터리입니다.
// shared/styles/font.scss

$font-large: 3rem;
$font-medium: 1.6rem;
$font-default: 1.2rem;
$font-small: 0.8rem;

// shared/styles/color.scss

$color-white: #ffffff;
$color-black: #000000;

$color-gray-400: #bdbdbd;
$color-gray-500: #9e9e9e;
$color-gray-700: #616161;

$color-red-400: #ef5350;
$color-red-500: #f44336;
$color-red-700: #d32f2f;

$color-blue-400: #42a5f5;
$color-blue-500: #2196f3;
$color-blue-700: #1976d2;
  • ui: 자주 사용될 수 있는, View들이 위치하는 디렉터리입니다.
// shared/ui/Button.tsx

const Button = ({ onClick, color, data }) => {
  return (
    <button className="button-style" onClick={onClick}>
      {data}
    </button>
  );
};
  • util: 특정 비즈니스 로직에 종속되지 않은 유틸리티 함수가 포함된 디렉터리입니다.
// shared/util/key-factories.ts

export const generateUserKey = (userId: number) => {
  return [queryKeys.user, userId];
};

export const generateUserAppointmentsKey = (
  userId: number,
  userToken: string
) => {
  return [queryKeys.appointments, queryKeys.user, userId, userToken];
};