Skip to content

프로젝트 초기 설정

Sangmin Lee edited this page Jan 1, 2023 · 3 revisions

1. Vite + React + TypeScript 개발환경 세팅

프로젝트 생성 후 패키지 설치

$ 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/*"]
    },
    ...
  },
  ...
}

2. 리액트 라우터 설정

패키지 설치

$ 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>
  );
}

3. React-Query 설정

패키지 설치

$ 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>
  );
}

4. ESLint, Prettier 설정

패키지 설치

$ 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"
}

5. MSW(Mock Service Worker) 설정

패키지 설치

$ 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(
  ...
);

테스트 API 호출해보기

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를 통해 데이터를 확인해본다.


6. TailwindCSS 설정

패키지 설치

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>
  );
}