-
Notifications
You must be signed in to change notification settings - Fork 1
OaO 환경설정 A to Z
npm i -g yarn # yarn global 설치
yarn create vite client --template react-ts
cd client
yarn # client 내 패키지 설치
하위 내용은 client
폴더 안에서 진행함
<!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
function App() {
return <div>Hello World!</div>
}
export default App;
src/index.css
내용 삭제 : ! 파일은 삭제하지 않음
src/App.css
파일 삭제
src/assets
폴더 비우기
public
폴더 비우기
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
[eslint-config-airbnb](https://www.npmjs.com/package/eslint-config-airbnb)
npx install-peerdeps --dev eslint-config-airbnb
It seems as if you are using Yarn. Would you like to use Yarn for the installation? (y/n)
=> y
[eslint-config-airbnb-typescript](https://www.npmjs.com/package/eslint-config-airbnb-typescript)
yarn add -D eslint-config-airbnb-typescript
-
root 추가
- oao 작업영역에서 lint 사용을 위해서
"root": true
추가
- oao 작업영역에서 lint 사용을 위해서
-
extends : 순서유의
-
"airbnb"
,"airbnb-typescript"
,"airbnb/hooks"
추가 -
"eslint:recommended"
를 제거
-
-
parserOptions
-
tsconfigRootDir: __dirname
, // 추가 -
project: "./tsconfig.json"
를 추가
-
-
rules
-
indent: ["error", 2]
4에서 2로 수정
-
// .eslintrc.cjs
module.exports = {
root: true, // 추가
env: {
browser: true,
es2021: true,
},
extends: [
"airbnb", // 추가
"airbnb-typescript", // 추가
"airbnb/hooks", // 추가
// **** 삭제 "eslint:recommended" ****
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended",
],
overrides: [],
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
tsconfigRootDir: __dirname, // 추가
project: "./tsconfig.json", // 추가
},
plugins: ["react", "@typescript-eslint"],
rules: {
indent: ["error", 2], // 4에서 2로 수정
"linebreak-style": "off",
quotes: ["error", "single"],
semi: ["error", "always"],
},
};
-
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" }]
}
yarn add -D prettier eslint-config-prettier eslint-plugin-prettier
[Configuration File · Prettier](https://prettier.io/docs/en/configuration.html)
//.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',
};
-
plugins에
'prettier'
추가 -
extends에
'plugin:@prettier/recommended'
추가 - rules custom
// .eslintrc.cjs
module.exports = {
root: true,
env: {
browser: true,
es2021: true,
},
extends: [
'airbnb',
'airbnb-typescript',
'airbnb/hooks',
'plugin:react/recommended',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended', // 추가
],
overrides: [],
parser: '@typescript-eslint/parser',
parserOptions: {
project: './tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['react', '@typescript-eslint', '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' }],
}, // custom
};
// client 폴더에 jest 설치
yarn add -D jest @types/jest
- 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
jest.config.js
파일이 생성된다.
-
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)
-
jest.config.cjs
를 include하라는 에러가 뜨기 때문에tsconfig.json
의 include에jest.config.cjs
파일을 넣어준다
{
"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" }]
}
[FIX: .gitignore 파일 하나로 병합 by n-ryu · Pull Request #10 · boostcampwm-2022/web20-OAO](https://github.com/boostcampwm-2022/web20-OAO/pull/10)
{
"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.43.0",
"@typescript-eslint/parser": "^5.43.0",
"@vitejs/plugin-react": "^2.2.0",
"eslint": "^8.2.0",
"eslint-config-airbnb": "19.0.4",
"eslint-config-airbnb-typescript": "^17.0.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.31.10",
"eslint-plugin-react-hooks": "^4.3.0",
"jest": "^29.3.1",
"prettier": "^2.7.1",
"ts-jest": "^29.0.3",
"typescript": "*",
"vite": "^3.2.3"
}
}
{
"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" }]
}
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}
module.exports = {
root: true,
env: {
browser: true,
es2021: true,
},
extends: [
'airbnb',
'airbnb-typescript',
'airbnb/hooks',
'plugin:react/recommended',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
overrides: [],
parser: '@typescript-eslint/parser',
parserOptions: {
project: './tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['react', '@typescript-eslint', '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' }],
},
};
module.exports = {
trailingComma: 'all',
tabWidth: 2,
semi: true,
singleQuote: true,
printWidth: 120,
useTabs: false,
bracketSpacing: true,
endOfLine: 'auto',
arrowParens: 'always',
proseWrap: 'never',
};
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
};
- Yarn
참고자료 - 참고
- TypeScript
- React
- Vite
Vite에서 Asset과 Public 폴더의 차이는?
Asset은 번들링 시에 loader를 거쳐갈 파일들이 위치한다. Public은 그냥 해당 폴더에 위치한 상태 그대로 제공될 파일들이 위치한다.
Vite는 빌드시 TypeScript 타입 체크를 별도로 수행하지 않는다.
원한다면 빌드 이전에tsc --noEmit
명령어로 별도의 타입체크 과정을 거쳐야한다.
Vite에서는 Babel을 안쓰나요?
쓸 수 있고, 하위호환성이 필요하다면 여전히 Babel을 사용해야한다. 다만 Vite가 기본적으로 ESmodule을 지원하는 브라우저를 상정하고 있어서, 그런 사례가 많이 보이지는 않고, 별도의 plugin을 사용해서 Babel을 적용시킬 수 있다.
참고자료
- 공식문서: vite
- 참고1
- 참고2
- 참고3
- ESLint
- Prettier
참고자료
- 공식문서: eslint
- 참고
- jest
- ts-jest
참고자료
- 공식문서: ts-jest
- 참고
- Jotai
- Styled-component
- Tailwind CSS
- twin.macro (참고)
- Tailwind-Styled-Component
고민이 필요한 영역
무엇이 정답이다 할 수 없지만,twin.macro
와tailwind-styled-component
각각 70,000과 10,000의 주간 다운로드 수를 가지고 있다. 패키지 치고는 대중적이라고 하기는 어렵다. 또한, 애초에 tailwind css의 철학 자체가 css-in-js의 철학과 어느정도 충돌한다는 관점도 꽤나 있는 듯 하다.
- Express.js
- OaO 환경설정 A to Z
- CRLF 너가 뭔데 날 힘들게 해?
- Github Issue 똑똑하게 사용하기
- OAO! CI CD 적용기 with release 자동화
- 매번 다른 import문
- 못생긴 상대경로에서 간zlzl존 절대경로로😎
- TodoList API 개발기
- 의존성 주입으로 DB를 바꿔보자
- 렌더링 최적화 서막: useNavigate를 추가한 순간 리렌더 범위가 확장된 건에 대하여
- 렌더링 최적화 1탄: 렌더링 범위에 대하여 (by 최적화무새)
- 렌더링 최적화 2탄: 잘못된 custom hook 사용,, 전체 리렌더링을 부르다,,
- 렌더링 최적화 3탄: Todo 상세 좀 봤다고 테이블 전체가 재렌더링 되는건을 고치기😌
- 렌더링 최적화 4탄: 다이어그램 편
- 🐁 마우스 상대위치 계산은 이상해
- React 컴포넌트에 애니메이션을 적용해보자 🏃🏻💨
- 컴포넌트 재사용성을 높여보자: Modal 분리기 🌹
- 선후관계를 자동완성으로 추가해보자 🔎