-
Notifications
You must be signed in to change notification settings - Fork 2
프로젝트 초기 설정
Sangmin Lee edited this page Jan 1, 2023
·
3 revisions
$ yarn create vite "프로젝트 이름" --template react-ts
$ cd "프로젝트 이름" && yarn
$ yarn add -D vite-tsconfig-paths
- vite.config.ts
import tsconfigPaths from 'vite-tsconfig-paths';
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), tsconfigPaths()],
server: {
port: 3000,
host: true,
},
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
});
- tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
},
...
},
...
}
$ yarn add react-router-dom
- src/pages/_layout.tsx
import { Suspense } from "react";
import { Outlet } from "react-router-dom";
export default function Layout() {
return (
<Suspense fallback={"...loading"}>
<Outlet />
</Suspense>
);
}
- src/Routes.tsx
import { lazy } from 'react';
import GlobalLayout from '@/pages/_layout';
const MainPage = lazy(() => import('@/pages/Main'));
export const routes = [
{
path: '/',
element: <GlobalLayout />,
children: [
{ path: '/', element: <MainPage /> },
],
},
];
export const pages = [{ route: '/' }];
- src/main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>,
);
- src/App.tsx
import "@/reset.scss";
import { useRoutes } from "react-router-dom";
import { routes } from "@/Routes";
export default function App() {
const elem = useRoutes(routes);
return (
<div>
{elem}
</div>
);
}
$ yarn add @tanstack/react-query @tanstack/react-query-devtools axios
- src/queryClient.ts
import { QueryClient } from "@tanstack/react-query";
import axios, { AxiosRequestConfig } from "axios";
type AnyOBJ = {
[key: string]: any;
};
export const getClient = (() => {
let client: QueryClient | null = null;
return () => {
if (!client) {
client = new QueryClient({
defaultOptions: {
queries: {
cacheTime: 1000 * 60 * 60 * 24, // 24시간
staleTime: 1000 * 60, // 1분
refetchOnMount: false,
refetchOnReconnect: false,
refetchOnWindowFocus: false,
},
},
});
}
return client;
};
})();
const { VITE_BASE_URL } = import.meta.env;
const BASE_URL = VITE_BASE_URL;
export const restFetcher = async ({
method,
path,
body,
params,
}: {
method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
path: string;
body?: AnyOBJ;
params?: AnyOBJ;
}) => {
try {
let url = `${BASE_URL}${path}`;
const axiosConfig: AxiosRequestConfig = {
method,
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": BASE_URL,
},
};
if (body) axiosConfig.data = body;
if (params) {
const searchParams = new URLSearchParams(params);
url += "?" + searchParams.toString();
}
const res = await axios(url, axiosConfig);
return res.data;
} catch (err) {
console.error(err);
}
};
export const QueryKeys = {};
- src/App.tsx
import { useRoutes } from "react-router-dom";
import { QueryClientProvider } from "@tanstack/react-query";
import { getClient } from "./queryClient";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { routes } from "@/Routes";
export default function App() {
const queryClient = getClient();
const elem = useRoutes(routes);
return (
<QueryClientProvider client={queryClient}>
{elem}
<ReactQueryDevtools />
</QueryClientProvider>
);
}
$ yarn add -D eslint eslint-config-airbnb eslint-config-prettier eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-prettier eslint-plugin-react eslint-plugin-react-hooks
$ yarn add -D @typescript-eslint/eslint-plugin @typescript-eslint/parser
- .eslintrc.js
module.exports = {
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 13,
sourceType: "module",
},
plugins: ["react", "@typescript-eslint"],
extends: [
"eslint:recommended",
"plugin:react/recommended",
"airbnb",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended",
],
env: {
browser: true,
es2021: true,
},
rules: {
"@typescript-eslint/interface-name-prefix": "on",
"@typescript-eslint/explicit-function-return-type": "on",
"@typescript-eslint/explicit-module-boundary-types": "on",
"@typescript-eslint/no-explicit-any": "on",
},
};
- .prettierrc
{
"singleQuote": true,
"trailingComma": "all",
"semi": true,
"useTabs": false,
"tabWidth": 2,
"printWidth": 80,
"arrowParens": "always"
}
$ yarn add -D msw
$ npx msw init public/ --save
- src/mocks/handlers.ts
import { rest } from "msw";
const dummy = "테스트입니다.";
export const handlers = [
// 테스트 mock api
rest.get("/test", (req, res, ctx) => {
return res(ctx.status(200), ctx.json(dummy));
}),
];
- src/mocks/worker.ts
import { setupWorker } from "msw";
import { handlers } from "./handlers";
export const worker = setupWorker(...handlers);
- src/main.tsx
...
import { worker } from "./mocks/worker";
if (import.meta.env.DEV) {
worker.start();
}
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
...
);
handlers.ts에서 작성한 테스트 mock api를 호출해보자.
react-query를 사용하고 있으므로 우선 queryClient.ts에서 queryKeys에 테스트 API의 타입을 정의한다.
- src/types/test.ts
export type Test = string;
테스트 mock API에서 반환하는 데이터의 타입이 string 타입이므로 Test타입을 string으로 지정한다.
MainPage에서 해당 API를 호출하고 브라우저에 출력해보자.
- src/pages/Main/index.tsx
import { QueryKeys, restFetcher } from "@/queryClient";
import { Test } from "@/types/test";
import { useQuery } from "@tanstack/react-query";
import React from "react";
export default function Main() {
const { data } = useQuery<Test>([QueryKeys.TEST], () =>
restFetcher({ method: "GET", path: "test" }),
);
return <div>{data}</div>;
}
브라우저에서 결과를 확인해본다.
react-query를 사용하여 API를 호출했으므로 react-query 캐시에도 해당 값이 저장되어 있을 것이다. devtools를 통해 데이터를 확인해본다.
yarn add -D tailwindcss postcss autoprefixer
- postcss.config.cjs
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
- tailwind.config.cjs
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {},
},
plugins: [],
};
- src/index.css
...
@tailwind base;
@tailwind components;
@tailwind utilities;
잘 적용되는지 확인해보자.
- src/pages/Main/index.tsx
import { QueryKeys, restFetcher } from "@/queryClient";
import { Test } from "@/types/test";
import { useQuery } from "@tanstack/react-query";
import React from "react";
export default function Main() {
const { data } = useQuery<Test>([QueryKeys.TEST], () =>
restFetcher({ method: "GET", path: "test" }),
);
return (
<div className="min-h-screen flex justify-center items-center">
<h1 className="text-3xl font-bold text-blue-600">{data}</h1>
</div>
);
}