Last active
May 4, 2025 15:04
-
-
Save StatPan/9c6764c3b385e4f0b7ac3c07bdb54052 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # setup-react-pnpm.sh | |
| # pnpm을 사용한 React + TypeScript + ESLint + Prettier 설정 스크립트 | |
| #설치 명령어 | |
| #curl -o setup-react-pnpm.sh https://gist.githubusercontent.com/StatPan/9c6764c3b385e4f0b7ac3c07bdb54052/raw/0a65099bc776f1d81f9270df407f20c54cab7336/vite-react-starter.sh && chmod +x setup-react-pnpm.sh && ./setup-react-pnpm.sh 프로젝트명 [옵션] | |
| # 사용법: ./setup-react-pnpm.sh 프로젝트명 [옵션] | |
| # 예: ./setup-react-pnpm.sh my-app | |
| # 옵션: | |
| # --tailwind Tailwind CSS 설치 및 설정 | |
| # --router React Router 설치 | |
| # --husky Husky 및 lint-staged 설정 (Git Hook) | |
| # --eslint8 ESLint 8.x 버전 사용 (기본값: 9.x) | |
| set -e | |
| # 기본 옵션 설정 | |
| INSTALL_TAILWIND=false | |
| INSTALL_ROUTER=false | |
| INSTALL_HUSKY=false | |
| USE_ESLINT8=false | |
| INSTALL_TEST=false | |
| INSTALL_STORYBOOK=false | |
| # 프로젝트 이름 확인 | |
| if [ -z "$1" ]; then | |
| echo "프로젝트 이름을 입력해주세요." | |
| echo "사용법: ./setup-react-pnpm.sh 프로젝트명 [옵션]" | |
| echo "옵션:" | |
| echo " --tailwind Tailwind CSS 설치 및 설정" | |
| echo " --router React Router 설치" | |
| echo " --husky Husky 및 lint-staged 설정 (Git Hook)" | |
| echo " --eslint8 ESLint 8.x 버전 사용 (기본값: 9.x)" | |
| echo " --test Jest 및 React Testing Library 설정" | |
| echo " --storybook Storybook 설정" | |
| exit 1 | |
| fi | |
| PROJECT_NAME="$1" | |
| shift | |
| # 추가 옵션 파싱 | |
| for arg in "$@"; do | |
| case $arg in | |
| --test) | |
| INSTALL_TEST=true | |
| ;; | |
| --storybook) | |
| INSTALL_STORYBOOK=true | |
| ;; | |
| --tailwind) | |
| INSTALL_TAILWIND=true | |
| ;; | |
| --router) | |
| INSTALL_ROUTER=true | |
| ;; | |
| --husky) | |
| INSTALL_HUSKY=true | |
| ;; | |
| --eslint8) | |
| USE_ESLINT8=true | |
| ;; | |
| *) | |
| echo "알 수 없는 옵션: $arg" | |
| exit 1 | |
| ;; | |
| esac | |
| done | |
| echo "🚀 pnpm으로 리액트 프로젝트 생성 시작: $PROJECT_NAME" | |
| echo " - ESLint + Prettier 설정" | |
| if [ "$USE_ESLINT8" = true ]; then | |
| echo " - ESLint 8.x 버전 사용" | |
| else | |
| echo " - ESLint 9.x 버전 사용" | |
| fi | |
| if [ "$INSTALL_TAILWIND" = true ]; then | |
| echo " - Tailwind CSS 설정" | |
| fi | |
| if [ "$INSTALL_ROUTER" = true ]; then | |
| echo " - React Router 설치" | |
| fi | |
| if [ "$INSTALL_HUSKY" = true ]; then | |
| echo " - Husky + lint-staged 설정" | |
| fi | |
| # 옵션 출력 부분에 테스트 옵션 추가 | |
| if [ "$INSTALL_TEST" = true ]; then | |
| echo " - Jest + React Testing Library 설정" | |
| fi | |
| if [ "$INSTALL_STORYBOOK" = true ]; then | |
| echo " - Storybook 설정" | |
| fi | |
| # pnpm이 설치되어 있는지 확인 | |
| if ! command -v pnpm &> /dev/null; then | |
| echo "pnpm이 설치되어 있지 않습니다. pnpm을 먼저 설치해주세요." | |
| echo "npm install -g pnpm" | |
| exit 1 | |
| fi | |
| # Vite + React + TypeScript 프로젝트 생성 | |
| echo "📦 Vite + React + TypeScript 프로젝트 생성 중..." | |
| pnpm create vite "$PROJECT_NAME" --template react-ts | |
| # 프로젝트 디렉토리로 이동 | |
| cd "$PROJECT_NAME" | |
| # 기본 의존성 설치 | |
| echo "📦 기본 의존성 설치 중..." | |
| pnpm install | |
| # 의존성 설치 후 추가 패키지 설치 | |
| if [ "$INSTALL_TAILWIND" = true ]; then | |
| echo "📦 Tailwind CSS 설치 중..." | |
| pnpm add -D tailwindcss postcss autoprefixer | |
| npx tailwindcss init -p | |
| # Tailwind CSS 설정 파일 수정 | |
| cat > tailwind.config.js << EOF | |
| /** @type {import('tailwindcss').Config} */ | |
| export default { | |
| content: [ | |
| "./index.html", | |
| "./src/**/*.{js,ts,jsx,tsx}", | |
| ], | |
| theme: { | |
| extend: {}, | |
| }, | |
| plugins: [], | |
| } | |
| EOF | |
| # CSS 파일에 Tailwind 지시어 추가 | |
| cat > src/index.css << EOF | |
| @tailwind base; | |
| @tailwind components; | |
| @tailwind utilities; | |
| EOF | |
| echo "✅ Tailwind CSS 설정 완료" | |
| fi | |
| if [ "$INSTALL_ROUTER" = true ]; then | |
| echo "📦 React Router 설치 중..." | |
| pnpm add react-router-dom | |
| echo "✅ React Router 설치 완료" | |
| fi | |
| # 테스트 도구 설치 | |
| if [ "$INSTALL_TEST" = true ]; then | |
| echo "📦 테스트 도구 설치 중..." | |
| pnpm add -D jest @types/jest ts-jest @testing-library/react @testing-library/jest-dom @testing-library/user-event jest-environment-jsdom | |
| # Jest 설정 파일 생성 | |
| cat > jest.config.js << EOF | |
| module.exports = { | |
| preset: 'ts-jest', | |
| testEnvironment: 'jsdom', | |
| moduleNameMapper: { | |
| '\\.(css|less|scss|sass)$': 'identity-obj-proxy', | |
| '^@/(.*)$': '<rootDir>/src/$1' | |
| }, | |
| setupFilesAfterEnv: ['<rootDir>/src/setupTests.ts'], | |
| }; | |
| EOF | |
| # setupTests 파일 생성 | |
| mkdir -p src | |
| cat > src/setupTests.ts << EOF | |
| import '@testing-library/jest-dom'; | |
| EOF | |
| # 예제 테스트 파일 생성 | |
| mkdir -p src/components/__tests__ | |
| cat > src/components/__tests__/example.test.tsx << EOF | |
| import { render, screen } from '@testing-library/react'; | |
| test('기본 테스트 예제', () => { | |
| render(<div>테스트 성공</div>); | |
| expect(screen.getByText('테스트 성공')).toBeInTheDocument(); | |
| }); | |
| EOF | |
| # package.json에 테스트 스크립트 추가 | |
| TEST_SCRIPTS='"test": "jest",\n "test:watch": "jest --watch",\n "test:coverage": "jest --coverage",' | |
| sed -i.bak "s/\"scripts\": {/\"scripts\": {\n $TEST_SCRIPTS/" package.json && rm package.json.bak | |
| echo "✅ 테스트 설정 완료" | |
| fi | |
| # Storybook 설치 | |
| if [ "$INSTALL_STORYBOOK" = true ]; then | |
| echo "📦 Storybook 설치 중..." | |
| pnpm dlx storybook@latest init --builder @storybook/builder-vite -y | |
| # package.json에 Storybook 테스트 스크립트 추가 | |
| STORYBOOK_TEST_SCRIPT='"test-storybook": "test-storybook",' | |
| sed -i.bak "s/\"scripts\": {/\"scripts\": {\n $STORYBOOK_TEST_SCRIPT/" package.json && rm package.json.bak | |
| echo "✅ Storybook 설정 완료" | |
| fi | |
| if [ "$INSTALL_HUSKY" = true ]; then | |
| echo "📦 Husky 및 lint-staged 설치 중..." | |
| pnpm add -D husky lint-staged | |
| # package.json에 husky 및 lint-staged 설정 추가 | |
| HUSKY_SCRIPT='"prepare": "husky install",' | |
| LINT_STAGED='"lint-staged": {\n "*.{ts,tsx}": [\n "eslint --fix",\n "prettier --write"\n ],\n "*.{css,scss}": [\n "prettier --write"\n ]\n },' | |
| # sed를 사용하여 package.json 파일에 스크립트 추가 | |
| sed -i.bak "s/\"scripts\": {/\"scripts\": {\n $HUSKY_SCRIPT/" package.json && rm package.json.bak | |
| sed -i.bak "s/\"devDependencies\": {/\"devDependencies\": {\n },\n $LINT_STAGED\n \"devDependencies\": {/" package.json && rm package.json.bak | |
| # Git 초기화 및 Husky 설정 | |
| git init | |
| pnpm prepare | |
| npx husky add .husky/pre-commit "npx lint-staged" | |
| # .gitignore 생성 | |
| cat > .gitignore << EOF | |
| # Logs | |
| logs | |
| *.log | |
| npm-debug.log* | |
| yarn-debug.log* | |
| yarn-error.log* | |
| pnpm-debug.log* | |
| lerna-debug.log* | |
| node_modules | |
| dist | |
| dist-ssr | |
| *.local | |
| # Editor directories and files | |
| .vscode/* | |
| !.vscode/extensions.json | |
| !.vscode/settings.json | |
| .idea | |
| .DS_Store | |
| *.suo | |
| *.ntvs* | |
| *.njsproj | |
| *.sln | |
| *.sw? | |
| EOF | |
| echo "✅ Husky 및 lint-staged 설정 완료" | |
| fi | |
| # ESLint와 Prettier 설치 - 버전에 따라 분기 | |
| echo "📦 ESLint 및 Prettier 설치 중..." | |
| if [ "$USE_ESLINT8" = true ]; then | |
| # ESLint 8.x 버전 설치 | |
| pnpm add -D eslint@^8.0.0 eslint-plugin-react@^7.33.2 eslint-plugin-react-hooks@^4.6.0 \ | |
| eslint-plugin-react-refresh@^0.4.5 @typescript-eslint/eslint-plugin@^6.0.0 \ | |
| @typescript-eslint/parser@^6.0.0 prettier@^3.0.0 eslint-config-prettier@^9.0.0 \ | |
| eslint-plugin-prettier@^5.0.0 | |
| else | |
| # ESLint 9.x 버전 설치 | |
| pnpm add -D eslint@^9.0.0 @eslint/js globals typescript-eslint \ | |
| eslint-plugin-react@^7.33.2 eslint-plugin-react-hooks@^5.0.0 \ | |
| eslint-plugin-react-refresh@^0.4.5 prettier@^3.0.0 eslint-config-prettier@^9.0.0 \ | |
| eslint-plugin-prettier@^5.0.0 | |
| fi | |
| # VS Code 설정 디렉토리 및 파일 생성 | |
| echo "📝 VS Code 설정 생성 중..." | |
| mkdir -p .vscode | |
| cat > .vscode/settings.json << EOF | |
| { | |
| "editor.formatOnSave": true, | |
| "editor.defaultFormatter": "esbenp.prettier-vscode", | |
| "editor.codeActionsOnSave": { | |
| "source.fixAll.eslint": "explicit" | |
| }, | |
| "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"], | |
| "prettier.requireConfig": true | |
| } | |
| EOF | |
| # VS Code 추천 확장 프로그램 설정 | |
| cat > .vscode/extensions.json << EOF | |
| { | |
| "recommendations": [ | |
| "dbaeumer.vscode-eslint", | |
| "esbenp.prettier-vscode", | |
| "dsznajder.es7-react-js-snippets" | |
| ] | |
| } | |
| EOF | |
| # Prettier 설정 파일 생성 | |
| echo "📝 Prettier 설정 파일 생성 중..." | |
| cat > .prettierrc << EOF | |
| { | |
| "semi": true, | |
| "tabWidth": 2, | |
| "printWidth": 100, | |
| "singleQuote": true, | |
| "trailingComma": "es5", | |
| "bracketSpacing": true, | |
| "arrowParens": "avoid" | |
| } | |
| EOF | |
| # Prettier 무시 파일 생성 | |
| cat > .prettierignore << EOF | |
| node_modules | |
| dist | |
| .vscode | |
| EOF | |
| # package.json에 스크립트 추가 | |
| echo "📝 npm 스크립트 추가 중..." | |
| NPM_SCRIPTS='"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",\n "lint:fix": "eslint . --ext ts,tsx --fix",\n "format": "prettier --write \\"src/**/*.{ts,tsx,css,scss}\\"",\n "format:check": "prettier --check \\"src/**/*.{ts,tsx,css,scss}\\"",' | |
| # sed를 사용하여 package.json 파일에 스크립트 추가 | |
| sed -i.bak "s/\"scripts\": {/\"scripts\": {\n $NPM_SCRIPTS/" package.json && rm package.json.bak | |
| # ESLint 설정 파일 생성 (ESLint 버전에 따라 분기) | |
| echo "📝 ESLint 설정 파일 생성 중..." | |
| if [ "$USE_ESLINT8" = true ]; then | |
| # ESLint 8.x 방식 (.eslintrc.cjs) | |
| cat > .eslintrc.cjs << EOF | |
| module.exports = { | |
| root: true, | |
| env: { browser: true, es2020: true }, | |
| extends: [ | |
| 'eslint:recommended', | |
| 'plugin:@typescript-eslint/recommended', | |
| 'plugin:react-hooks/recommended', | |
| 'plugin:react/recommended', | |
| 'plugin:react/jsx-runtime', | |
| 'prettier' // ESLint와 Prettier 충돌 방지를 위해 마지막에 추가 | |
| ], | |
| ignorePatterns: ['dist', '.eslintrc.cjs'], | |
| parser: '@typescript-eslint/parser', | |
| plugins: ['react-refresh', 'prettier'], | |
| rules: { | |
| 'react-refresh/only-export-components': [ | |
| 'warn', | |
| { allowConstantExport: true }, | |
| ], | |
| 'react/react-in-jsx-scope': 'off', | |
| 'react-hooks/rules-of-hooks': 'error', | |
| 'react-hooks/exhaustive-deps': 'warn', | |
| 'prettier/prettier': 'error' // Prettier 규칙 위반 시 에러로 표시 | |
| }, | |
| settings: { | |
| react: { | |
| version: 'detect' | |
| } | |
| } | |
| }; | |
| EOF | |
| echo "✅ ESLint 8.x 설정 완료 (.eslintrc.cjs)" | |
| else | |
| # ESLint 9.x 방식 (eslint.config.js) | |
| cat > eslint.config.js << EOF | |
| import js from '@eslint/js' | |
| import globals from 'globals' | |
| import reactHooks from 'eslint-plugin-react-hooks' | |
| import reactRefresh from 'eslint-plugin-react-refresh' | |
| import tseslint from 'typescript-eslint' | |
| import prettierPlugin from 'eslint-plugin-prettier' | |
| import prettierConfig from 'eslint-config-prettier' | |
| export default tseslint.config( | |
| prettierConfig, | |
| { ignores: ['dist'] }, | |
| { | |
| extends: [js.configs.recommended, ...tseslint.configs.recommended], | |
| files: ['**/*.{ts,tsx}'], | |
| languageOptions: { | |
| ecmaVersion: 2020, | |
| globals: globals.browser, | |
| }, | |
| plugins: { | |
| 'react-hooks': reactHooks, | |
| 'react-refresh': reactRefresh, | |
| 'prettier': prettierPlugin, | |
| }, | |
| rules: { | |
| ...reactHooks.configs.recommended.rules, | |
| 'react-refresh/only-export-components': [ | |
| 'warn', | |
| { allowConstantExport: true }, | |
| ], | |
| 'prettier/prettier': 'error', | |
| }, | |
| }, | |
| ) | |
| EOF | |
| echo "✅ ESLint 9.x 설정 완료 (eslint.config.js)" | |
| fi | |
| # ESLint와 Prettier 설치 부분 이후... | |
| # SWC 플러그인 설치 | |
| echo "📦 SWC 플러그인 설치 중..." | |
| pnpm add -D vite-plugin-swc | |
| # vite.config.ts 파일 수정 | |
| cat > vite.config.ts << EOF | |
| import { defineConfig } from 'vite'; | |
| import react from '@vitejs/plugin-react'; | |
| import swc from 'vite-plugin-swc'; | |
| export default defineConfig({ | |
| plugins: [ | |
| react(), | |
| swc({ | |
| jsc: { | |
| parser: { | |
| syntax: 'typescript', | |
| tsx: true, | |
| }, | |
| transform: { | |
| react: { | |
| runtime: 'automatic', | |
| development: true, | |
| refresh: true, | |
| }, | |
| }, | |
| }, | |
| }), | |
| ], | |
| }); | |
| EOF | |
| echo "✅ SWC 설정 완료" | |
| if [ "$INSTALL_TEST" = true ]; then | |
| echo "✅ Jest + React Testing Library가 설정되었습니다" | |
| echo " - 테스트 실행: pnpm test" | |
| echo " - 테스트 감시 모드: pnpm test:watch" | |
| echo " - 테스트 커버리지 보기: pnpm test:coverage" | |
| fi | |
| if [ "$INSTALL_STORYBOOK" = true ]; then | |
| echo "✅ Storybook이 설정되었습니다" | |
| echo " - Storybook 실행: pnpm storybook" | |
| echo " - Storybook 테스트: pnpm test-storybook" | |
| fi | |
| # VS Code 설정 디렉토리 및 파일 생성 (기존 코드)... | |
| echo "🎉 설정 완료: $PROJECT_NAME" | |
| echo "" | |
| echo "📋 다음 단계:" | |
| echo " 1. cd $PROJECT_NAME" | |
| echo " 2. pnpm run dev" | |
| echo "" | |
| echo "💡 ESLint와 Prettier 충돌 방지 설정이 완료되었습니다:" | |
| if [ "$USE_ESLINT8" = true ]; then | |
| echo " - ESLint 8.x 방식으로 설정되었습니다 (.eslintrc.cjs)" | |
| else | |
| echo " - ESLint 9.x 방식으로 설정되었습니다 (eslint.config.js)" | |
| fi | |
| echo " - Prettier와 통합되어 코드 스타일을 통일합니다" | |
| echo "💻 즐거운 코딩되세요! 😊" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment