diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 000000000..1ec6de853 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,55 @@ +name: Deploy to GitHub Pages + +on: + push: + branches: [main] + workflow_dispatch: + +permissions: + contents: write + +jobs: + build-and-deploy: + if: github.event_name == 'push' + runs-on: ubuntu-latest + concurrency: + group: pages-deploy-${{ github.ref }} + cancel-in-progress: true + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + - name: Enable corepack (ensure pnpm shim) + run: corepack enable + + - name: Setup pnpm + uses: pnpm/action-setup@v2 + with: + version: 8 + run_install: false + + - name: Install dependencies + run: pnpm install --no-frozen-lockfile + + - name: Build + run: pnpm run build + + - name: Add 404.html (SPA fallback) + run: | + if [ -f "dist/index.html" ]; then + cp dist/index.html dist/404.html + echo "Added 404.html for SPA routing" + fi + + - name: Deploy to GitHub Pages + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./dist + force_orphan: true + publish_branch: gh-pages diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..37f4cb4e8 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,8 @@ +node_modules +dist +build +coverage +*.log +pnpm-lock.yaml +package-lock.json +yarn.lock \ No newline at end of file diff --git a/.prettierrc b/.prettierrc index d9ae6b1fb..391e48180 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,9 +1,11 @@ { - "semi": false, - "printWidth": 120, + "semi": true, + "singleQuote": true, + "jsxSingleQuote": true, "tabWidth": 2, - "singleQuote": false, - "quoteProps": "consistent", - "trailingComma": "all", - "singleAttributePerLine": false + "bracketSpacing": true, + "bracketSameLine": false, + "arrowParens": "always", + "printWidth": 100, + "endOfLine": "auto" } \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js index 092408a9f..4834d7280 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,28 +1,130 @@ -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' - -export default tseslint.config( - { ignores: ['dist'] }, +import typescript from "@typescript-eslint/eslint-plugin" +import typescriptParser from "@typescript-eslint/parser" +import { defineConfig } from "eslint/config" +import prettier from "eslint-config-prettier" +import compat from "eslint-plugin-compat" +import importPlugin from "eslint-plugin-import" +import eslintPluginPrettier from "eslint-plugin-prettier" +import react from "eslint-plugin-react" +import reactHooks from "eslint-plugin-react-hooks" +import reactRefresh from "eslint-plugin-react-refresh" + +export default defineConfig([ + { + ignores: ["**/node_modules/**", "dist/**", ".eslintrc.cjs"], + }, { - extends: [js.configs.recommended, ...tseslint.configs.recommended], - files: ['**/*.{ts,tsx}'], + files: ["**/*.{js,jsx,ts,tsx}"], languageOptions: { - ecmaVersion: 2020, - globals: globals.browser, + ecmaVersion: "latest", + sourceType: "module", + parser: typescriptParser, + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + tsconfigRootDir: ".", + }, }, plugins: { - 'react-hooks': reactHooks, - 'react-refresh': reactRefresh, + "prettier": eslintPluginPrettier, + react, + "react-hooks": reactHooks, + "react-refresh": reactRefresh, + "@typescript-eslint": typescript, + compat, + "import": importPlugin, + }, + settings: { + react: { + version: "detect", + }, + browsers: "> 0.5%, last 2 versions, not op_mini all, Firefox ESR, not dead", }, rules: { - ...reactHooks.configs.recommended.rules, - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, + // 기존 규칙 유지 + "react-refresh/only-export-components": ["warn", { allowConstantExport: true }], + + // Prettier 통합 규칙 + "comma-dangle": [ + "error", + { + arrays: "always-multiline", + objects: "always-multiline", + imports: "always-multiline", + exports: "always-multiline", + functions: "never", + }, + ], + + // React 관련 규칙 + "react/prop-types": "off", + "react/react-in-jsx-scope": "off", + "react-hooks/rules-of-hooks": "error", + + // TypeScript 관련 규칙 + "@typescript-eslint/no-explicit-any": "warn", + + // 팀 컨벤션 - var 사용 금지 + "no-var": "error", + "@typescript-eslint/no-unused-vars": "error", + + // 팀 컨벤션 - 동등 연산자 (==, !=) 금지 + "eqeqeq": ["error", "always", { null: "ignore" }], + + // 팀 컨벤션 - 얼리 리턴 권장 + "consistent-return": "error", + "no-else-return": ["error", { allowElseIf: false }], + + // 팀 컨벤션 - 템플릿 리터럴 규칙 + "prefer-template": "error", + + // 팀 컨벤션 - 상수는 대문자 + "camelcase": [ + "error", + { + properties: "never", + ignoreDestructuring: false, + ignoreImports: false, + ignoreGlobals: false, + allow: ["^[A-Z][A-Z0-9_]*$"], + }, + ], + + // 팀 컨벤션 - 구조분해할당 권장 + "prefer-destructuring": [ + "error", + { + array: true, + object: true, + }, + { + enforceForRenamedProperties: false, + }, + ], + + // 기본 코드 품질 규칙 + "prefer-const": "error", + "object-shorthand": "error", + "no-multiple-empty-lines": ["error", { max: 1, maxEOF: 0 }], + "no-console": ["warn", { allow: ["warn", "error"] }], + "no-debugger": "warn", + "no-undef": "off", + + // import 순서 규칙 + "import/order": [ + "error", + { + "groups": ["builtin", "external", ["parent", "sibling"], "index"], + "alphabetize": { + order: "asc", + caseInsensitive: true, + }, + "newlines-between": "always", + }, ], + "import/extensions": "off", }, }, -) + prettier, +]) \ No newline at end of file diff --git a/package.json b/package.json index e014c5272..794bb44a0 100644 --- a/package.json +++ b/package.json @@ -12,16 +12,20 @@ "coverage": "vitest run --coverage" }, "dependencies": { + "@tanstack/react-query": "^5.85.0", "react": "^19.1.1", - "react-dom": "^19.1.1" + "react-dom": "^19.1.1", + "zustand": "^5.0.7" }, "devDependencies": { "@eslint/js": "^9.33.0", "@radix-ui/react-dialog": "^1.1.14", "@radix-ui/react-select": "^2.2.5", + "@tanstack/react-query-devtools": "^5.85.3", "@testing-library/jest-dom": "^6.6.4", "@testing-library/react": "^16.3.0", "@testing-library/user-event": "^14.6.1", + "@types/node": "^24.2.1", "@types/react": "^19.1.9", "@types/react-dom": "^19.1.7", "@vitejs/plugin-react": "^5.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6b2a40d18..4c6a04c52 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,12 +8,18 @@ importers: .: dependencies: + '@tanstack/react-query': + specifier: ^5.85.0 + version: 5.85.0(react@19.1.1) react: specifier: ^19.1.1 version: 19.1.1 react-dom: specifier: ^19.1.1 version: 19.1.1(react@19.1.1) + zustand: + specifier: ^5.0.7 + version: 5.0.7(@types/react@19.1.9)(react@19.1.1) devDependencies: '@eslint/js': specifier: ^9.33.0 @@ -24,6 +30,9 @@ importers: '@radix-ui/react-select': specifier: ^2.2.5 version: 2.2.5(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@tanstack/react-query-devtools': + specifier: ^5.85.3 + version: 5.85.3(@tanstack/react-query@5.85.0(react@19.1.1))(react@19.1.1) '@testing-library/jest-dom': specifier: ^6.6.4 version: 6.6.4 @@ -33,6 +42,9 @@ importers: '@testing-library/user-event': specifier: ^14.6.1 version: 14.6.1(@testing-library/dom@10.4.0) + '@types/node': + specifier: ^24.2.1 + version: 24.2.1 '@types/react': specifier: ^19.1.9 version: 19.1.9 @@ -41,7 +53,7 @@ importers: version: 19.1.7(@types/react@19.1.9) '@vitejs/plugin-react': specifier: ^5.0.0 - version: 5.0.0(vite@7.1.1(@types/node@22.8.1)) + version: 5.0.0(vite@7.1.1(@types/node@24.2.1)) axios: specifier: ^1.11.0 version: 1.11.0 @@ -68,7 +80,7 @@ importers: version: 0.539.0(react@19.1.1) msw: specifier: ^2.10.4 - version: 2.10.4(@types/node@22.8.1)(typescript@5.9.2) + version: 2.10.4(@types/node@24.2.1)(typescript@5.9.2) prettier: specifier: ^3.6.2 version: 3.6.2 @@ -83,10 +95,10 @@ importers: version: 8.39.0(eslint@9.33.0)(typescript@5.9.2) vite: specifier: ^7.1.1 - version: 7.1.1(@types/node@22.8.1) + version: 7.1.1(@types/node@24.2.1) vitest: specifier: ^3.2.4 - version: 3.2.4(@types/node@22.8.1)(@vitest/browser@2.1.3)(jsdom@26.1.0)(msw@2.10.4(@types/node@22.8.1)(typescript@5.9.2)) + version: 3.2.4(@types/node@24.2.1)(@vitest/browser@2.1.3)(jsdom@26.1.0)(msw@2.10.4(@types/node@24.2.1)(typescript@5.9.2)) vitest-browser-react: specifier: ^1.0.1 version: 1.0.1(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(@vitest/browser@2.1.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(vitest@3.2.4) @@ -103,10 +115,6 @@ packages: '@asamuzakjp/css-color@2.8.3': resolution: {integrity: sha512-GIc76d9UI1hCvOATjZPyHFmE5qhRccp3/zGfMPapK3jBi+yocEzp6BBB0UnfRYP9NP4FANqUZYb0hnfs3TM3hw==} - '@babel/code-frame@7.26.2': - resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} - engines: {node: '>=6.9.0'} - '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} @@ -930,6 +938,23 @@ packages: cpu: [x64] os: [win32] + '@tanstack/query-core@5.83.1': + resolution: {integrity: sha512-OG69LQgT7jSp+5pPuCfzltq/+7l2xoweggjme9vlbCPa/d7D7zaqv5vN/S82SzSYZ4EDLTxNO1PWrv49RAS64Q==} + + '@tanstack/query-devtools@5.84.0': + resolution: {integrity: sha512-fbF3n+z1rqhvd9EoGp5knHkv3p5B2Zml1yNRjh7sNXklngYI5RVIWUrUjZ1RIcEoscarUb0+bOvIs5x9dwzOXQ==} + + '@tanstack/react-query-devtools@5.85.3': + resolution: {integrity: sha512-WSVweCE1Kh1BVvPDHAmLgGT+GGTJQ9+a7bVqzD+zUiUTht+salJjYm5nikpMNaHFPJV102TCYdvgHgBXtURRNg==} + peerDependencies: + '@tanstack/react-query': ^5.85.3 + react: ^18 || ^19 + + '@tanstack/react-query@5.85.0': + resolution: {integrity: sha512-t1HMfToVMGfwEJRya6GG7gbK0luZJd+9IySFNePL1BforU1F3LqQ3tBC2Rpvr88bOrlU6PXyMLgJD0Yzn4ztUw==} + peerDependencies: + react: ^18 || ^19 + '@testing-library/dom@10.4.0': resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} engines: {node: '>=18'} @@ -992,8 +1017,8 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@22.8.1': - resolution: {integrity: sha512-k6Gi8Yyo8EtrNtkHXutUu2corfDf9su95VYVP10aGYMMROM6SAItZi0w1XszA6RtWTHSVp5OeFof37w0IEqCQg==} + '@types/node@24.2.1': + resolution: {integrity: sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==} '@types/react-dom@19.1.7': resolution: {integrity: sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw==} @@ -2133,8 +2158,8 @@ packages: engines: {node: '>=14.17'} hasBin: true - undici-types@6.19.8: - resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + undici-types@7.10.0: + resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==} universalify@0.2.0: resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} @@ -2345,6 +2370,24 @@ packages: resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} engines: {node: '>=18'} + zustand@5.0.7: + resolution: {integrity: sha512-Ot6uqHDW/O2VdYsKLLU8GQu8sCOM1LcoE8RwvLv9uuRT9s6SOHCKs0ZEOhxg+I1Ld+A1Q5lwx+UlKXXUoCZITg==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + snapshots: '@adobe/css-tools@4.4.0': {} @@ -2362,12 +2405,6 @@ snapshots: '@csstools/css-tokenizer': 3.0.3 lru-cache: 10.4.3 - '@babel/code-frame@7.26.2': - dependencies: - '@babel/helper-validator-identifier': 7.25.9 - js-tokens: 4.0.0 - picocolors: 1.1.1 - '@babel/code-frame@7.27.1': dependencies: '@babel/helper-validator-identifier': 7.27.1 @@ -2684,16 +2721,16 @@ snapshots: '@humanwhocodes/retry@0.4.2': {} - '@inquirer/confirm@5.0.1(@types/node@22.8.1)': + '@inquirer/confirm@5.0.1(@types/node@24.2.1)': dependencies: - '@inquirer/core': 10.0.1(@types/node@22.8.1) - '@inquirer/type': 3.0.0(@types/node@22.8.1) - '@types/node': 22.8.1 + '@inquirer/core': 10.0.1(@types/node@24.2.1) + '@inquirer/type': 3.0.0(@types/node@24.2.1) + '@types/node': 24.2.1 - '@inquirer/core@10.0.1(@types/node@22.8.1)': + '@inquirer/core@10.0.1(@types/node@24.2.1)': dependencies: '@inquirer/figures': 1.0.7 - '@inquirer/type': 3.0.0(@types/node@22.8.1) + '@inquirer/type': 3.0.0(@types/node@24.2.1) ansi-escapes: 4.3.2 cli-width: 4.1.0 mute-stream: 2.0.0 @@ -2706,9 +2743,9 @@ snapshots: '@inquirer/figures@1.0.7': {} - '@inquirer/type@3.0.0(@types/node@22.8.1)': + '@inquirer/type@3.0.0(@types/node@24.2.1)': dependencies: - '@types/node': 22.8.1 + '@types/node': 24.2.1 '@jridgewell/gen-mapping@0.3.12': dependencies: @@ -3081,9 +3118,24 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.46.2': optional: true + '@tanstack/query-core@5.83.1': {} + + '@tanstack/query-devtools@5.84.0': {} + + '@tanstack/react-query-devtools@5.85.3(@tanstack/react-query@5.85.0(react@19.1.1))(react@19.1.1)': + dependencies: + '@tanstack/query-devtools': 5.84.0 + '@tanstack/react-query': 5.85.0(react@19.1.1) + react: 19.1.1 + + '@tanstack/react-query@5.85.0(react@19.1.1)': + dependencies: + '@tanstack/query-core': 5.83.1 + react: 19.1.1 + '@testing-library/dom@10.4.0': dependencies: - '@babel/code-frame': 7.26.2 + '@babel/code-frame': 7.27.1 '@babel/runtime': 7.26.0 '@types/aria-query': 5.0.4 aria-query: 5.3.0 @@ -3153,9 +3205,9 @@ snapshots: '@types/json-schema@7.0.15': {} - '@types/node@22.8.1': + '@types/node@24.2.1': dependencies: - undici-types: 6.19.8 + undici-types: 7.10.0 '@types/react-dom@19.1.7(@types/react@19.1.9)': dependencies: @@ -3262,7 +3314,7 @@ snapshots: '@typescript-eslint/types': 8.39.0 eslint-visitor-keys: 4.2.1 - '@vitejs/plugin-react@5.0.0(vite@7.1.1(@types/node@22.8.1))': + '@vitejs/plugin-react@5.0.0(vite@7.1.1(@types/node@24.2.1))': dependencies: '@babel/core': 7.28.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.0) @@ -3270,21 +3322,21 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.30 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 7.1.1(@types/node@22.8.1) + vite: 7.1.1(@types/node@24.2.1) transitivePeerDependencies: - supports-color - '@vitest/browser@2.1.3(@types/node@22.8.1)(@vitest/spy@3.2.4)(typescript@5.9.2)(vite@7.1.1(@types/node@22.8.1))(vitest@3.2.4)': + '@vitest/browser@2.1.3(@types/node@24.2.1)(@vitest/spy@3.2.4)(typescript@5.9.2)(vite@7.1.1(@types/node@24.2.1))(vitest@3.2.4)': dependencies: '@testing-library/dom': 10.4.0 '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.0) - '@vitest/mocker': 2.1.3(@vitest/spy@3.2.4)(msw@2.10.4(@types/node@22.8.1)(typescript@5.9.2))(vite@7.1.1(@types/node@22.8.1)) + '@vitest/mocker': 2.1.3(@vitest/spy@3.2.4)(msw@2.10.4(@types/node@24.2.1)(typescript@5.9.2))(vite@7.1.1(@types/node@24.2.1)) '@vitest/utils': 2.1.3 magic-string: 0.30.17 - msw: 2.10.4(@types/node@22.8.1)(typescript@5.9.2) + msw: 2.10.4(@types/node@24.2.1)(typescript@5.9.2) sirv: 2.0.4 tinyrainbow: 1.2.0 - vitest: 3.2.4(@types/node@22.8.1)(@vitest/browser@2.1.3)(jsdom@26.1.0)(msw@2.10.4(@types/node@22.8.1)(typescript@5.9.2)) + vitest: 3.2.4(@types/node@24.2.1)(@vitest/browser@2.1.3)(jsdom@26.1.0)(msw@2.10.4(@types/node@24.2.1)(typescript@5.9.2)) ws: 8.18.0 transitivePeerDependencies: - '@types/node' @@ -3302,23 +3354,23 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@2.1.3(@vitest/spy@3.2.4)(msw@2.10.4(@types/node@22.8.1)(typescript@5.9.2))(vite@7.1.1(@types/node@22.8.1))': + '@vitest/mocker@2.1.3(@vitest/spy@3.2.4)(msw@2.10.4(@types/node@24.2.1)(typescript@5.9.2))(vite@7.1.1(@types/node@24.2.1))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - msw: 2.10.4(@types/node@22.8.1)(typescript@5.9.2) - vite: 7.1.1(@types/node@22.8.1) + msw: 2.10.4(@types/node@24.2.1)(typescript@5.9.2) + vite: 7.1.1(@types/node@24.2.1) - '@vitest/mocker@3.2.4(msw@2.10.4(@types/node@22.8.1)(typescript@5.9.2))(vite@7.1.1(@types/node@22.8.1))': + '@vitest/mocker@3.2.4(msw@2.10.4(@types/node@24.2.1)(typescript@5.9.2))(vite@7.1.1(@types/node@24.2.1))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - msw: 2.10.4(@types/node@22.8.1)(typescript@5.9.2) - vite: 7.1.1(@types/node@22.8.1) + msw: 2.10.4(@types/node@24.2.1)(typescript@5.9.2) + vite: 7.1.1(@types/node@24.2.1) '@vitest/pretty-format@2.1.3': dependencies: @@ -3347,7 +3399,7 @@ snapshots: '@vitest/utils@2.1.3': dependencies: '@vitest/pretty-format': 2.1.3 - loupe: 3.1.3 + loupe: 3.2.0 tinyrainbow: 1.2.0 '@vitest/utils@3.2.4': @@ -3946,12 +3998,12 @@ snapshots: ms@2.1.3: {} - msw@2.10.4(@types/node@22.8.1)(typescript@5.9.2): + msw@2.10.4(@types/node@24.2.1)(typescript@5.9.2): dependencies: '@bundled-es-modules/cookie': 2.0.1 '@bundled-es-modules/statuses': 1.0.1 '@bundled-es-modules/tough-cookie': 0.1.6 - '@inquirer/confirm': 5.0.1(@types/node@22.8.1) + '@inquirer/confirm': 5.0.1(@types/node@24.2.1) '@mswjs/interceptors': 0.39.5 '@open-draft/deferred-promise': 2.2.0 '@open-draft/until': 2.1.0 @@ -4288,7 +4340,7 @@ snapshots: typescript@5.9.2: {} - undici-types@6.19.8: {} + undici-types@7.10.0: {} universalify@0.2.0: {} @@ -4322,13 +4374,13 @@ snapshots: optionalDependencies: '@types/react': 19.1.9 - vite-node@3.2.4(@types/node@22.8.1): + vite-node@3.2.4(@types/node@24.2.1): dependencies: cac: 6.7.14 debug: 4.4.1 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.1.1(@types/node@22.8.1) + vite: 7.1.1(@types/node@24.2.1) transitivePeerDependencies: - '@types/node' - jiti @@ -4343,7 +4395,7 @@ snapshots: - tsx - yaml - vite@7.1.1(@types/node@22.8.1): + vite@7.1.1(@types/node@24.2.1): dependencies: esbuild: 0.25.3 fdir: 6.4.6(picomatch@4.0.3) @@ -4352,24 +4404,24 @@ snapshots: rollup: 4.46.2 tinyglobby: 0.2.14 optionalDependencies: - '@types/node': 22.8.1 + '@types/node': 24.2.1 fsevents: 2.3.3 vitest-browser-react@1.0.1(@types/react-dom@19.1.7(@types/react@19.1.9))(@types/react@19.1.9)(@vitest/browser@2.1.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(vitest@3.2.4): dependencies: - '@vitest/browser': 2.1.3(@types/node@22.8.1)(@vitest/spy@3.2.4)(typescript@5.9.2)(vite@7.1.1(@types/node@22.8.1))(vitest@3.2.4) + '@vitest/browser': 2.1.3(@types/node@24.2.1)(@vitest/spy@3.2.4)(typescript@5.9.2)(vite@7.1.1(@types/node@24.2.1))(vitest@3.2.4) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) - vitest: 3.2.4(@types/node@22.8.1)(@vitest/browser@2.1.3)(jsdom@26.1.0)(msw@2.10.4(@types/node@22.8.1)(typescript@5.9.2)) + vitest: 3.2.4(@types/node@24.2.1)(@vitest/browser@2.1.3)(jsdom@26.1.0)(msw@2.10.4(@types/node@24.2.1)(typescript@5.9.2)) optionalDependencies: '@types/react': 19.1.9 '@types/react-dom': 19.1.7(@types/react@19.1.9) - vitest@3.2.4(@types/node@22.8.1)(@vitest/browser@2.1.3)(jsdom@26.1.0)(msw@2.10.4(@types/node@22.8.1)(typescript@5.9.2)): + vitest@3.2.4(@types/node@24.2.1)(@vitest/browser@2.1.3)(jsdom@26.1.0)(msw@2.10.4(@types/node@24.2.1)(typescript@5.9.2)): dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(msw@2.10.4(@types/node@22.8.1)(typescript@5.9.2))(vite@7.1.1(@types/node@22.8.1)) + '@vitest/mocker': 3.2.4(msw@2.10.4(@types/node@24.2.1)(typescript@5.9.2))(vite@7.1.1(@types/node@24.2.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -4387,12 +4439,12 @@ snapshots: tinyglobby: 0.2.14 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.1.1(@types/node@22.8.1) - vite-node: 3.2.4(@types/node@22.8.1) + vite: 7.1.1(@types/node@24.2.1) + vite-node: 3.2.4(@types/node@24.2.1) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 22.8.1 - '@vitest/browser': 2.1.3(@types/node@22.8.1)(@vitest/spy@3.2.4)(typescript@5.9.2)(vite@7.1.1(@types/node@22.8.1))(vitest@3.2.4) + '@types/node': 24.2.1 + '@vitest/browser': 2.1.3(@types/node@24.2.1)(@vitest/spy@3.2.4)(typescript@5.9.2)(vite@7.1.1(@types/node@24.2.1))(vitest@3.2.4) jsdom: 26.1.0 transitivePeerDependencies: - jiti @@ -4473,3 +4525,8 @@ snapshots: yocto-queue@0.1.0: {} yoctocolors-cjs@2.1.2: {} + + zustand@5.0.7(@types/react@19.1.9)(react@19.1.1): + optionalDependencies: + '@types/react': 19.1.9 + react: 19.1.1 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 000000000..a817d1c60 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,8 @@ +packages: + - '.' + +ignoredBuiltDependencies: + - esbuild + +onlyBuiltDependencies: + - '@swc/core' diff --git a/public/404.html b/public/404.html new file mode 100644 index 000000000..a90fd3c6e --- /dev/null +++ b/public/404.html @@ -0,0 +1,33 @@ + + + + + Posts Manager + + + + diff --git a/src/App.tsx b/src/App.tsx index 0c0032aab..a78257e3b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,20 +1,38 @@ -import { BrowserRouter as Router } from "react-router-dom" -import Header from "./components/Header.tsx" -import Footer from "./components/Footer.tsx" -import PostsManagerPage from "./pages/PostsManagerPage.tsx" +import { BrowserRouter as Router } from 'react-router-dom'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; +import { Header } from './widgets/Header'; +import { Footer } from './widgets/Footer'; +import PostsManagerPage from './pages/PostsManagerPage.tsx'; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: 5 * 60 * 1000, // 5분 + gcTime: 10 * 60 * 1000, // 10분 + retry: 1, + }, + mutations: { + retry: 1, + }, + }, +}); const App = () => { return ( - -
-
-
- -
-
-
- ) -} + + +
+
+
+ +
+
+
+
+ +
+ ); +}; -export default App +export default App; diff --git a/src/components/index.tsx b/src/components/index.tsx deleted file mode 100644 index 8495817d3..000000000 --- a/src/components/index.tsx +++ /dev/null @@ -1,214 +0,0 @@ -import * as React from "react" -import { forwardRef } from "react" -import * as SelectPrimitive from "@radix-ui/react-select" -import * as DialogPrimitive from "@radix-ui/react-dialog" -import { Check, ChevronDown, X } from "lucide-react" -import { cva, VariantProps } from "class-variance-authority" - -const buttonVariants = cva( - "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background", - { - variants: { - variant: { - default: "bg-blue-500 text-white hover:bg-blue-600", - destructive: "bg-red-500 text-white hover:bg-red-600", - outline: "border border-gray-300 bg-transparent text-gray-700 hover:bg-gray-100", - secondary: "bg-gray-200 text-gray-800 hover:bg-gray-300", - ghost: "bg-transparent text-gray-700 hover:bg-gray-100", - link: "underline-offset-4 hover:underline text-blue-500", - }, - size: { - default: "h-10 py-2 px-4", - sm: "h-8 px-3 rounded-md text-xs", - lg: "h-11 px-8 rounded-md", - icon: "h-9 w-9", - }, - }, - defaultVariants: { - variant: "default", - size: "default", - }, - }, -) - -interface ButtonProps extends React.ButtonHTMLAttributes, VariantProps { - className?: string -} - -export const Button = forwardRef(({ className, variant, size, ...props }, ref) => { - return