diff --git a/package.json b/package.json index e0f5d6a..395dd20 100644 --- a/package.json +++ b/package.json @@ -17,8 +17,10 @@ "@tanstack/react-query": "^5.84.1", "@tanstack/react-query-devtools": "^5.84.1", "@vercel/analytics": "^1.5.0", + "axios": "^1.11.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "lottie-react": "^2.4.1", "lucide-react": "^0.536.0", "next": "15.4.5", "next-pwa": "^5.6.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3a811a4..842416d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -26,12 +26,18 @@ importers: '@vercel/analytics': specifier: ^1.5.0 version: 1.5.0(next@15.4.5(@babel/core@7.28.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0) + axios: + specifier: ^1.11.0 + version: 1.11.0 class-variance-authority: specifier: ^0.7.1 version: 0.7.1 clsx: specifier: ^2.1.1 version: 2.1.1 + lottie-react: + specifier: ^2.4.1 + version: 2.4.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) lucide-react: specifier: ^0.536.0 version: 0.536.0(react@19.1.0) @@ -2325,6 +2331,9 @@ packages: async@3.2.6: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + at-least-node@1.0.0: resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} engines: {node: '>= 4.0.0'} @@ -2337,6 +2346,9 @@ packages: resolution: {integrity: sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==} engines: {node: '>=4'} + axios@1.11.0: + resolution: {integrity: sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==} + axobject-query@4.1.0: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} engines: {node: '>= 0.4'} @@ -2489,6 +2501,10 @@ packages: resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} engines: {node: '>=12.5.0'} + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -2611,6 +2627,10 @@ packages: resolution: {integrity: sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==} engines: {node: '>=6'} + 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'} @@ -2917,10 +2937,23 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + for-each@0.3.5: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} + form-data@4.0.4: + resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} + engines: {node: '>= 6'} + fs-extra@9.1.0: resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} engines: {node: '>=10'} @@ -3428,6 +3461,15 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true + lottie-react@2.4.1: + resolution: {integrity: sha512-LQrH7jlkigIIv++wIyrOYFLHSKQpEY4zehPicL9bQsrt1rnoKRYCYgpCUe5maqylNtacy58/sQDZTkwMcTRxZw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + lottie-web@5.13.0: + resolution: {integrity: sha512-+gfBXl6sxXMPe8tKQm7qzLnUy5DUPJPKIyRHwtpCpyUEYjHYRJC/5gjUvdkuO2c3JllrPtHXH5UJJK8LRYl5yQ==} + lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} @@ -3773,6 +3815,9 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + 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'} @@ -7153,6 +7198,8 @@ snapshots: async@3.2.6: {} + asynckit@0.4.0: {} + at-least-node@1.0.0: {} available-typed-arrays@1.0.7: @@ -7161,6 +7208,14 @@ snapshots: axe-core@4.10.3: {} + axios@1.11.0: + dependencies: + follow-redirects: 1.15.11 + form-data: 4.0.4 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + axobject-query@4.1.0: {} babel-loader@8.4.1(@babel/core@7.28.0)(webpack@5.101.3): @@ -7320,6 +7375,10 @@ snapshots: color-string: 1.9.1 optional: true + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + commander@2.20.3: {} commander@7.2.0: {} @@ -7453,6 +7512,8 @@ snapshots: pify: 4.0.1 rimraf: 2.7.1 + delayed-stream@1.0.0: {} + detect-libc@2.0.4: {} dir-glob@3.0.1: @@ -7930,10 +7991,20 @@ snapshots: flatted@3.3.3: {} + follow-redirects@1.15.11: {} + for-each@0.3.5: dependencies: is-callable: 1.2.7 + form-data@4.0.4: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + fs-extra@9.1.0: dependencies: at-least-node: 1.0.0 @@ -8423,6 +8494,14 @@ snapshots: dependencies: js-tokens: 4.0.0 + lottie-react@2.4.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + lottie-web: 5.13.0 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + + lottie-web@5.13.0: {} + lower-case@2.0.2: dependencies: tslib: 2.8.1 @@ -8762,6 +8841,8 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 + proxy-from-env@1.1.0: {} + punycode@2.3.1: {} queue-microtask@1.2.3: {} diff --git a/public/lottie/loading.json b/public/lottie/loading.json new file mode 100644 index 0000000..41bc204 --- /dev/null +++ b/public/lottie/loading.json @@ -0,0 +1 @@ +{"v":"4.8.0","meta":{"g":"LottieFiles AE 3.5.7","a":"","k":"","d":"","tc":""},"fr":30,"ip":0,"op":60,"w":1080,"h":1080,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"로고 흰색 Outlines 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[540,540,0],"ix":2},"a":{"a":0,"k":[540,540,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":20,"s":[{"i":[[321.988,0],[0,0],[0,0],[0,0],[346.287,0],[261.236,0]],"o":[[-218.709,0],[0,0],[0,0],[0,0],[-297.688,0],[-261.236,0]],"v":[[244.864,1481.971],[-4.221,1390.842],[1654.321,1415.142],[1654.321,-134.045],[1235.13,1573.099],[749.11,1226.811]],"c":true}]},{"t":40,"s":[{"i":[[321.988,0],[0,0],[0,0],[0,0],[346.288,0],[261.236,0]],"o":[[-218.709,0],[0,0],[0,0],[0,0],[-297.688,0],[-261.236,0]],"v":[[-36.452,-315.913],[-243.01,1385.156],[1415.532,1409.457],[1415.532,-139.731],[1032.792,-407.041],[516.396,-139.731]],"c":true}]}],"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-32.17,-16.699],[0,0],[34.747,-18.043],[0,0],[0,0],[-0.007,-26.685],[0,0],[26.69,-0.005],[0,0],[0,0],[24.558,-10.45],[0,0],[10.448,24.562],[0,0],[0,0],[2.04,-4.802],[0,0],[24.564,10.447],[0,0],[-10.441,24.558],[0,0],[0,0],[0,26.695],[0,0],[-26.69,0],[0,0],[0,2.409]],"o":[[0.007,-36.244],[0,0],[34.747,18.041],[0,0],[0,0],[26.685,0.005],[0,0],[0,26.69],[0,0],[0,0],[10.445,24.558],[0,0],[-24.563,10.447],[0,0],[0,0],[-0.435,4.93],[0,0],[-10.448,24.562],[0,0],[-24.554,-10.45],[0,0],[0,0],[-26.694,0],[0,0],[0.006,-26.69],[0,0],[-0.326,-2.286],[0,0]],"v":[[-241.665,-460.664],[-171.077,-503.545],[304.041,-256.853],[304.041,-171.066],[-102.024,39.777],[434.997,39.777],[483.33,88.11],[483.33,160.61],[434.997,208.943],[315.487,208.943],[412.694,437.44],[387.136,500.828],[366.014,509.797],[302.625,484.238],[185.522,208.943],[-170.205,208.943],[-173.861,223.621],[-284.712,484.238],[-348.102,509.797],[-369.224,500.828],[-394.783,437.44],[-297.574,208.943],[-434.997,208.943],[-483.33,160.61],[-483.33,88.11],[-434.997,39.777],[-241.17,39.777],[-241.665,32.72]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.980392156863,0.313725490196,0.180392156863,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[540,537.69],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":20,"op":90,"st":21,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"로고 흰색 Outlines 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[540,540,0],"ix":2},"a":{"a":0,"k":[540,540,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":10,"s":[{"i":[[321.988,0],[0,0],[0,0],[0,0],[346.287,0],[261.236,0]],"o":[[-218.709,0],[0,0],[0,0],[0,0],[-297.687,0],[-261.236,0]],"v":[[-166.545,1476.286],[-415.63,1385.156],[1242.912,1409.457],[1242.912,-139.731],[823.721,1567.414],[337.701,1221.125]],"c":true}]},{"t":40,"s":[{"i":[[321.988,0],[0,0],[0,0],[0,0],[346.288,0],[261.236,0]],"o":[[-218.709,0],[0,0],[0,0],[0,0],[-297.688,0],[-261.236,0]],"v":[[-36.452,-315.913],[-243.01,1385.156],[1415.532,1409.457],[1415.532,-139.731],[1032.792,-407.041],[516.396,-139.731]],"c":true}]}],"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-32.17,-16.699],[0,0],[34.747,-18.043],[0,0],[0,0],[-0.007,-26.685],[0,0],[26.69,-0.005],[0,0],[0,0],[24.558,-10.45],[0,0],[10.448,24.562],[0,0],[0,0],[2.04,-4.802],[0,0],[24.564,10.447],[0,0],[-10.441,24.558],[0,0],[0,0],[0,26.695],[0,0],[-26.69,0],[0,0],[0,2.409]],"o":[[0.007,-36.244],[0,0],[34.747,18.041],[0,0],[0,0],[26.685,0.005],[0,0],[0,26.69],[0,0],[0,0],[10.445,24.558],[0,0],[-24.563,10.447],[0,0],[0,0],[-0.435,4.93],[0,0],[-10.448,24.562],[0,0],[-24.554,-10.45],[0,0],[0,0],[-26.694,0],[0,0],[0.006,-26.69],[0,0],[-0.326,-2.286],[0,0]],"v":[[-241.665,-460.664],[-171.077,-503.545],[304.041,-256.853],[304.041,-171.066],[-102.024,39.777],[434.997,39.777],[483.33,88.11],[483.33,160.61],[434.997,208.943],[315.487,208.943],[412.694,437.44],[387.136,500.828],[366.014,509.797],[302.625,484.238],[185.522,208.943],[-170.205,208.943],[-173.861,223.621],[-284.712,484.238],[-348.102,509.797],[-369.224,500.828],[-394.783,437.44],[-297.574,208.943],[-434.997,208.943],[-483.33,160.61],[-483.33,88.11],[-434.997,39.777],[-241.17,39.777],[-241.665,32.72]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.507398597867,0.399371936275,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[540,537.69],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":10,"op":90,"st":10,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"로고 흰색 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[540,540,0],"ix":2},"a":{"a":0,"k":[540,540,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[{"i":[[321.988,0],[0,0],[0,0],[0,0],[346.287,0],[261.236,0]],"o":[[-218.709,0],[0,0],[0,0],[0,0],[-297.688,0],[-261.236,0]],"v":[[6.075,1476.286],[-243.01,1385.156],[1415.532,1409.457],[1415.532,-139.731],[996.341,1567.414],[510.321,1221.125]],"c":true}]},{"t":40,"s":[{"i":[[321.988,0],[0,0],[0,0],[0,0],[346.288,0],[261.236,0]],"o":[[-218.709,0],[0,0],[0,0],[0,0],[-297.688,0],[-261.236,0]],"v":[[-36.452,-315.913],[-243.01,1385.156],[1415.532,1409.457],[1415.532,-139.731],[1032.792,-407.041],[516.396,-139.731]],"c":true}]}],"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-32.17,-16.699],[0,0],[34.747,-18.043],[0,0],[0,0],[-0.007,-26.685],[0,0],[26.69,-0.005],[0,0],[0,0],[24.558,-10.45],[0,0],[10.448,24.562],[0,0],[0,0],[2.04,-4.802],[0,0],[24.564,10.447],[0,0],[-10.441,24.558],[0,0],[0,0],[0,26.695],[0,0],[-26.69,0],[0,0],[0,2.409]],"o":[[0.007,-36.244],[0,0],[34.747,18.041],[0,0],[0,0],[26.685,0.005],[0,0],[0,26.69],[0,0],[0,0],[10.445,24.558],[0,0],[-24.563,10.447],[0,0],[0,0],[-0.435,4.93],[0,0],[-10.448,24.562],[0,0],[-24.554,-10.45],[0,0],[0,0],[-26.694,0],[0,0],[0.006,-26.69],[0,0],[-0.326,-2.286],[0,0]],"v":[[-241.665,-460.664],[-171.077,-503.545],[304.041,-256.853],[304.041,-171.066],[-102.024,39.777],[434.997,39.777],[483.33,88.11],[483.33,160.61],[434.997,208.943],[315.487,208.943],[412.694,437.44],[387.136,500.828],[366.014,509.797],[302.625,484.238],[185.522,208.943],[-170.205,208.943],[-173.861,223.621],[-284.712,484.238],[-348.102,509.797],[-369.224,500.828],[-394.783,437.44],[-297.574,208.943],[-434.997,208.943],[-483.33,160.61],[-483.33,88.11],[-434.997,39.777],[-241.17,39.777],[-241.665,32.72]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.682625265682,0.575428981407,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[540,537.69],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":90,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/public/svg/mypage/logout.svg b/public/svg/mypage/logout.svg new file mode 100644 index 0000000..9c5d002 --- /dev/null +++ b/public/svg/mypage/logout.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/svg/mypage/tiny-arrow-orange.svg b/public/svg/mypage/tiny-arrow-orange.svg new file mode 100644 index 0000000..8475b67 --- /dev/null +++ b/public/svg/mypage/tiny-arrow-orange.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/app/(pages)/(auth)/auth/kakao/callback/page.tsx b/src/app/(pages)/(auth)/auth/kakao/callback/page.tsx new file mode 100644 index 0000000..b2ba358 --- /dev/null +++ b/src/app/(pages)/(auth)/auth/kakao/callback/page.tsx @@ -0,0 +1,39 @@ +import { authService } from '@/lib/auth/auth.service'; +import { redirect } from 'next/navigation'; + +interface SearchParams { + code?: string; + error?: string; + error_description?: string; + state?: string; +} + +export default async function KakaoCallbackPage({ + searchParams, +}: { + searchParams: Promise; +}) { + const params = await searchParams; + const { code, error, error_description } = params; + + // try { + // const result = await authService.kakaoLogin(code ?? ''); + + // // token이 있다고 가정 + // const token = result.token; + + // const response = await fetch('/api/cookie-set', { + // method: 'POST', + // body: JSON.stringify({ token }), + // }); + + // if (!response.ok) { + // throw new Error('Failed to set cookie'); + // } + // redirect('/'); + // } catch (error) { + // console.error(error); + // } + + return null; +} diff --git a/src/app/(pages)/(auth)/login/page.tsx b/src/app/(pages)/(auth)/login/page.tsx index 4c93cc1..27a5619 100644 --- a/src/app/(pages)/(auth)/login/page.tsx +++ b/src/app/(pages)/(auth)/login/page.tsx @@ -1,13 +1,17 @@ 'use client'; +import { kakaoAuthService } from '@/lib/auth/kakao.service'; import KakaoLogo from '@/public/svg/logo/kakao-logo.svg'; import LoginMainBigLogo from '@/public/svg/logo/login-main-big.svg'; import { useRouter } from 'next/navigation'; export default function LoginPage() { const router = useRouter(); + const handleKakaoLogin = () => { + kakaoAuthService.login(); + }; return ( -
+
{/* 헤더 || 로고 영역 */}
@@ -23,11 +27,7 @@ export default function LoginPage() {
{/* 카카오 로그인 */} - diff --git a/src/app/(pages)/(main)/mypage/(mypage)/info/page.tsx b/src/app/(pages)/(main)/mypage/(mypage)/info/page.tsx index 7b1b3c0..7fc905d 100644 --- a/src/app/(pages)/(main)/mypage/(mypage)/info/page.tsx +++ b/src/app/(pages)/(main)/mypage/(mypage)/info/page.tsx @@ -5,9 +5,7 @@ import { LoadingSpinner } from '@/components/loading-spinner'; export default async function MyPageInfoPage() { return ( }> -
- -
+
); } diff --git a/src/app/(pages)/(main)/mypage/(mypage)/layout.tsx b/src/app/(pages)/(main)/mypage/(mypage)/layout.tsx index be1a32f..1e58a55 100644 --- a/src/app/(pages)/(main)/mypage/(mypage)/layout.tsx +++ b/src/app/(pages)/(main)/mypage/(mypage)/layout.tsx @@ -11,7 +11,7 @@ export default function MyPageLayout({ }>
- {children} +
{children}
); } diff --git a/src/app/(pages)/(main)/mypage/(mypage)/manage-video/[video_id]/page.tsx b/src/app/(pages)/(main)/mypage/(mypage)/manage-video/[video_id]/page.tsx new file mode 100644 index 0000000..1c0877f --- /dev/null +++ b/src/app/(pages)/(main)/mypage/(mypage)/manage-video/[video_id]/page.tsx @@ -0,0 +1,16 @@ +import { VideoPlayer } from '../../../_components/manage-video/video-player'; + +export default async function VideoDetailPage({ + params, +}: { + params: Promise<{ video_id: string }>; +}) { + const videoId = (await params).video_id; + console.log(videoId); + + return ( +
+ +
+ ); +} diff --git a/src/app/(pages)/(main)/mypage/(mypage)/manage-video/[video_id]/video-check/loading.tsx b/src/app/(pages)/(main)/mypage/(mypage)/manage-video/[video_id]/video-check/loading.tsx new file mode 100644 index 0000000..9830d0a --- /dev/null +++ b/src/app/(pages)/(main)/mypage/(mypage)/manage-video/[video_id]/video-check/loading.tsx @@ -0,0 +1,5 @@ +import { LoadingSpinner } from '@/components/loading-spinner'; + +export default function VideoCheckPageLoading() { + return ; +} diff --git a/src/app/(pages)/(main)/mypage/(mypage)/manage-video/[video_id]/video-check/page.tsx b/src/app/(pages)/(main)/mypage/(mypage)/manage-video/[video_id]/video-check/page.tsx new file mode 100644 index 0000000..a9f7ff2 --- /dev/null +++ b/src/app/(pages)/(main)/mypage/(mypage)/manage-video/[video_id]/video-check/page.tsx @@ -0,0 +1,35 @@ +import { FieldContainer } from '@/components/store-info'; +import { VideoCheckDescription } from '../../../../_components/manage-video/video-check-description'; +import { cn } from '@/lib/utils/cn'; + +const VIDEO_URL = + 'https://f002.backblazeb2.com/file/creatomate-c8xg3hsxdu/6683c14f-7d35-45ea-9402-d87fd498f5a7.mp4'; +const VIDEO_FILE_NAME = '사장님 대상 영상 설명'; + +export default async function VideoCheckPage({ + params, +}: { + params: Promise<{ video_id: string }>; +}) { + const videoId = (await params).video_id; + console.log(videoId); + + return ( +
+
+
+ +
+ ); +} diff --git a/src/app/(pages)/(main)/mypage/(mypage)/manage-video/page.tsx b/src/app/(pages)/(main)/mypage/(mypage)/manage-video/page.tsx index 8756fe6..a693fb1 100644 --- a/src/app/(pages)/(main)/mypage/(mypage)/manage-video/page.tsx +++ b/src/app/(pages)/(main)/mypage/(mypage)/manage-video/page.tsx @@ -3,9 +3,9 @@ import { VideoSection } from '../../_components/manage-video/video-section'; export default function ManageVideoPage() { return ( -
+ <> -
+ ); } diff --git a/src/app/(pages)/(main)/mypage/_components/info/info-contents.tsx b/src/app/(pages)/(main)/mypage/_components/info/info-contents.tsx index 26657c6..d789f7d 100644 --- a/src/app/(pages)/(main)/mypage/_components/info/info-contents.tsx +++ b/src/app/(pages)/(main)/mypage/_components/info/info-contents.tsx @@ -26,11 +26,11 @@ export function InfoContents() { return ( <> {!storeAdd ? ( -
+
) : ( -
+
)} @@ -41,7 +41,7 @@ export function InfoContents() { return ( <> {tabLabel === '가게 정보' && ( -
+
)} diff --git a/src/app/(pages)/(main)/mypage/_components/info/info-store-detail.tsx b/src/app/(pages)/(main)/mypage/_components/info/info-store-detail.tsx index 26d0106..c16366c 100644 --- a/src/app/(pages)/(main)/mypage/_components/info/info-store-detail.tsx +++ b/src/app/(pages)/(main)/mypage/_components/info/info-store-detail.tsx @@ -12,7 +12,7 @@ export function InfoStoreDetail() { if (!storeDetail) return null; return ( -
+
{edit ? : }
); diff --git a/src/app/(pages)/(main)/mypage/_components/manage-video/video-check-description.tsx b/src/app/(pages)/(main)/mypage/_components/manage-video/video-check-description.tsx new file mode 100644 index 0000000..53988a3 --- /dev/null +++ b/src/app/(pages)/(main)/mypage/_components/manage-video/video-check-description.tsx @@ -0,0 +1,31 @@ +import { FieldContainer } from '@/components/store-info'; + +const VideoDescription = [ + { + label: '제목', + value: '추천 멘트', + }, + { + label: '상세 설명', + value: + '📍 신촌에서 찾은 숨은 맛집, 가문의 우동!매일 직접 뽑는 쫄깃한 면발과 깊은 국물 맛이 일품이에요.\n따뜻한 우동 한 그릇으로 오늘 하루를 마무리해보세요🍲\n✅ 위치: 서울 서대문구 ○○로 ○○\n✅ 영업시간: 매일 11:00 ~ 21:00 (브레이크타임 15:00~17:00)\n✅ 전화: 02-000-0000\n#신촌맛집 #가문의우동 #우동맛집 #신촌데이트코스 #신촌점심추천 #신촌저녁추천 #신촌핫플 #서울맛집', + }, + { + label: '메뉴', + value: '아메리카노\n라떼\n티\n커피라떼', + }, +]; + +export function VideoCheckDescription() { + return ( +
+ {VideoDescription.map(item => ( + + ))} +
+ ); +} diff --git a/src/app/(pages)/(main)/mypage/_components/manage-video/video-player.tsx b/src/app/(pages)/(main)/mypage/_components/manage-video/video-player.tsx new file mode 100644 index 0000000..308f2b2 --- /dev/null +++ b/src/app/(pages)/(main)/mypage/_components/manage-video/video-player.tsx @@ -0,0 +1,53 @@ +'use client'; + +import { useState } from 'react'; +import { LoadingSpinner } from '@/components/loading-spinner'; +import { useRouter } from 'next/navigation'; + +export function VideoPlayer({ videoId }: { videoId: string }) { + const router = useRouter(); + const videoUrl = + 'https://f002.backblazeb2.com/file/creatomate-c8xg3hsxdu/6683c14f-7d35-45ea-9402-d87fd498f5a7.mp4'; + + return ( + <> + {/* ReactPlayer - 전체 화면 꽉 채우기 */} +
+ {videoUrl ? ( +
+ + ); +} diff --git a/src/app/(pages)/(main)/mypage/_components/manage-video/video-progress-header.tsx b/src/app/(pages)/(main)/mypage/_components/manage-video/video-progress-header.tsx index fc70184..c6efa44 100644 --- a/src/app/(pages)/(main)/mypage/_components/manage-video/video-progress-header.tsx +++ b/src/app/(pages)/(main)/mypage/_components/manage-video/video-progress-header.tsx @@ -1,18 +1,35 @@ -'use client'; - import { GradientProgressBar } from '@/components/gradient-progress-bar'; - +import TinyArrowOrangeIcon from '@/public/svg/mypage/tiny-arrow-orange.svg'; +import Link from 'next/link'; const FILE_PROGRESS = 72; const FILE_NAME = '파일명파일명파일명파일명파일명파일명파일명파일명파일명'; +const FILE_RESULT = true; export function VideoProgressHeader() { return ( - <> - {true && ( -
-

- 새로운 영상이 제작중이에요! -

+
+

+ 새로운 영상이 제작중이에요! +

+ {FILE_RESULT ? ( + <> + +

+ {FILE_NAME} +

+ + + 영상 확인하기 + + + +
+ + ) : ( + <>

{FILE_NAME} @@ -20,8 +37,8 @@ export function VideoProgressHeader() {

{FILE_PROGRESS}%

-

+ )} - +
); } diff --git a/src/app/(pages)/(main)/mypage/_hooks/use-info-query.tsx b/src/app/(pages)/(main)/mypage/_hooks/use-info-query.tsx index 1b51078..4c9d9ec 100644 --- a/src/app/(pages)/(main)/mypage/_hooks/use-info-query.tsx +++ b/src/app/(pages)/(main)/mypage/_hooks/use-info-query.tsx @@ -12,7 +12,7 @@ export const tabs: Record = { store: '가게 정보', 'store-detail': '상세 정보', owner: '사장님 정보', - video: '영상 관리', + video: '영상관리', } as const; const DEFAULT_TAB: TabType = 'store'; @@ -32,7 +32,11 @@ export function useInfoQuery() { 'tab', parseAsStringLiteral(validTabs).withDefault(DEFAULT_TAB), ); + + // 가게 상세 페이지 정보를 위한 쿼리 파라미터 const [storeName, setStoreName] = useQueryState('storeName', parseAsString); + + // 가게 상세 정보 편집 여부 const [edit, setEdit] = useQueryState( 'edit', parseAsBoolean.withDefault(false), diff --git a/src/app/(pages)/(main)/mypage/page.tsx b/src/app/(pages)/(main)/mypage/page.tsx index 9e5e521..eec57e5 100644 --- a/src/app/(pages)/(main)/mypage/page.tsx +++ b/src/app/(pages)/(main)/mypage/page.tsx @@ -2,6 +2,7 @@ import Link from 'next/link'; import StoreIcon from '@/public/svg/mypage/store.svg'; import OwnerIcon from '@/public/svg/mypage/owner.svg'; import VideoIcon from '@/public/svg/mypage/video.svg'; +import { LogoutButton } from '@/components/logout-button'; const LinkItems = [ { @@ -15,7 +16,7 @@ const LinkItems = [ icon: , }, { - label: '영상 관리', + label: '영상관리', herf: '/mypage/manage-video', icon: , }, @@ -39,6 +40,7 @@ export default function MyPage() { {item.label} ))} +
); diff --git a/src/app/(pages)/layout.tsx b/src/app/(pages)/layout.tsx index da3e8ee..bac3c80 100644 --- a/src/app/(pages)/layout.tsx +++ b/src/app/(pages)/layout.tsx @@ -22,9 +22,30 @@ export const metadata: Metadata = { 'creatomate', '홍보', '마케팅', + 'AI 숏폼 제작', + '숏폼 영상 제작', + '쇼츠 제작 플랫폼', + 'SNS 마케팅 자동화', + 'AI 마케팅 도구', + '음식점 홍보', + '외식업 마케팅', + '자영업 마케팅', + '식당 홍보', + '소상공인 마케팅', + '인스타그램 마케팅', + '틱톡 음식점 홍보', + '유튜브 숏츠 제작', + 'SNS 광고', + '바이럴 마케팅', + '신촌 음식점 홍보', + '서대문구 마케팅', + '이대 상권 마케팅', + '신촌 외식업', + '자영업 사장님 마케팅', + '푸드 크리에이터 제휴', ], openGraph: { - title: '쇼츠테이블, 음식점 홍보 숏폼 영상 제작 AI 서비스', + title: '쇼츠테이블', description: '쇼츠테이블, 음식점 홍보 숏폼 영상 제작 AI 서비스', type: 'website', siteName: '쇼츠테이블', @@ -43,7 +64,7 @@ export const metadata: Metadata = { }, twitter: { card: 'summary_large_image', - title: '쇼츠테이블, 음식점 홍보 숏폼 영상 제작 AI 서비스', + title: '쇼츠테이블', description: '쇼츠테이블, 음식점 홍보 숏폼 영상 제작 AI 서비스', images: ['https://shorts-table.com/img/og/og-image.png'], creator: '@shorts-table', diff --git a/src/app/api/cookie-set/route.ts b/src/app/api/cookie-set/route.ts new file mode 100644 index 0000000..e561799 --- /dev/null +++ b/src/app/api/cookie-set/route.ts @@ -0,0 +1,30 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { cookies } from 'next/headers'; + +export async function POST(request: NextRequest) { + try { + const { token } = await request.json(); + + if (!token) { + return NextResponse.json({ error: '토큰이 필요합니다' }, { status: 400 }); + } + + const cookieStore = await cookies(); + + // 토큰 쿠키 설정 + cookieStore.set('accessToken', token as string, { + httpOnly: true, + secure: process.env.NODE_ENV === 'production', + sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'strict', + maxAge: 60 * 60 * 24 * 7, // 7일 + path: '/', + }); + + return NextResponse.json({ success: true }); + } catch (error) { + return NextResponse.json( + { error: '토큰 설정 중 오류가 발생했습니다' }, + { status: 500 }, + ); + } +} diff --git a/src/components/header.tsx b/src/components/header.tsx index d463bcf..a2b291b 100644 --- a/src/components/header.tsx +++ b/src/components/header.tsx @@ -110,7 +110,7 @@ export function Header() { }; case 'VIDEO_MANAGEMENT': return { - title: '영상 관리', + title: '영상관리', showBackButton: true, showEditButton: false, }; diff --git a/src/components/loading-spinner.tsx b/src/components/loading-spinner.tsx index 220f1fd..4a1ea86 100644 --- a/src/components/loading-spinner.tsx +++ b/src/components/loading-spinner.tsx @@ -1,9 +1,27 @@ -import { Loader2 } from 'lucide-react'; +// import { Loader2 } from 'lucide-react'; + +// export function LoadingSpinner() { +// return ( +//
+// +//
+// ); +// } + +'use client'; + +import Lottie from 'lottie-react'; +import loadingAnimation from '@/public/lottie/loading.json'; export function LoadingSpinner() { return (
- +
); } diff --git a/src/components/logout-button.tsx b/src/components/logout-button.tsx new file mode 100644 index 0000000..2c457df --- /dev/null +++ b/src/components/logout-button.tsx @@ -0,0 +1,51 @@ +'use client'; + +import { cn } from '@/lib/utils/cn'; +import LogoutIcon from '@/public/svg/mypage/logout.svg'; +import clsx from 'clsx'; +import { useState } from 'react'; + +const LOGOUT_BUTTON_STYLE = + 'py-2 flex-1 w-full h-full text-bodySmall text-white000 rounded-[8px]'; +export function LogoutButton() { + const [isLogout, setIsLogout] = useState(false); + return ( + <> + + {isLogout && ( +
void setIsLogout(false)} + > +

+ 로그아웃 하시겠습니까? +

+
+ + +
+
+ )} + + ); +} diff --git a/src/components/store-info/index.tsx b/src/components/store-info/index.tsx index 5008a82..ee4183e 100644 --- a/src/components/store-info/index.tsx +++ b/src/components/store-info/index.tsx @@ -23,7 +23,7 @@ export const FieldContainer = memo(

diff --git a/src/lib/api/config.ts b/src/lib/api/config.ts new file mode 100644 index 0000000..268c9bc --- /dev/null +++ b/src/lib/api/config.ts @@ -0,0 +1,31 @@ +import axios from 'axios'; + +const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:3000'; + +export const apiClient = axios.create({ + baseURL: API_BASE_URL, + timeout: 10000, + withCredentials: true, +}); + +// Request 인터셉터 - 토큰 자동 추가 +apiClient.interceptors.request.use( + config => { + // 로직 추가 예정 -> header에다가 token 아마 ? + return config; + }, + error => Promise.reject(error as Error), +); + +// Response 인터셉터 - 에러 처리 +apiClient.interceptors.response.use( + response => response, + error => { + // if (error.response?.status === 401) { + // // 토큰 만료 시 처리 + // Cookies.remove('accessToken'); + // window.location.href = '/login'; + // } + return Promise.reject(error as Error); + }, +); diff --git a/src/lib/api/types.ts b/src/lib/api/types.ts new file mode 100644 index 0000000..c8bc44b --- /dev/null +++ b/src/lib/api/types.ts @@ -0,0 +1,19 @@ +export interface ApiResponse { + data: T; + message?: string; + status: number; +} + +export interface LoginRequest { + email: string; + password: string; +} + +export interface LoginResponse { + token: string; + user: { + id: string; + email: string; + name: string; + }; +} diff --git a/src/lib/auth/auth.service.ts b/src/lib/auth/auth.service.ts new file mode 100644 index 0000000..9fc3a3f --- /dev/null +++ b/src/lib/auth/auth.service.ts @@ -0,0 +1,17 @@ +import { apiClient } from '../api/config'; +import { ApiResponse, LoginResponse } from '../api/types'; + +export const authService = { + // ... 기존 코드 + + async kakaoLogin(code: string): Promise { + const response = await apiClient.post>( + '/auth/login', + { code }, + ); + + // 토큰 저장 + + return response.data.data; + }, +}; diff --git a/src/lib/auth/kakao.service.ts b/src/lib/auth/kakao.service.ts new file mode 100644 index 0000000..4b4c343 --- /dev/null +++ b/src/lib/auth/kakao.service.ts @@ -0,0 +1,22 @@ +export const kakaoAuthService = { + // 카카오 로그인 URL 생성 + getAuthUrl: () => { + const clientId = process.env.NEXT_PUBLIC_KAKAO_CLIENT_ID; + const redirectUri = process.env.NEXT_PUBLIC_KAKAO_REDIRECT_URI; + + const params = new URLSearchParams({ + response_type: 'code', + client_id: clientId!, + redirect_uri: redirectUri!, + scope: 'profile_nickname,profile_image', // 필요한 권한 + }); + + return `https://kauth.kakao.com/oauth/authorize?${params.toString()}`; + }, + + // 카카오 로그인 실행 + login: () => { + const authUrl = kakaoAuthService.getAuthUrl(); + window.location.href = authUrl; + }, +}; diff --git a/src/styles/globals.css b/src/styles/globals.css index 88e6a9c..a992f02 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -222,4 +222,11 @@ .custom-scrollbar::-webkit-scrollbar-thumb:hover { background: var(--color-orange400); /* 호버시 색상 */ } + + /* ReactPlayer 비디오 전체 화면 스타일 */ + .video-player-container .react-player video { + object-fit: cover !important; + width: 100% !important; + height: 100% !important; + } }