Skip to content

Directory Structure

BangDori edited this page Mar 9, 2024 · 9 revisions

์ „์ฒด ๋””๋ ‰ํ„ฐ๋ฆฌ ๊ตฌ์กฐ

๐Ÿ“ฆsrc
 โ”ฃ ๐Ÿ“‚app
    โ”ฃ ๐Ÿ“‚providers/
    โ”ฃ ๐Ÿ“‚routers/
    โ”— ๐Ÿ“œindex.tsx
 โ”ฃ ๐Ÿ“‚components
 โ”ฃ ๐Ÿ“‚pages
 โ”— ๐Ÿ“‚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: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ง„์ž…์  ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“‚components

View์— ํ•ด๋‹นํ•˜๋Š” ๋ถ€๋ถ„์ด ์—ฌ๊ธฐ์—์„œ ์ •์˜๋ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž์˜ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ๋˜ํ•œ ์—ฌ๊ธฐ์„œ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

feeds/

  • hooks/useLike.tsx

  • Feeds.tsx

  • FeedItem.tsx

  • Feeds.scss

...

๐Ÿ“‚pages

ํŽ˜์ด์ง€์— ํ•ด๋‹นํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์—ฌ๊ธฐ์—์„œ ์ •์˜๋ฉ๋‹ˆ๋‹ค. ํŽ˜์ด์ง€ ์ง„์ž…์‹œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์ปค์Šคํ…€ ํ›…๋„ ์—ฌ๊ธฐ์—์„œ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

  • FSD ์•„ํ‚คํ…์ฒ˜๋ฅผ ์‚ฌ์šฉํ•จ์— ์žˆ์–ด, pages ์—์„œ๋Š” components๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์—†์–ด์•ผํ•˜๋Š”๋ฐ entity, feature ๋ชจ๋‘ ๋‚˜๋ˆ„๊ณ  ํ•˜๊ธฐ์—๋Š” ์•„์ง ํž˜๋“ค ๊ฒƒ ๊ฐ™์•„์„œ ์ด ๋ถ€๋ถ„์— ๋Œ€ํ•ด์„œ๋Š” ๊ทœ์น™์„ ์œ„๋ฐฐํ•ด์•ผํ•  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค!
// 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,
  });
}

๐Ÿ“‚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: SERVER_URL,
  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];
};