-
Notifications
You must be signed in to change notification settings - Fork 1
Directory Structure
BangDori edited this page Mar 9, 2024
·
9 revisions
๐ฆsrc
โฃ ๐app
โฃ ๐providers/
โฃ ๐routers/
โ ๐index.tsx
โฃ ๐components
โฃ ๐pages
โ ๐shared
โฃ ๐axios/
โฃ ๐styles/
โฃ ๐ui/
โ ๐util/
์ ํ๋ฆฌ์ผ์ด์ ๋ก์ง์ด ์ด๊ธฐํ๋๋ ๊ณณ์ ๋๋ค. ํ๋ก๋ฐ์ด๋, ๋ผ์ฐํฐ, ์ ์ญ ์คํ์ผ, ์ ์ญ ํ์ ์ ์ธ ๋ฑ์ด ์ฌ๊ธฐ์์ ์ ์๋ฉ๋๋ค. ์ ํ๋ฆฌ์ผ์ด์ ์ ์ง์ ์ ์ญํ ์ ํฉ๋๋ค.
-
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: ์ ํ๋ฆฌ์ผ์ด์ ์ ์ง์ ์ ์ญํ ์ ํฉ๋๋ค.
View์ ํด๋นํ๋ ๋ถ๋ถ์ด ์ฌ๊ธฐ์์ ์ ์๋ฉ๋๋ค. ์ฌ์ฉ์์ ์ธํฐ๋ํฐ๋ธ ๋ํ ์ฌ๊ธฐ์ ์ ์ํฉ๋๋ค.
-
hooks/useLike.tsx
-
Feeds.tsx
-
FeedItem.tsx
-
Feeds.scss
...
ํ์ด์ง์ ํด๋นํ๋ ์ปดํฌ๋ํธ๊ฐ ์ฌ๊ธฐ์์ ์ ์๋ฉ๋๋ค. ํ์ด์ง ์ง์ ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ์ปค์คํ ํ ๋ ์ฌ๊ธฐ์์ ์ ์ํฉ๋๋ค.
- 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,
});
}
์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์ปดํฌ๋ํธ์ ์ ํธ๋ฆฌํฐ ํจ์, 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];
};