Skip to content

OaO 환경설정 A to Z

NaGyeong Park edited this page Dec 18, 2022 · 6 revisions

💡 React-Typescript 프로젝트 생성

npm i -g yarn # yarn global 설치
yarn create vite client --template react-ts
cd client

image

yarn # client 내 패키지 설치

image image

💡 기본 템플릿 파일 수정

하위 내용은 client 폴더 안에서 진행함

✔️ 파일 내용 수정

index.html

<!DOCTYPE html>
<html lang="kr">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>OaO</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

src/App.tsx

// src/App.tsx

function App() {
  return <div>Hello World!</div>
}

export default App;

✔️ 파일 내용 비우기

src/index.css 내용 삭제 : ! 파일은 삭제하지 않음

✔️ 파일 삭제

src/App.css 파일 삭제

src/assets 폴더 비우기

public 폴더 비우기

✔️ 결과 사진

image

💡 eslint, prettier 설정

✔️ eslint 추가

yarn add eslint -D
yarn eslint --init

? How would you like to use ESLint?
❯ To check syntax, find problems, and enforce code style

? What type of modules does your project use?
❯ JavaScript modules (import/export)

? Which framework does your project use?
❯ react

? Does your project use TypeScript?
❯ Yes

? Where does your code run?
❯ browser

? How would you like to define a style for your project? ❯ Answer questions about your style

? What format do you want your config file to be in?
JavaScript

? What style of indentation do you use?
❯ space

? What quotes do you use for strings?
❯ single

? What line endings do you use? … 
❯ Unix

? Do you require semicolons? 
❯ Yes

✔ Would you like to install them now?
❯ Yes

✔ Which package manager do you want to use?
❯ yarn

✔️ standard eslint

yarn : eslint-config-standard-with-typescript

yarn add eslint-config-standard-with-typescript

.eslintrc.cjs

  • extends : 순서유의
    • standard-with-typescript 추가
    • "eslint:recommended"를 제거
  • parserOptions
    • tsconfigRootDir: __dirname 추가
    • project: "./tsconfig.json"를 추가
  • rules
    • indent: ["error", 2]4에서 2로 수정
// .eslintrc.cjs
module.exports = {
  env: {
    browser: true,
    es2021: true,
  },
  extends: [
// **** 삭제 "eslint:recommended" ****
    "plugin:react/recommended",
    "standard-with-typescript", // 추가
  ],
  overrides: [],
  parser: "@typescript-eslint/parser",
  parserOptions: {
    ecmaVersion: "latest",
    sourceType: "module",
    tsconfigRootDir: __dirname, // 추가
    project: "./tsconfig.json", // 추가
  },
  plugins: ["react"],
  rules: {
    indent: ["error", 2], // 4에서 2로 수정
    "linebreak-style": "off",
    quotes: ["error", "single"],
    semi: ["error", "always"],
  },
};

tsconfig.json

  • include
    • "./.eslintrc.cjs"추가
// tsconfig.json
{
  "compilerOptions": {
    "target": "ESNext",
    "useDefineForClassFields": true,
    "lib": ["DOM", "DOM.Iterable", "ESNext"],
    "allowJs": false,
    "skipLibCheck": true,
    "esModuleInterop": false,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "ESNext",
    "moduleResolution": "Node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": ["./.eslintrc.cjs", "src"], // 추가
  "references": [{ "path": "./tsconfig.node.json" }]
}

✔️ prettier 설정

yarn add -D prettier eslint-config-prettier eslint-plugin-prettier

.prettierrc.cjs 파일 생성 및 수정

Configuration File · Prettier

//.prettierrc.cjs custom
module.exports = {
  trailingComma: 'all',
  tabWidth: 2,
  semi: true,
  singleQuote: true,
  printWidth: 120,
  useTabs: false,
  bracketSpacing: true,
  endOfLine: 'auto',
  arrowParens: 'always',
  proseWrap: 'never',
};

.eslintrc.cjs

  • plugins'prettier' 추가
  • extends'plugin:prettier/recommended' 추가
  • rules custom
// .eslintrc.cjs
module.exports = {
  env: {
    browser: true,
    es2021: true,
  },
  extends: ['plugin:react/recommended', 'standard-with-typescript', 'plugin:prettier/recommended'], // 추가
  overrides: [],
  parser: '@typescript-eslint/parser',
  parserOptions: {
    project: './tsconfig.json',
    tsconfigRootDir: __dirname,
    ecmaVersion: 'latest',
    sourceType: 'module',
  },
  plugins: ['react', 'prettier'], // 추가 
  rules: {
    indent: ['error', 2],
    'linebreak-style': 'off',
    quotes: ['error', 'single'],
    semi: ['error', 'always'],
    'react/react-in-jsx-scope': 0,
    'prettier/prettier': ['error', { endOfLine: 'auto' }],
  },
};

💡 Jest

// client 폴더에 jest 설치
yarn add -D jest @types/jest

✔️ package.json

  • test script 추가 작성
// package.json
...
"scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview",
    "test" : "jest"
  },
...
yarn add -D ts-jest
yarn ts-jest config:init

image

jest.config.js 파일이 생성된다.

✔️ 파일 이름 일치하지 않을 경우 describe, it 등 jest 함수 찾지 못하는 문제 발생

  • Error log

    ReferenceError: module is not defined in ES module scope
    This file is being treated as an ES module because it has a '.js' file extension and '/Users/sby/Desktop/MEMBERSHIP/web20-OAO/client/package.json' contains "type": "module". To treat it as a CommonJS script, rename it to use the '.cjs' file extension.
        at file:///Users/sby/Desktop/MEMBERSHIP/web20-OAO/client/jest.config.js:2:1
        at ModuleJob.run (node:internal/modules/esm/module_job:193:25)
        at async Promise.all (index 0)
        at async ESMLoader.import (node:internal/modules/esm/loader:530:24)
        at async importModuleDynamicallyWrapper (node:internal/vm/module:438:15)
        at async requireOrImportModule (/Users/sby/Desktop/MEMBERSHIP/web20-OAO/client/node_modules/jest-util/build/requireOrImportModule.js:55:32)
        at async readConfigFileAndSetRootDir (/Users/sby/Desktop/MEMBERSHIP/web20-OAO/client/node_modules/jest-config/build/readConfigFileAndSetRootDir.js:112:22)
        at async readInitialOptions (/Users/sby/Desktop/MEMBERSHIP/web20-OAO/client/node_modules/jest-config/build/index.js:396:13)
        at async readConfig (/Users/sby/Desktop/MEMBERSHIP/web20-OAO/client/node_modules/jest-config/build/index.js:147:48)
        at async readConfigs (/Users/sby/Desktop/MEMBERSHIP/web20-OAO/client/node_modules/jest-config/build/index.js:417:26)
💡 **해결 :** type이 **module**이기 때문에 확장자를 `js`가 아닌 `cjs`로 수정해준다 `jest.config.js` ⇒ `jest.config.cjs`

✔️ lint error : include error

  • jest.config.cjsinclude하라는 에러가 뜨기 때문에 tsconfig.jsonincludejest.config.cjs 파일을 넣어준다

✔️ tsconfig.json

{
  "compilerOptions": {
    "target": "ESNext",
    "useDefineForClassFields": true,
    "lib": ["DOM", "DOM.Iterable", "ESNext"],
    "allowJs": false,
    "skipLibCheck": true,
    "esModuleInterop": false,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "ESNext",
    "moduleResolution": "Node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": [".eslintrc.cjs", "src", "jest.config.cjs"], // 추가
  "references": [{ "path": "./tsconfig.node.json" }]
}

💡 .gitignore 하나로 병합

[FIX: .gitignore 파일 하나로 병합 by n-ryu · Pull Request #10 · boostcampwm-2022/web20-OAO](https://github.com/boostcampwm-2022/web20-OAO/pull/10)

💡 최종 결과물

✔️ 폴더 및 파일

image

✔️ package.json

{
  "name": "client",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview",
    "test": "jest"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "@types/jest": "^29.2.3",
    "@types/react": "^18.0.24",
    "@types/react-dom": "^18.0.8",
    "@typescript-eslint/eslint-plugin": "^5.0.0",
    "@typescript-eslint/parser": "^5.43.0",
    "@vitejs/plugin-react": "^2.2.0",
    "eslint": "^8.0.1",
    "eslint-config-prettier": "^8.5.0",
    "eslint-config-standard-with-typescript": "^23.0.0",
    "eslint-plugin-import": "^2.25.2",
    "eslint-plugin-jsx-a11y": "^6.5.1",
    "eslint-plugin-n": "^15.0.0",
    "eslint-plugin-prettier": "^4.2.1",
    "eslint-plugin-promise": "^6.0.0",
    "eslint-plugin-react": "^7.31.11",
    "eslint-plugin-react-hooks": "^4.3.0",
    "jest": "^29.3.1",
    "jest-junit": "^15.0.0",
    "prettier": "^2.7.1",
    "ts-jest": "^29.0.3",
    "ts-node": "^10.9.1",
    "typescript": "*",
    "vite": "^3.2.3"
  }
}

✔️ tsconfig.json

{
  "compilerOptions": {
    "target": "ESNext",
    "useDefineForClassFields": true,
    "lib": ["DOM", "DOM.Iterable", "ESNext"],
    "allowJs": false,
    "skipLibCheck": true,
    "esModuleInterop": false,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "ESNext",
    "moduleResolution": "Node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",
    "baseUrl": ".",
    "paths": {
      "@repository/*": ["src/core/repository/*"],
      "@todo/*": ["src/core/todo/*"],
      "@components/*": ["src/components/*"],
      "@container/*": ["src/container/*"],
      "@core/*": ["src/core/*"],
      "@images/*": ["src/images/*"],
      "@page/*": ["src/page/*"],
      "@util/*": ["src/util/*"],
      "@hooks/*": ["src/hooks/*"],
      "@": ["src/*"],
    }
  },
  "include": [".eslintrc.cjs", "src", "jest.config.cjs"],
  "references": [{ "path": "./tsconfig.node.json" }]
}

✔️  tsconfig.node.json

{
  "compilerOptions": {
    "composite": true,
    "module": "ESNext",
    "moduleResolution": "Node",
    "allowSyntheticDefaultImports": true
  },
  "include": ["vite.config.ts"]
}

✔️ .eslintrc.cjs

module.exports = {
  env: {
    browser: true,
    es2021: true,
  },
  extends: ['plugin:react/recommended', 'standard-with-typescript', 'plugin:prettier/recommended'],
  overrides: [],
  parser: '@typescript-eslint/parser',
  parserOptions: {
    project: './tsconfig.json',
    tsconfigRootDir: __dirname,
    ecmaVersion: 'latest',
    sourceType: 'module',
  },
  plugins: ['react', 'prettier'],
  rules: {
    indent: ['error', 2],
    'linebreak-style': 'off',
    quotes: ['error', 'single'],
    semi: ['error', 'always'],
    'react/react-in-jsx-scope': 0,
    'prettier/prettier': ['error', { endOfLine: 'auto' }],
  },
};

✔️ .prettierrc.cjs

module.exports = {
  trailingComma: 'all',
  tabWidth: 2,
  semi: true,
  singleQuote: true,
  printWidth: 120,
  useTabs: false,
  bracketSpacing: true,
  endOfLine: 'auto',
  arrowParens: 'always',
  proseWrap: 'never',
};

✔️ jest.config.cjs

/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
};

eslint 설정 standard 이전 airbnb로 설정했을때 환경설정

개발 환경 설정 notion

선행학습자료

Package Manager

  • Yarn

참고자료 - 참고

Essential for React Build

  • TypeScript
  • React
  • Vite

Vite에서 Asset과 Public 폴더의 차이는?
Asset은 번들링 시에 loader를 거쳐갈 파일들이 위치한다. Public은 그냥 해당 폴더에 위치한 상태 그대로 제공될 파일들이 위치한다.

Vite는 빌드시 TypeScript 타입 체크를 별도로 수행하지 않는다.
원한다면 빌드 이전에 tsc --noEmit 명령어로 별도의 타입체크 과정을 거쳐야한다.

Vite에서는 Babel을 안쓰나요?
쓸 수 있고, 하위호환성이 필요하다면 여전히 Babel을 사용해야한다. 다만 Vite가 기본적으로 ESmodule을 지원하는 브라우저를 상정하고 있어서, 그런 사례가 많이 보이지는 않고, 별도의 plugin을 사용해서 Babel을 적용시킬 수 있다.

참고자료
- 공식문서: vite
- 참고1
- 참고2
- 참고3

Linter and Formatter

  • ESLint
  • Prettier

참고자료
- 공식문서: eslint
- 참고

Testing

  • jest
  • ts-jest

참고자료
- 공식문서: ts-jest
- 참고

State Management

  • Jotai

Styling

  • Styled-component
  • Tailwind CSS
  • twin.macro (참고)
  • Tailwind-Styled-Component

고민이 필요한 영역
무엇이 정답이다 할 수 없지만, twin.macrotailwind-styled-component 각각 70,000과 10,000의 주간 다운로드 수를 가지고 있다. 패키지 치고는 대중적이라고 하기는 어렵다. 또한, 애초에 tailwind css의 철학 자체가 css-in-js의 철학과 어느정도 충돌한다는 관점도 꽤나 있는 듯 하다.

Backend

  • Express.js

💊 비타500

📌 프로젝트

🐾 개발 일지

🥑 그룹활동

🌴 멘토링
🥕 데일리 스크럼
🍒 데일리 개인 회고
🐥 주간 회고
👯 발표 자료
Clone this wiki locally