diff --git a/.env b/.env
new file mode 100644
index 0000000..8978752
--- /dev/null
+++ b/.env
@@ -0,0 +1 @@
+VITE_SERVER_API_URL=http://localhost:8000
\ No newline at end of file
diff --git a/index.html b/index.html
index 6049f2a..4d1b104 100644
--- a/index.html
+++ b/index.html
@@ -2,9 +2,9 @@
-
+
- PayCheck
+ 나만의 회계 비서, PayCheck
diff --git a/package.json b/package.json
index 0d46058..4ceb83c 100644
--- a/package.json
+++ b/package.json
@@ -11,8 +11,10 @@
},
"dependencies": {
"@tailwindcss/vite": "^4.1.7",
+ "axios": "^1.9.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",
+ "react-router-dom": "^7.6.0",
"tailwindcss": "^4.1.7"
},
"devDependencies": {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index fac60a4..d9372c9 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -11,12 +11,18 @@ importers:
'@tailwindcss/vite':
specifier: ^4.1.7
version: 4.1.7(vite@6.3.5(jiti@2.4.2)(lightningcss@1.30.1))
+ axios:
+ specifier: ^1.9.0
+ version: 1.9.0
react:
specifier: ^19.1.0
version: 19.1.0
react-dom:
specifier: ^19.1.0
version: 19.1.0(react@19.1.0)
+ react-router-dom:
+ specifier: ^7.6.0
+ version: 7.6.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
tailwindcss:
specifier: ^4.1.7
version: 4.1.7
@@ -671,6 +677,12 @@ packages:
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+ asynckit@0.4.0:
+ resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
+
+ axios@1.9.0:
+ resolution: {integrity: sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==}
+
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
@@ -689,6 +701,10 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
+ call-bind-apply-helpers@1.0.2:
+ resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
+ engines: {node: '>= 0.4'}
+
callsites@3.1.0:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'}
@@ -711,12 +727,20 @@ packages:
color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+ combined-stream@1.0.8:
+ resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
+ engines: {node: '>= 0.8'}
+
concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+ cookie@1.0.2:
+ resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==}
+ engines: {node: '>=18'}
+
cross-spawn@7.0.6:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
@@ -736,10 +760,18 @@ packages:
deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+ delayed-stream@1.0.0:
+ resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
+ engines: {node: '>=0.4.0'}
+
detect-libc@2.0.4:
resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==}
engines: {node: '>=8'}
+ dunder-proto@1.0.1:
+ resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
+ engines: {node: '>= 0.4'}
+
electron-to-chromium@1.5.155:
resolution: {integrity: sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==}
@@ -747,6 +779,22 @@ packages:
resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==}
engines: {node: '>=10.13.0'}
+ es-define-property@1.0.1:
+ resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
+ engines: {node: '>= 0.4'}
+
+ es-errors@1.3.0:
+ resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
+ engines: {node: '>= 0.4'}
+
+ es-object-atoms@1.1.1:
+ resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
+ engines: {node: '>= 0.4'}
+
+ es-set-tostringtag@2.1.0:
+ resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
+ engines: {node: '>= 0.4'}
+
esbuild@0.25.4:
resolution: {integrity: sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==}
engines: {node: '>=18'}
@@ -856,15 +904,39 @@ packages:
flatted@3.3.3:
resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
+ follow-redirects@1.15.9:
+ resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
+ engines: {node: '>=4.0'}
+ peerDependencies:
+ debug: '*'
+ peerDependenciesMeta:
+ debug:
+ optional: true
+
+ form-data@4.0.2:
+ resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==}
+ engines: {node: '>= 6'}
+
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
+ function-bind@1.1.2:
+ resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+
gensync@1.0.0-beta.2:
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
engines: {node: '>=6.9.0'}
+ get-intrinsic@1.3.0:
+ resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
+ engines: {node: '>= 0.4'}
+
+ get-proto@1.0.1:
+ resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
+ engines: {node: '>= 0.4'}
+
glob-parent@5.1.2:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'}
@@ -885,6 +957,10 @@ packages:
resolution: {integrity: sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==}
engines: {node: '>=18'}
+ gopd@1.2.0:
+ resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
+ engines: {node: '>= 0.4'}
+
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
@@ -895,6 +971,18 @@ packages:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
+ has-symbols@1.1.0:
+ resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
+ engines: {node: '>= 0.4'}
+
+ has-tostringtag@1.0.2:
+ resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
+ engines: {node: '>= 0.4'}
+
+ hasown@2.0.2:
+ resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
+ engines: {node: '>= 0.4'}
+
ignore@5.3.2:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
@@ -1040,6 +1128,10 @@ packages:
magic-string@0.30.17:
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
+ math-intrinsics@1.1.0:
+ resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
+ engines: {node: '>= 0.4'}
+
merge2@1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
@@ -1048,6 +1140,14 @@ packages:
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
engines: {node: '>=8.6'}
+ mime-db@1.52.0:
+ resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
+ engines: {node: '>= 0.6'}
+
+ mime-types@2.1.35:
+ resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
+ engines: {node: '>= 0.6'}
+
minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
@@ -1125,6 +1225,9 @@ packages:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
+ proxy-from-env@1.1.0:
+ resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
+
punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
@@ -1141,6 +1244,23 @@ packages:
resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==}
engines: {node: '>=0.10.0'}
+ react-router-dom@7.6.0:
+ resolution: {integrity: sha512-DYgm6RDEuKdopSyGOWZGtDfSm7Aofb8CCzgkliTjtu/eDuB0gcsv6qdFhhi8HdtmA+KHkt5MfZ5K2PdzjugYsA==}
+ engines: {node: '>=20.0.0'}
+ peerDependencies:
+ react: '>=18'
+ react-dom: '>=18'
+
+ react-router@7.6.0:
+ resolution: {integrity: sha512-GGufuHIVCJDbnIAXP3P9Sxzq3UUsddG3rrI3ut1q6m0FI6vxVBF3JoPQ38+W/blslLH4a5Yutp8drkEpXoddGQ==}
+ engines: {node: '>=20.0.0'}
+ peerDependencies:
+ react: '>=18'
+ react-dom: '>=18'
+ peerDependenciesMeta:
+ react-dom:
+ optional: true
+
react@19.1.0:
resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==}
engines: {node: '>=0.10.0'}
@@ -1173,6 +1293,9 @@ packages:
engines: {node: '>=10'}
hasBin: true
+ set-cookie-parser@2.7.1:
+ resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==}
+
shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
@@ -1856,6 +1979,16 @@ snapshots:
argparse@2.0.1: {}
+ asynckit@0.4.0: {}
+
+ axios@1.9.0:
+ dependencies:
+ follow-redirects: 1.15.9
+ form-data: 4.0.2
+ proxy-from-env: 1.1.0
+ transitivePeerDependencies:
+ - debug
+
balanced-match@1.0.2: {}
brace-expansion@1.1.11:
@@ -1878,6 +2011,11 @@ snapshots:
node-releases: 2.0.19
update-browserslist-db: 1.1.3(browserslist@4.24.5)
+ call-bind-apply-helpers@1.0.2:
+ dependencies:
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+
callsites@3.1.0: {}
caniuse-lite@1.0.30001718: {}
@@ -1895,10 +2033,16 @@ snapshots:
color-name@1.1.4: {}
+ combined-stream@1.0.8:
+ dependencies:
+ delayed-stream: 1.0.0
+
concat-map@0.0.1: {}
convert-source-map@2.0.0: {}
+ cookie@1.0.2: {}
+
cross-spawn@7.0.6:
dependencies:
path-key: 3.1.1
@@ -1913,8 +2057,16 @@ snapshots:
deep-is@0.1.4: {}
+ delayed-stream@1.0.0: {}
+
detect-libc@2.0.4: {}
+ dunder-proto@1.0.1:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-errors: 1.3.0
+ gopd: 1.2.0
+
electron-to-chromium@1.5.155: {}
enhanced-resolve@5.18.1:
@@ -1922,6 +2074,21 @@ snapshots:
graceful-fs: 4.2.11
tapable: 2.2.2
+ es-define-property@1.0.1: {}
+
+ es-errors@1.3.0: {}
+
+ es-object-atoms@1.1.1:
+ dependencies:
+ es-errors: 1.3.0
+
+ es-set-tostringtag@2.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ has-tostringtag: 1.0.2
+ hasown: 2.0.2
+
esbuild@0.25.4:
optionalDependencies:
'@esbuild/aix-ppc64': 0.25.4
@@ -2073,11 +2240,40 @@ snapshots:
flatted@3.3.3: {}
+ follow-redirects@1.15.9: {}
+
+ form-data@4.0.2:
+ dependencies:
+ asynckit: 0.4.0
+ combined-stream: 1.0.8
+ es-set-tostringtag: 2.1.0
+ mime-types: 2.1.35
+
fsevents@2.3.3:
optional: true
+ function-bind@1.1.2: {}
+
gensync@1.0.0-beta.2: {}
+ get-intrinsic@1.3.0:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ function-bind: 1.1.2
+ get-proto: 1.0.1
+ gopd: 1.2.0
+ has-symbols: 1.1.0
+ hasown: 2.0.2
+ math-intrinsics: 1.1.0
+
+ get-proto@1.0.1:
+ dependencies:
+ dunder-proto: 1.0.1
+ es-object-atoms: 1.1.1
+
glob-parent@5.1.2:
dependencies:
is-glob: 4.0.3
@@ -2092,12 +2288,24 @@ snapshots:
globals@16.1.0: {}
+ gopd@1.2.0: {}
+
graceful-fs@4.2.11: {}
graphemer@1.4.0: {}
has-flag@4.0.0: {}
+ has-symbols@1.1.0: {}
+
+ has-tostringtag@1.0.2:
+ dependencies:
+ has-symbols: 1.1.0
+
+ hasown@2.0.2:
+ dependencies:
+ function-bind: 1.1.2
+
ignore@5.3.2: {}
ignore@7.0.4: {}
@@ -2205,6 +2413,8 @@ snapshots:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.0
+ math-intrinsics@1.1.0: {}
+
merge2@1.4.1: {}
micromatch@4.0.8:
@@ -2212,6 +2422,12 @@ snapshots:
braces: 3.0.3
picomatch: 2.3.1
+ mime-db@1.52.0: {}
+
+ mime-types@2.1.35:
+ dependencies:
+ mime-db: 1.52.0
+
minimatch@3.1.2:
dependencies:
brace-expansion: 1.1.11
@@ -2275,6 +2491,8 @@ snapshots:
prelude-ls@1.2.1: {}
+ proxy-from-env@1.1.0: {}
+
punycode@2.3.1: {}
queue-microtask@1.2.3: {}
@@ -2286,6 +2504,20 @@ snapshots:
react-refresh@0.17.0: {}
+ react-router-dom@7.6.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
+ dependencies:
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ react-router: 7.6.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+
+ react-router@7.6.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
+ dependencies:
+ cookie: 1.0.2
+ react: 19.1.0
+ set-cookie-parser: 2.7.1
+ optionalDependencies:
+ react-dom: 19.1.0(react@19.1.0)
+
react@19.1.0: {}
resolve-from@4.0.0: {}
@@ -2328,6 +2560,8 @@ snapshots:
semver@7.7.2: {}
+ set-cookie-parser@2.7.1: {}
+
shebang-command@2.0.0:
dependencies:
shebang-regex: 3.0.0
diff --git a/public/add.svg b/public/add.svg
new file mode 100644
index 0000000..9912fc3
--- /dev/null
+++ b/public/add.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/barcode.svg b/public/barcode.svg
new file mode 100644
index 0000000..f59c9e2
--- /dev/null
+++ b/public/barcode.svg
@@ -0,0 +1,10 @@
+
diff --git a/public/icon.svg b/public/icon.svg
new file mode 100644
index 0000000..18e25f3
--- /dev/null
+++ b/public/icon.svg
@@ -0,0 +1,9 @@
+
+
diff --git a/public/icon2.svg b/public/icon2.svg
new file mode 100644
index 0000000..872cbd3
--- /dev/null
+++ b/public/icon2.svg
@@ -0,0 +1,9 @@
+
+
diff --git a/public/icon3.svg b/public/icon3.svg
new file mode 100644
index 0000000..a60e494
--- /dev/null
+++ b/public/icon3.svg
@@ -0,0 +1,9 @@
+
+
diff --git a/public/logo.svg b/public/logo.svg
new file mode 100644
index 0000000..b292309
--- /dev/null
+++ b/public/logo.svg
@@ -0,0 +1,15 @@
+
+
diff --git a/public/navigate_left.svg b/public/navigate_left.svg
new file mode 100644
index 0000000..de4b7d3
--- /dev/null
+++ b/public/navigate_left.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/navigate_right.svg b/public/navigate_right.svg
new file mode 100644
index 0000000..cb44808
--- /dev/null
+++ b/public/navigate_right.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/receipt.svg b/public/receipt.svg
new file mode 100644
index 0000000..44fc08d
--- /dev/null
+++ b/public/receipt.svg
@@ -0,0 +1,7 @@
+
+
diff --git a/src/App.tsx b/src/App.tsx
index d2760b4..872e4ee 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,10 +1,33 @@
-import './App.css'
+import './App.css';
+import {createBrowserRouter, RouterProvider} from 'react-router-dom';
+import LandingPage from './pages/LandingPage';
+import NotFoundPage from './pages/NotFoundPage';
+import MainPage from './pages/MainPage';
+import CheckPage from './pages/CheckPage';
+import Layout from './layouts/Layout';
-function App() {
+const router = createBrowserRouter([
+ {
+ path: "/",
+ element: ,
+ errorElement: ,
+ },
+ {
+ path: "/main",
+ element: ,
+ errorElement: ,
+ },
+ {
+ path: "/check",
+ element: ,
+ errorElement: ,
+ }
+ ]);
+function App() {
return (
- <>
- >
+
+
)
}
diff --git a/src/apis/auth.ts b/src/apis/auth.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/apis/axios.ts b/src/apis/axios.ts
new file mode 100644
index 0000000..6c66cf2
--- /dev/null
+++ b/src/apis/axios.ts
@@ -0,0 +1,5 @@
+import axios from "axios";
+
+export const axiosInstance = axios.create({
+ baseURL: import.meta.env.VITE_SERVER_API_URL,
+});
\ No newline at end of file
diff --git a/src/index.css b/src/index.css
index a461c50..310ba43 100644
--- a/src/index.css
+++ b/src/index.css
@@ -1 +1,2 @@
+@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;700;900&display=swap');
@import "tailwindcss";
\ No newline at end of file
diff --git a/src/layouts/Layout.tsx b/src/layouts/Layout.tsx
new file mode 100644
index 0000000..d216d3d
--- /dev/null
+++ b/src/layouts/Layout.tsx
@@ -0,0 +1,43 @@
+import type { ReactNode } from 'react';
+import { useNavigate } from 'react-router-dom';
+
+interface LayoutProps {
+ children: ReactNode;
+}
+
+const Layout = ({ children }: LayoutProps) => {
+ const navigate = useNavigate();
+ return (
+
+
+ {/* 메인 컨텐츠 */}
+
{children}
+ {/* 푸터 */}
+
+
+ );
+};
+
+export default Layout;
\ No newline at end of file
diff --git a/src/pages/CheckPage.tsx b/src/pages/CheckPage.tsx
new file mode 100644
index 0000000..87f7509
--- /dev/null
+++ b/src/pages/CheckPage.tsx
@@ -0,0 +1,40 @@
+const CheckPage = () => {
+ return (
+
+
결제내역
+
25/05/08 동국대학교생활협동조합
+
+
+
+
삼겹김치철판
+
2
+
13000
+
모수진
+
+
+
+
+
+
+
+ )
+};
+
+export default CheckPage;
\ No newline at end of file
diff --git a/src/pages/LandingPage.tsx b/src/pages/LandingPage.tsx
new file mode 100644
index 0000000..1a07c43
--- /dev/null
+++ b/src/pages/LandingPage.tsx
@@ -0,0 +1,33 @@
+import { useNavigate } from 'react-router-dom';
+
+const LandingPage = () => {
+ const navigate = useNavigate();
+
+ const handleStartClick = () => {
+ navigate('/main'); // MainPage 경로로 이동
+ };
+
+ return (
+
+
+
나만의 회계 비서,
+
+

+
+
+
+
+

+
ChillGuys!
+
+
+
+ );
+};
+
+export default LandingPage;
\ No newline at end of file
diff --git a/src/pages/MainPage.tsx b/src/pages/MainPage.tsx
new file mode 100644
index 0000000..92f50e2
--- /dev/null
+++ b/src/pages/MainPage.tsx
@@ -0,0 +1,218 @@
+import React, { useState, useRef } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { axiosInstance } from '../apis/axios';
+
+const MainPage = () => {
+ const [participant, setParticipant] = useState('');
+ const [participants, setParticipants] = useState([]);
+ const [settleType, setSettleType] = useState<'even' | 'item'>('even');
+ const [previewFiles, setPreviewFiles] = useState([]);
+ const [previewUrls, setPreviewUrls] = useState([]);
+ const fileInputRef = useRef(null);
+ const navigate = useNavigate();
+ const [uploading, setUploading] = useState(false);
+
+ const handleAdd = () => {
+ if (participant.trim() && !participants.includes(participant.trim())) {
+ setParticipants([...participants, participant.trim()]);
+ setParticipant('');
+ }
+ };
+
+ const handleRemove = (name: string) => {
+ setParticipants(participants.filter(p => p !== name));
+ };
+
+ const handleKeyDown = (e: React.KeyboardEvent) => {
+ if (e.key === 'Enter') {
+ handleAdd();
+ }
+ };
+
+ const handleAddBtnClick = () => {
+ fileInputRef.current?.click();
+ };
+
+ const handleFileChange = (e: React.ChangeEvent) => {
+ if (e.target.files && e.target.files.length > 0) {
+ const files = Array.from(e.target.files);
+ // 새로 선택한 파일들을 기존 대기열에 추가
+ setPreviewFiles(prev => [...prev, ...files]);
+ // 미리보기 URL 생성 및 추가
+ const newUrls = files.map(file => URL.createObjectURL(file));
+ setPreviewUrls(prev => [...prev, ...newUrls]);
+ }
+ };
+
+ const handleRemovePreview = (idx: number) => {
+ setPreviewFiles(prev => prev.filter((_, i) => i !== idx));
+ setPreviewUrls(prev => prev.filter((_, i) => i !== idx));
+ };
+
+ const handlePayCheck = async () => {
+ if (previewFiles.length === 0) {
+ alert('영수증 이미지를 첨부해주세요.');
+ return;
+ }
+ if (participants.length === 0) {
+ alert('참여자를 추가해주세요.');
+ return;
+ }
+ setUploading(true);
+ try {
+ const formData = new FormData();
+ previewFiles.forEach(file => formData.append('image', file));
+
+ // 이미지 업로드 POST 요청
+ await axiosInstance.post('api/receipt/upload/', formData, {
+ headers: {
+ 'Content-Type': 'multipart/form-data',
+ },
+ });
+
+ // 참여자 명단 개별 POST 요청
+ // 참여자 명단 배열을 순회하며 각 이름에 대해 개별 POST 요청 전송
+ for (const participantName of participants) {
+ // 각 참여자 이름을 JSON 객체 형태로 감싸서 전송
+ const participantBody = {
+ name: participantName
+ };
+
+ // '/api/participant/' 엔드포인트로 JSON 형태의 본문 전송
+ await axiosInstance.post('api/participant/', participantBody, {
+ headers: {
+ 'Content-Type': 'application/json',
+ }
+ });
+ }
+
+ // 업로드 성공 시 페이지 이동
+ navigate('/check');
+ } catch (error: any) { // 에러 타입 명시
+ console.error('업로드 및 참여자 명단 전송 실패:', error);
+ alert('업로드 및 참여자 명단 전송에 실패했습니다.\n' + (error.response?.data?.detail || error.message)); // 상세 에러 메시지 포함
+ } finally {
+ setUploading(false);
+ }
+ };
+
+ return (
+
+
+ 영수증 이미지 첨부
+
+
+
+
이미지를 첨부하세요
+
+ {/* 미리보기 썸네일 */}
+
+ {previewUrls.map((url, idx) => (
+
+

+
+
+ ))}
+
+
+
+ 참여자 리스트
+
+ setParticipant(e.target.value)}
+ onKeyDown={handleKeyDown}
+ placeholder="참여자 이름 입력"
+ className="w-[220px] h-[50px] rounded-[12px] bg-[#F5F5F5] px-4 text-[18px] font-['Inter'] outline-none min-w-0"
+ />
+
+
+
+ {participants.map(name => (
+
+ {name}
+
+
+ ))}
+
+
+
+ 정산 방식 선택
+
+
+
+
+
+ {/* 하단 PayCheck 버튼 */}
+
+
+
+
+ );
+};
+
+export default MainPage;
\ No newline at end of file
diff --git a/src/pages/NotFoundPage.tsx b/src/pages/NotFoundPage.tsx
new file mode 100644
index 0000000..2493e7b
--- /dev/null
+++ b/src/pages/NotFoundPage.tsx
@@ -0,0 +1,5 @@
+const NotFoundPage = () => {
+ return Not Found Error!
+};
+
+export default NotFoundPage;
\ No newline at end of file
diff --git a/src/types/auth.ts b/src/types/auth.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/types/common.ts b/src/types/common.ts
new file mode 100644
index 0000000..76b2b14
--- /dev/null
+++ b/src/types/common.ts
@@ -0,0 +1,6 @@
+export type CommonResponse = {
+ status:boolean;
+ statusCode:number;
+ message:string;
+ data: T;
+}
\ No newline at end of file
diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts
index 11f02fe..08a5978 100644
--- a/src/vite-env.d.ts
+++ b/src/vite-env.d.ts
@@ -1 +1,9 @@
///
+interface ImportMetaEnv {
+ readonly VITE_SERVER_API_URL: string
+ // 다른 환경 변수들에 대한 타입 정의...
+ }
+
+interface ImportMeta {
+ readonly env: ImportMetaEnv
+}
\ No newline at end of file
diff --git a/tsconfig.app.json b/tsconfig.app.json
index c9ccbd4..9c8a3c4 100644
--- a/tsconfig.app.json
+++ b/tsconfig.app.json
@@ -19,7 +19,6 @@
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
- "erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},