From 2e3eea409960688ae870aa4fc1da066fc858a415 Mon Sep 17 00:00:00 2001 From: qowjdals23 Date: Tue, 6 Jan 2026 01:48:11 +0900 Subject: [PATCH 01/10] =?UTF-8?q?init:=20husky=20=EC=B4=88=EA=B8=B0=20?= =?UTF-8?q?=EC=84=B8=ED=8C=85=20(#16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .husky/pre-commit | 1 + package.json | 14 ++- pnpm-lock.yaml | 278 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 290 insertions(+), 3 deletions(-) create mode 100644 .husky/pre-commit diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 00000000..dff836df --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +pnpm exec lint-staged \ No newline at end of file diff --git a/package.json b/package.json index fd06cffb..7ce2da02 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,17 @@ "dev": "vite", "build": "tsc -b && vite build", "lint": "eslint .", - "preview": "vite preview" + "preview": "vite preview", + "prepare": "husky" + }, + "lint-staged": { + "*.{ts,tsx,js,jsx}": [ + "eslint --fix", + "prettier --write" + ], + "*.{json,md,css}": [ + "prettier --write" + ] }, "dependencies": { "eslint-import-resolver-typescript": "^4.4.4", @@ -31,6 +41,8 @@ "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-refresh": "^0.4.24", "globals": "^16.5.0", + "husky": "^9.1.7", + "lint-staged": "^16.2.7", "prettier": "3.7.4", "typescript": "~5.9.3", "typescript-eslint": "^8.46.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5c29e85c..0af24a6b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -66,6 +66,12 @@ importers: globals: specifier: ^16.5.0 version: 16.5.0 + husky: + specifier: ^9.1.7 + version: 9.1.7 + lint-staged: + specifier: ^16.2.7 + version: 16.2.7 prettier: specifier: 3.7.4 version: 3.7.4 @@ -867,10 +873,22 @@ packages: ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ansi-escapes@7.2.0: + resolution: {integrity: sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==} + engines: {node: '>=18'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -930,6 +948,10 @@ packages: brace-expansion@2.0.2: resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + browserslist@4.28.1: resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -962,6 +984,14 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-truncate@5.1.1: + resolution: {integrity: sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A==} + engines: {node: '>=20'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -969,6 +999,13 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + commander@14.0.2: + resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==} + engines: {node: '>=20'} + commander@7.2.0: resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} engines: {node: '>= 10'} @@ -1085,10 +1122,17 @@ packages: electron-to-chromium@1.5.267: resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} + emoji-regex@10.6.0: + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} @@ -1275,6 +1319,9 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -1300,6 +1347,10 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -1338,6 +1389,10 @@ packages: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} + get-east-asian-width@1.4.0: + resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==} + engines: {node: '>=18'} + get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -1406,6 +1461,11 @@ packages: hermes-parser@0.25.1: resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + husky@9.1.7: + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} + engines: {node: '>=18'} + hasBin: true + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -1472,6 +1532,10 @@ packages: resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} engines: {node: '>= 0.4'} + is-fullwidth-code-point@5.1.0: + resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} + engines: {node: '>=18'} + is-generator-function@1.1.2: resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} engines: {node: '>= 0.4'} @@ -1492,6 +1556,10 @@ packages: resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} engines: {node: '>= 0.4'} + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -1589,6 +1657,15 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + lint-staged@16.2.7: + resolution: {integrity: sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow==} + engines: {node: '>=20.17'} + hasBin: true + + listr2@9.0.5: + resolution: {integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==} + engines: {node: '>=20.0.0'} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -1596,6 +1673,10 @@ packages: lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -1616,6 +1697,14 @@ packages: mdn-data@2.0.30: resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -1629,6 +1718,10 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + nano-spawn@2.0.0: + resolution: {integrity: sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw==} + engines: {node: '>=20.17'} + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -1683,6 +1776,10 @@ packages: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -1725,10 +1822,19 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + picomatch@4.0.3: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + pidtree@0.6.0: + resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} + engines: {node: '>=0.10'} + hasBin: true + possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -1793,6 +1899,13 @@ packages: resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} hasBin: true + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + rollup@4.54.0: resolution: {integrity: sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -1858,6 +1971,14 @@ packages: resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + slice-ansi@7.1.2: + resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} + engines: {node: '>=18'} + snake-case@3.0.4: resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} @@ -1873,6 +1994,18 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string-width@8.1.0: + resolution: {integrity: sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==} + engines: {node: '>=20'} + string.prototype.matchall@4.0.12: resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} engines: {node: '>= 0.4'} @@ -1892,6 +2025,10 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} + engines: {node: '>=12'} + strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} @@ -1924,6 +2061,10 @@ packages: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + ts-api-utils@2.3.0: resolution: {integrity: sha512-6eg3Y9SF7SsAvGzRHQvvc1skDAhwI4YQ32ui1scxD1Ccr0G5qIIbUBT3pFTKX8kmWIQClHobtUdNuaBgwdfdWg==} engines: {node: '>=18.12'} @@ -2057,6 +2198,10 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wrap-ansi@9.0.2: + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} + engines: {node: '>=18'} + yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} @@ -2764,10 +2909,18 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ansi-escapes@7.2.0: + dependencies: + environment: 1.1.0 + + ansi-regex@6.2.2: {} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 + ansi-styles@6.2.3: {} + argparse@2.0.1: {} array-buffer-byte-length@1.0.2: @@ -2858,6 +3011,10 @@ snapshots: dependencies: balanced-match: 1.0.2 + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + browserslist@4.28.1: dependencies: baseline-browser-mapping: 2.9.11 @@ -2894,12 +3051,25 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-truncate@5.1.1: + dependencies: + slice-ansi: 7.1.2 + string-width: 8.1.0 + color-convert@2.0.1: dependencies: color-name: 1.1.4 color-name@1.1.4: {} + colorette@2.0.20: {} + + commander@14.0.2: {} + commander@7.2.0: {} concat-map@0.0.1: {} @@ -3024,8 +3194,12 @@ snapshots: electron-to-chromium@1.5.267: {} + emoji-regex@10.6.0: {} + entities@4.5.0: {} + environment@1.1.0: {} + error-ex@1.3.4: dependencies: is-arrayish: 0.2.1 @@ -3354,6 +3528,8 @@ snapshots: esutils@2.0.3: {} + eventemitter3@5.0.1: {} + fast-deep-equal@3.1.3: {} fast-diff@1.3.0: {} @@ -3370,6 +3546,10 @@ snapshots: dependencies: flat-cache: 4.0.1 + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -3406,6 +3586,8 @@ snapshots: gensync@1.0.0-beta.2: {} + get-east-asian-width@1.4.0: {} + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -3477,6 +3659,8 @@ snapshots: dependencies: hermes-estree: 0.25.1 + husky@9.1.7: {} + ignore@5.3.2: {} ignore@7.0.5: {} @@ -3546,6 +3730,10 @@ snapshots: dependencies: call-bound: 1.0.4 + is-fullwidth-code-point@5.1.0: + dependencies: + get-east-asian-width: 1.4.0 + is-generator-function@1.1.2: dependencies: call-bound: 1.0.4 @@ -3567,6 +3755,8 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 + is-number@7.0.0: {} + is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -3662,12 +3852,39 @@ snapshots: lines-and-columns@1.2.4: {} + lint-staged@16.2.7: + dependencies: + commander: 14.0.2 + listr2: 9.0.5 + micromatch: 4.0.8 + nano-spawn: 2.0.0 + pidtree: 0.6.0 + string-argv: 0.3.2 + yaml: 2.8.2 + + listr2@9.0.5: + dependencies: + cli-truncate: 5.1.1 + colorette: 2.0.20 + eventemitter3: 5.0.1 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 9.0.2 + locate-path@6.0.0: dependencies: p-locate: 5.0.0 lodash.merge@4.6.2: {} + log-update@6.1.0: + dependencies: + ansi-escapes: 7.2.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.2 + strip-ansi: 7.1.2 + wrap-ansi: 9.0.2 + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -3686,6 +3903,13 @@ snapshots: mdn-data@2.0.30: {} + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mimic-function@5.0.1: {} + minimatch@3.1.2: dependencies: brace-expansion: 1.1.12 @@ -3698,6 +3922,8 @@ snapshots: ms@2.1.3: {} + nano-spawn@2.0.0: {} + nanoid@3.3.11: {} napi-postinstall@0.3.4: {} @@ -3757,6 +3983,10 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -3801,8 +4031,12 @@ snapshots: picocolors@1.1.1: {} + picomatch@2.3.1: {} + picomatch@4.0.3: {} + pidtree@0.6.0: {} + possible-typed-array-names@1.1.0: {} postcss@8.5.6: @@ -3872,6 +4106,13 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + rfdc@1.4.1: {} + rollup@4.54.0: dependencies: '@types/estree': 1.0.8 @@ -3981,6 +4222,13 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 + signal-exit@4.1.0: {} + + slice-ansi@7.1.2: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + snake-case@3.0.4: dependencies: dot-case: 3.0.4 @@ -3995,6 +4243,19 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 + string-argv@0.3.2: {} + + string-width@7.2.0: + dependencies: + emoji-regex: 10.6.0 + get-east-asian-width: 1.4.0 + strip-ansi: 7.1.2 + + string-width@8.1.0: + dependencies: + get-east-asian-width: 1.4.0 + strip-ansi: 7.1.2 + string.prototype.matchall@4.0.12: dependencies: call-bind: 1.0.8 @@ -4039,6 +4300,10 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + strip-ansi@7.1.2: + dependencies: + ansi-regex: 6.2.2 + strip-bom@3.0.0: {} strip-json-comments@3.1.1: {} @@ -4070,6 +4335,10 @@ snapshots: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + ts-api-utils@2.3.0(typescript@5.9.3): dependencies: typescript: 5.9.3 @@ -4248,10 +4517,15 @@ snapshots: word-wrap@1.2.5: {} + wrap-ansi@9.0.2: + dependencies: + ansi-styles: 6.2.3 + string-width: 7.2.0 + strip-ansi: 7.1.2 + yallist@3.1.1: {} - yaml@2.8.2: - optional: true + yaml@2.8.2: {} yocto-queue@0.1.0: {} From 3a8b44fa9c5df839515391450b85bbea041c86e1 Mon Sep 17 00:00:00 2001 From: qowjdals23 Date: Tue, 6 Jan 2026 03:29:03 +0900 Subject: [PATCH 02/10] =?UTF-8?q?chore:=20pre-commit=20=ED=9B=85=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=20=EC=B6=94=EA=B0=80=20(#16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .husky/pre-commit | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.husky/pre-commit b/.husky/pre-commit index dff836df..382c07ee 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1 +1,2 @@ -pnpm exec lint-staged \ No newline at end of file +echo "๐Ÿถ Husky pre-commit ์‹คํ–‰" +pnpm exec lint-staged && echo "๐ŸŽ‰ lint-staged ํ†ต๊ณผ" From 24159bd1d94c707aaf54f9809fea3e72c70dd4a1 Mon Sep 17 00:00:00 2001 From: qowjdals23 Date: Wed, 7 Jan 2026 23:47:38 +0900 Subject: [PATCH 03/10] =?UTF-8?q?chore:=20ESLint=20=EA=B2=BD=EA=B3=A0=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/main.tsx | 5 ++++- src/pages/home/home-page.tsx | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/app/main.tsx b/src/app/main.tsx index 8804886d..9c727442 100644 --- a/src/app/main.tsx +++ b/src/app/main.tsx @@ -3,7 +3,10 @@ import { createRoot } from "react-dom/client"; import App from "@app/App"; -createRoot(document.getElementById("root")!).render( +const rootEl = document.getElementById("root"); +if (!rootEl) throw new Error('Root element "#root" not found'); + +createRoot(rootEl).render( diff --git a/src/pages/home/home-page.tsx b/src/pages/home/home-page.tsx index 71d0dcf0..0be03466 100644 --- a/src/pages/home/home-page.tsx +++ b/src/pages/home/home-page.tsx @@ -1,12 +1,12 @@ +import Heart from "@icons/heart.svg?react"; import KERORO from "@images/comfit_web_status.jpg"; -import Heart from "@icons/heart.svg?react" const HomePage = () => { return (

Welcome to the Home Page

Keroro - +
); }; From 33d5e296c44e34ab89afb0b726eb934b7cd009a7 Mon Sep 17 00:00:00 2001 From: qowjdals23 Date: Thu, 8 Jan 2026 16:58:42 +0900 Subject: [PATCH 04/10] =?UTF-8?q?chore:=20pre-commit=20=ED=9B=85=20?= =?UTF-8?q?=EC=A2=85=EB=A3=8C=20=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?(#16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .husky/pre-commit | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.husky/pre-commit b/.husky/pre-commit index 382c07ee..cdd0c95b 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,2 +1,5 @@ +#!/bin/sh + echo "๐Ÿถ Husky pre-commit ์‹คํ–‰" -pnpm exec lint-staged && echo "๐ŸŽ‰ lint-staged ํ†ต๊ณผ" +pnpm exec lint-staged || exit 1 +echo "๐ŸŽ‰ lint-staged ํ†ต๊ณผ" \ No newline at end of file From 7e9c98bc9091b5e3e0c38eb1ac95c71d06ccb76f Mon Sep 17 00:00:00 2001 From: qowjdals23 Date: Thu, 8 Jan 2026 17:06:50 +0900 Subject: [PATCH 05/10] =?UTF-8?q?chore:=20=EC=BB=A4=EB=B0=8B=20=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=20=EC=BB=A8=EB=B2=A4=EC=85=98=20=EA=B2=80?= =?UTF-8?q?=EC=82=AC=20=ED=9B=85=20commit-msg=20=EC=B6=94=EA=B0=80=20(#16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .husky/commit-msg | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .husky/commit-msg diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100644 index 00000000..f83bbf1e --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1,20 @@ + +#!/bin/sh + +COMMIT_MSG_FILE=$1 +COMMIT_MSG_HEADER=$(grep -v '^\s*#' "$COMMIT_MSG_FILE" | head -n 1) + +# Merge๋‚˜ Revert ์ปค๋ฐ‹์€ ๊ฒ€์‚ฌํ•˜์ง€ ์•Š๊ณ  ํ†ต๊ณผ์‹œํ‚ด +if echo "$COMMIT_MSG_HEADER" | grep -Eq "^(Merge|Revert)"; then + exit 0 +fi + +PATTERN='^(init|feat|fix|design|update|remove|add|move|rename|docs|comment|refactor|test|chore|deploy|): .+ \(#[0-9]+\)$' + +# ์ปค๋ฐ‹ ์ปจ๋ฒค์…˜๊ณผ ์ผ์น˜ํ•˜๋Š”์ง€ ์ฒดํฌ +if ! echo "$COMMIT_MSG_HEADER" | grep -Eq "$PATTERN"; then + echo "๐Ÿšจ [์ปค๋ฐ‹ ๋ฉ”์‹œ์ง€ ์—๋Ÿฌ] ํ˜•์‹์ด ์ปจ๋ฒค์…˜๊ณผ ๋‹ค๋ฆ…๋‹ˆ๋‹ค." + echo "๐Ÿ‘‰ ๊ทœ์น™: <ํƒ€์ž…>: <๋‚ด์šฉ> (#<์ด์Šˆ๋ฒˆํ˜ธ>)" + echo "๐Ÿ‘‰ ์˜ˆ์‹œ: feat: ๊ณตํ†ต ์ปดํฌ๋„ŒํŠธ ํผ๋ธ”๋ฆฌ์‹ฑ (#1)" + exit 1 +fi \ No newline at end of file From d9f00add5646e6980bf2d2d742d35aa0d78d4f47 Mon Sep 17 00:00:00 2001 From: qowjdals23 Date: Thu, 8 Jan 2026 17:56:26 +0900 Subject: [PATCH 06/10] =?UTF-8?q?chore:=20lint-staged=20=ED=8F=AC=EB=A7=B7?= =?UTF-8?q?=20=EB=8C=80=EC=83=81=20=ED=99=95=EC=9E=A5=20(#16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7ce2da02..fec86b13 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "eslint --fix", "prettier --write" ], - "*.{json,md,css}": [ + "*.{json,md,mdx,css,scss,yml,yaml}": [ "prettier --write" ] }, From d195bef52a5e55cf6f09ae91f59d67a826bbad5f Mon Sep 17 00:00:00 2001 From: qowjdals23 Date: Thu, 8 Jan 2026 18:18:32 +0900 Subject: [PATCH 07/10] =?UTF-8?q?fix:=20=EC=B6=A9=EB=8F=8C=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0=20(#16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 33a78dbe..224a6053 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,9 @@ "build": "tsc -b && vite build", "lint": "eslint .", "preview": "vite preview", - "prepare": "husky" + "prepare": "husky", + "storybook": "storybook dev -p 6006", + "build-storybook": "storybook build" }, "lint-staged": { "*.{ts,tsx,js,jsx}": [ @@ -17,9 +19,7 @@ ], "*.{json,md,mdx,css,scss,yml,yaml}": [ "prettier --write" - ], - "storybook": "storybook dev -p 6006", - "build-storybook": "storybook build" + ] }, "dependencies": { "eslint-import-resolver-typescript": "^4.4.4", From 3cfcd6da30d5e5f4060f037d9f2deb0412a8cc7c Mon Sep 17 00:00:00 2001 From: qowjdals23 Date: Thu, 8 Jan 2026 18:27:21 +0900 Subject: [PATCH 08/10] =?UTF-8?q?fix:=20=EC=8A=A4=ED=86=A0=EB=A6=AC?= =?UTF-8?q?=EB=B6=81=20=ED=8C=8C=EC=9D=BC=20=EC=82=AD=EC=A0=9C=20=EB=B3=B5?= =?UTF-8?q?=EA=B5=AC=20(#16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +- .storybook/main.ts | 28 ++++ .storybook/preview.ts | 22 ++++ eslint.config.js | 2 + src/shared/stories/Introduction.mdx | 164 ++++++++++++++++++++++++ src/shared/ui/button/Button.stories.tsx | 57 ++++++++ src/shared/ui/button/Button.tsx | 35 +++++ 7 files changed, 311 insertions(+), 1 deletion(-) create mode 100644 .storybook/main.ts create mode 100644 .storybook/preview.ts create mode 100644 src/shared/stories/Introduction.mdx create mode 100644 src/shared/ui/button/Button.stories.tsx create mode 100644 src/shared/ui/button/Button.tsx diff --git a/.gitignore b/.gitignore index 182d11ec..1e6092c6 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,6 @@ dist-ssr *.sln *.sw? -.env \ No newline at end of file +.env +*storybook.log +storybook-static diff --git a/.storybook/main.ts b/.storybook/main.ts new file mode 100644 index 00000000..822ee99f --- /dev/null +++ b/.storybook/main.ts @@ -0,0 +1,28 @@ +import { mergeConfig } from "vite"; +import tsconfigPaths from "vite-tsconfig-paths"; + +import type { StorybookConfig } from "@storybook/react-vite"; + +const config: StorybookConfig = { + stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"], + + addons: ["@storybook/addon-docs", "@storybook/addon-a11y"], + + framework: "@storybook/react-vite", + + viteFinal: async (storybookConfig) => + mergeConfig(storybookConfig, { + plugins: [tsconfigPaths()], + }), + + typescript: { + reactDocgen: "react-docgen-typescript", + reactDocgenTypescriptOptions: { + shouldExtractLiteralValuesFromEnum: true, + shouldRemoveUndefinedFromOptional: true, + propFilter: (prop) => !/node_modules/.test(prop.parent?.fileName || ""), + }, + }, +}; + +export default config; diff --git a/.storybook/preview.ts b/.storybook/preview.ts new file mode 100644 index 00000000..4a213cb5 --- /dev/null +++ b/.storybook/preview.ts @@ -0,0 +1,22 @@ +import type { Preview } from "@storybook/react-vite"; + +// ๋‚˜์ค‘์— ์ „์—ญ ์Šคํƒ€์ผ ์—ฌ๊ธฐ์„œ import +// vanilla-extract์˜ globalStyle / theme entry ์—ฌ๊ธฐ์„œ ๋ถˆ๋Ÿฌ์˜ด +// ์˜ˆ: import '@/shared/styles/global.css.ts'; + +const preview: Preview = { + tags: ["autodocs"], + + parameters: { + layout: "padded", + + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/i, + }, + }, + }, +}; + +export default preview; diff --git a/eslint.config.js b/eslint.config.js index da88192d..92579f98 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,3 +1,5 @@ +// For more info, see https://github.com/storybookjs/eslint-plugin-storybook#configuration-flat-config-format +import storybook from "eslint-plugin-storybook"; import js from "@eslint/js"; import globals from "globals"; import reactHooks from "eslint-plugin-react-hooks"; diff --git a/src/shared/stories/Introduction.mdx b/src/shared/stories/Introduction.mdx new file mode 100644 index 00000000..e600c67d --- /dev/null +++ b/src/shared/stories/Introduction.mdx @@ -0,0 +1,164 @@ +# COMFIT Storybook + +> COMFIT ๋””์ž์ธ ์‹œ์Šคํ…œ / ๊ณต์šฉ UI๋ฅผ ๋ฌธ์„œํ™”ํ•˜๊ณ , ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋…๋ฆฝ์ ์œผ๋กœ ๊ฐœ๋ฐœยท๊ฒ€์ฆํ•˜๊ธฐ ์œ„ํ•œ ๊ณต๊ฐ„์ž…๋‹ˆ๋‹ค. + +COMFIT Storybook์€ +**๋””์ž์ธ ์‹œ์Šคํ…œ์„ ์ฝ”๋“œ ๋‹จ์œ„๋กœ ๊ด€๋ฆฌ**ํ•˜๊ณ , +**ํŒ€์› ๊ฐ„ UI ์ปจ๋ฒค์…˜์„ ๊ณต์œ **ํ•˜๋ฉฐ, +**๋ณ€๊ฒฝ ์‚ฌํ•ญ์˜ ์˜ํ–ฅ ๋ฒ”์œ„๋ฅผ ๋น ๋ฅด๊ฒŒ ํ™•์ธ**ํ•˜๊ธฐ ์œ„ํ•œ ๋ชฉ์ ์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. + +--- + +## Storybook์ด๋ž€? + +Storybook์€ UI ์ปดํฌ๋„ŒํŠธ๋ฅผ +**ํŽ˜์ด์ง€์™€ ๋ถ„๋ฆฌํ•ด์„œ ๋…๋ฆฝ์ ์œผ๋กœ ๊ฐœ๋ฐœ ยท ํ…Œ์ŠคํŠธ ยท ๋ฌธ์„œํ™”**ํ•  ์ˆ˜ ์žˆ๋Š” ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. + +COMFIT ํŒ€์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ด์œ ๋กœ Storybook์„ ๋„์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค. + +- ๐Ÿงฉ **์ปดํฌ๋„ŒํŠธ ๋‹จ์œ„ ๊ฐœ๋ฐœ** + - ํŽ˜์ด์ง€ ๊ตฌํ˜„ ์ „์—๋„ UI ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‹จ๋…์œผ๋กœ ๊ฐœ๋ฐœ ๊ฐ€๋Šฅ + - ์ƒํƒœ(variant / size / disabled ๋“ฑ)๋ฅผ ๋ช…ํ™•ํžˆ ๋ถ„๋ฆฌํ•ด ๊ด€๋ฆฌ + +- ๐Ÿ“š **๋ฌธ์„œํ™”** + - ์ปดํฌ๋„ŒํŠธ Props, ์‚ฌ์šฉ ์˜ˆ์‹œ, ์ƒํƒœ๋ฅผ ํ•œ ๊ณณ์—์„œ ํ™•์ธ + - ์‹ ๊ทœ ํŒ€์› ์˜จ๋ณด๋”ฉ ์‹œ UI ์ดํ•ด ๋น„์šฉ ๊ฐ์†Œ + +- โœ… **๊ฒ€์ฆ** + - ๋‹ค์–‘ํ•œ ์ƒํƒœ๋ฅผ Story ๋‹จ์œ„๋กœ ๊ณ ์ •ํ•ด๋‘์–ด + - UI ๋ณ€๊ฒฝ ์‹œ ์‚ฌ์ด๋“œ ์ดํŽ™ํŠธ ๋น ๋ฅด๊ฒŒ ํ™•์ธ ๊ฐ€๋Šฅ + +--- + +## COMFIT์—์„œ Storybook์„ ์‚ฌ์šฉํ•˜๋Š” ๋ชฉ์  + +COMFIT ํ”„๋กœ์ ํŠธ๋Š” +ํŽ˜์ด์ง€ ๋‹จ์œ„๋ณด๋‹ค **๊ณต์šฉ UI / ๋””์ž์ธ ์ผ๊ด€์„ฑ**์ด ์ค‘์š”ํ•œ ์„œ๋น„์Šค์ž…๋‹ˆ๋‹ค. + +Storybook์€ ๋‹จ์ˆœํ•œ UI ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๋„๊ตฌ๊ฐ€ ์•„๋‹ˆ๋ผ, +๋‹ค์Œ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. + +### 1๏ธโƒฃ ๋””์ž์ธ ์‹œ์Šคํ…œ์˜ ๊ธฐ์ค€์  + +- Button, Input, Modal ๋“ฑ ๊ณต์šฉ UI์˜ **๋‹จ์ผ ๊ธฐ์ค€(Single Source of Truth)** +- โ€œ์ด ๋ฒ„ํŠผ์€ ์–ด๋–ค variant๊ฐ€ ์žˆ๋Š”์ง€?โ€๋ฅผ ์ฝ”๋“œ๋กœ ๋ช…ํ™•ํžˆ ์ •์˜ + +### 2๏ธโƒฃ ํ˜‘์—…์„ ์œ„ํ•œ ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜ ๋„๊ตฌ + +- ๋””์ž์ด๋„ˆ โ†” ํ”„๋ก ํŠธ์—”๋“œ ๊ฐ„ UI ๋…ผ์˜ ๊ธฐ์ค€ +- โ€œ์ด ์ปดํฌ๋„ŒํŠธ๋Š” Storybook ๊ธฐ์ค€์œผ๋กœ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹คโ€๋ผ๋Š” ๊ณตํ†ต ํ•ฉ์˜ + +### 3๏ธโƒฃ ๋ณ€๊ฒฝ ์˜ํ–ฅ ๋ฒ”์œ„ ํ™•์ธ + +- ๊ณต์šฉ ์ปดํฌ๋„ŒํŠธ ์ˆ˜์ • ์‹œ + - ์–ด๋–ค ์ƒํƒœ๊ฐ€ ๊นจ์ง€๋Š”์ง€ ์ฆ‰์‹œ ํ™•์ธ ๊ฐ€๋Šฅ +- ํŽ˜์ด์ง€ QA ์ „์— UI ๋ ˆ๋ฒจ์—์„œ 1์ฐจ ๊ฒ€์ฆ + +--- + +## ํ”„๋กœ์ ํŠธ ์•„ํ‚คํ…์ฒ˜ (Feature-Sliced Design) + +COMFIT์€ **Feature-Sliced Design(FSD)** ์•„ํ‚คํ…์ฒ˜๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๊ตฌ์กฐ๋ฅผ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค. + +```txt +src/ +โ”œโ”€โ”€ app/ # ์•ฑ ์ดˆ๊ธฐํ™” / ์ „์—ญ ์„ค์ • +โ”œโ”€โ”€ pages/ # ๋ผ์šฐํŒ… ๋‹จ์œ„ ํŽ˜์ด์ง€ +โ”œโ”€โ”€ widgets/ # ์—ฌ๋Ÿฌ feature๋ฅผ ์กฐํ•ฉํ•œ UI ๋ธ”๋ก +โ”œโ”€โ”€ features/ # ๊ธฐ๋Šฅ ๋‹จ์œ„ ๋กœ์ง/์ปดํฌ๋„ŒํŠธ +โ””โ”€โ”€ shared/ # ๊ณต์šฉ UI / ์œ ํ‹ธ / ํƒ€์ž… +``` + +## ๐Ÿ“ฆ Storybook๊ณผ FSD์˜ ๊ด€๊ณ„ + +COMFIT Storybook์€ **Feature-Sliced Design(FSD)** ๊ตฌ์กฐ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ˜์˜ํ•˜์—ฌ +์–ด๋–ค UI๋ฅผ ๋ฌธ์„œํ™”ํ• ์ง€์— ๋Œ€ํ•œ ๊ธฐ์ค€์„ ๋ช…ํ™•ํ•˜๊ฒŒ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. + +### shared + +- **๊ฐ€์žฅ ์šฐ์„ ์ ์œผ๋กœ Storybook ๋ฌธ์„œํ™” ๋Œ€์ƒ** +- ์—ฌ๋Ÿฌ ํŽ˜์ด์ง€/๊ธฐ๋Šฅ์—์„œ ๊ณตํ†ต์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” UI ์ปดํฌ๋„ŒํŠธ +- ์˜ˆ์‹œ: + - Button + - Input + - Text + - Badge + +> shared ๋ ˆ์ด์–ด์˜ ์ปดํฌ๋„ŒํŠธ๋Š” +> **๋ฐ˜๋“œ์‹œ Storybook์„ ํ†ตํ•ด ์‚ฌ์šฉ ๋ฐฉ๋ฒ•๊ณผ ์ƒํƒœ๋ฅผ ์ •์˜**ํ•˜๋Š” ๊ฒƒ์„ ๋ชฉํ‘œ๋กœ ํ•ฉ๋‹ˆ๋‹ค. + +--- + +### features / widgets + +- **ํ•„์š” ์‹œ ๋ฌธ์„œํ™”** +- ์žฌ์‚ฌ์šฉ๋˜๊ฑฐ๋‚˜ ๊ตฌ์กฐ๊ฐ€ ๋ณต์žกํ•œ UI ๋ธ”๋ก +- ํŒ€ ๋‚ด์—์„œ ๊ณตํ†ต ์ดํ•ด๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ Storybook ๋Œ€์ƒ + +> ๋‹จ์ˆœํžˆ ํŠน์ • ํŽ˜์ด์ง€์—์„œ๋งŒ ์‚ฌ์šฉํ•˜๋Š” UI๋Š” +> Storybook ๋ฌธ์„œํ™” ๋Œ€์ƒ์ด ์•„๋‹™๋‹ˆ๋‹ค. + +--- + +## ๐Ÿ“˜ Story ์ž‘์„ฑ ๊ทœ์น™ (๊ฐ€์ด๋“œ) + +### ๐Ÿ“ Story ์œ„์น˜ + +Story ํŒŒ์ผ์€ **์ปดํฌ๋„ŒํŠธ์™€ ๋™์ผํ•œ ๋””๋ ‰ํ† ๋ฆฌ**์— ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. + +```txt +src/shared/ui/button/ +โ”œโ”€โ”€ Button.tsx +โ””โ”€โ”€ Button.stories.tsx +``` + +### ๐Ÿ“ Story ํŒŒ์ผ ๊ทœ์น™ + +- `.stories.tsx` ํŒŒ์ผ์€ ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ์˜ **์‚ฌ์šฉ ์˜ˆ์‹œ์™€ ์ƒํƒœ**๋ฅผ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. +- Storybook ์ „์šฉ ๋””๋ ‰ํ† ๋ฆฌ๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉฐ, + **์ปดํฌ๋„ŒํŠธ์™€ ๋™์ผํ•œ ๋””๋ ‰ํ† ๋ฆฌ**์— Story ํŒŒ์ผ์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. + +--- + +### ๐Ÿท๏ธ Story ๋„ค์ด๋ฐ ์ปจ๋ฒค์…˜ + +```ts +title: "Shared/Button"; +``` + +- `Shared / Feature / Widget` ๊ธฐ์ค€์œผ๋กœ ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. +- Storybook ์‚ฌ์ด๋“œ๋ฐ” ๊ตฌ์กฐ๊ฐ€ **FSD ๊ตฌ์กฐ๋ฅผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ฐ˜์˜**ํ•˜๋„๋ก ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค. + +--- + +### ๐ŸŽ›๏ธ Controls & Args ์‚ฌ์šฉ ๊ธฐ์ค€ + +- `variant`, `size`, `disabled` ๋“ฑ์€ **Controls๋กœ ์ œ์–ด ๊ฐ€๋Šฅ**ํ•˜๋„๋ก ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค. +- ์‹ค์ œ ์„œ๋น„์Šค์—์„œ **์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” Props๋Š” Story์— ํฌํ•จํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.** +- Story๋Š” **โ€œ์˜๋„๋œ ์‚ฌ์šฉ ์˜ˆ์‹œโ€**๋งŒ์„ ํ‘œํ˜„ํ•ฉ๋‹ˆ๋‹ค. + +> ์—ฌ๋Ÿฌ Story args๋Š” **์ดˆ๊ธฐ ์˜ˆ์‹œ์šฉ**์ด๋ฉฐ, +> ํŒ€ ๋…ผ์˜๋ฅผ ํ†ตํ•ด ์ตœ์ข… ์ปจ๋ฒค์…˜์„ ํ™•์ •ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค. + +--- + +### ๐Ÿ”ฎ ์•ž์œผ๋กœ ์ถ”๊ฐ€ ์˜ˆ์ • + +- ๐ŸŽจ **๋””์ž์ธ ํ† ํฐ** + - color / spacing / typography + +- โ™ฟ **์ ‘๊ทผ์„ฑ(A11y) ์ฒดํฌ ๊ธฐ์ค€ ์ •๋ฆฌ** + +- ๐Ÿงช **Interaction / Visual Test ํ™•์žฅ** + +- ๐Ÿงฉ **Feature / Widget ๋‹จ์œ„ ๋ฌธ์„œํ™” ๊ธฐ์ค€** + +--- + +### โ–ถ๏ธ ์‹คํ–‰ ๋ฐฉ๋ฒ• + +```bash +pnpm storybook +``` + +> Storybook์€ ๊ฐœ๋ฐœ ์ค‘ ์ƒ์‹œ ์ฐธ๊ณ ํ•˜๋Š” UI ๊ธฐ์ค€ ๋ฌธ์„œ์ž…๋‹ˆ๋‹ค. diff --git a/src/shared/ui/button/Button.stories.tsx b/src/shared/ui/button/Button.stories.tsx new file mode 100644 index 00000000..dfbbcdb9 --- /dev/null +++ b/src/shared/ui/button/Button.stories.tsx @@ -0,0 +1,57 @@ +import { Button } from "@shared/ui/button/Button"; + +import type { Meta, StoryObj } from "@storybook/react-vite"; + +const meta: Meta = { + title: "Shared/Button", + component: Button, + tags: ["autodocs"], + + argTypes: { + variant: { + control: { type: "radio" }, + options: ["primary", "secondary"], + }, + size: { + control: { type: "radio" }, + options: ["sm", "md", "lg"], + }, + disabled: { control: "boolean" }, + + onClick: { action: "clicked" }, + }, + + args: { + children: "๋ฒ„ํŠผ ์˜ˆ์‹œ", + variant: "primary", + size: "md", + disabled: false, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Primary: Story = { + args: { variant: "primary" }, +}; + +export const Secondary: Story = { + args: { variant: "secondary" }, +}; + +export const Small: Story = { + args: { size: "sm" }, +}; + +export const Large: Story = { + args: { size: "lg" }, +}; + +export const Disabled: Story = { + args: { disabled: true }, +}; + +export const LongLabel: Story = { + args: { children: "์•„์ฃผ์•„์ฃผ ๊ธด ๋ฒ„ํŠผ ๋ผ๋ฒจ ์˜ˆ์‹œ" }, +}; diff --git a/src/shared/ui/button/Button.tsx b/src/shared/ui/button/Button.tsx new file mode 100644 index 00000000..54a988e0 --- /dev/null +++ b/src/shared/ui/button/Button.tsx @@ -0,0 +1,35 @@ +import type { ButtonHTMLAttributes } from "react"; + +type Props = ButtonHTMLAttributes & { + variant?: "primary" | "secondary"; + size?: "sm" | "md" | "lg"; +}; + +export const Button = ({ + variant = "primary", + size = "md", + className, + type, + ...props +}: Props) => { + const base = "rounded font-medium"; + const variantClass = + variant === "primary" + ? "bg-black text-white" + : "border border-gray-300 bg-white text-black"; + + const sizeClass = + size === "sm" + ? "px-3 py-1 text-sm" + : size === "lg" + ? "px-5 py-3 text-base" + : "px-4 py-2 text-sm"; + + return ( +