diff --git a/next.config.ts b/next.config.ts
index 0ecd118..aeb8204 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -2,17 +2,6 @@ import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
- // TurboPack 설정
- experimental: {
- turbo: {
- rules: {
- '*.svg': {
- loaders: ['@svgr/webpack'],
- as: '*.js',
- },
- },
- },
- },
// webpack 설정
webpack: (config) => {
// @ts-expect-error 타입 에러 무시
@@ -42,6 +31,14 @@ const nextConfig: NextConfig = {
fileLoaderRule.exclude = /\.svg$/i;
return config;
},
+ turbopack: {
+ rules: {
+ '*.svg': {
+ loaders: ['@svgr/webpack'],
+ as: '*.js',
+ },
+ }
+ },
};
export default nextConfig;
diff --git a/package-lock.json b/package-lock.json
index 0b1c61b..958ff80 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,7 +15,8 @@
"next": "15.5.3",
"react": "19.1.0",
"react-dom": "19.1.0",
- "react-hot-toast": "^2.6.0"
+ "react-hot-toast": "^2.6.0",
+ "react-use": "^17.6.0"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
@@ -1758,6 +1759,15 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/runtime": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz",
+ "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/@babel/template": {
"version": "7.27.2",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
@@ -2499,7 +2509,6 @@
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
- "dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
@@ -3316,6 +3325,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/js-cookie": {
+ "version": "2.2.7",
+ "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.7.tgz",
+ "integrity": "sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==",
+ "license": "MIT"
+ },
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
@@ -3917,6 +3932,12 @@
"win32"
]
},
+ "node_modules/@xobotyi/scrollbar-width": {
+ "version": "1.9.5",
+ "resolved": "https://registry.npmjs.org/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz",
+ "integrity": "sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==",
+ "license": "MIT"
+ },
"node_modules/acorn": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
@@ -4660,6 +4681,15 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/copy-to-clipboard": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz",
+ "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==",
+ "license": "MIT",
+ "dependencies": {
+ "toggle-selection": "^1.0.6"
+ }
+ },
"node_modules/core-js-compat": {
"version": "3.45.1",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.45.1.tgz",
@@ -4716,6 +4746,15 @@
"node": ">= 8"
}
},
+ "node_modules/css-in-js-utils": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz",
+ "integrity": "sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==",
+ "license": "MIT",
+ "dependencies": {
+ "hyphenate-style-name": "^1.0.3"
+ }
+ },
"node_modules/css-select": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz",
@@ -5113,6 +5152,15 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/error-stack-parser": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz",
+ "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==",
+ "license": "MIT",
+ "dependencies": {
+ "stackframe": "^1.3.4"
+ }
+ },
"node_modules/es-abstract": {
"version": "1.24.0",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz",
@@ -5750,7 +5798,6 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
- "dev": true,
"license": "MIT"
},
"node_modules/fast-glob": {
@@ -5797,6 +5844,17 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/fast-shallow-equal": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz",
+ "integrity": "sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw=="
+ },
+ "node_modules/fastest-stable-stringify": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz",
+ "integrity": "sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==",
+ "license": "MIT"
+ },
"node_modules/fastq": {
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
@@ -6230,6 +6288,12 @@
"url": "https://github.com/sponsors/typicode"
}
},
+ "node_modules/hyphenate-style-name": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz",
+ "integrity": "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==",
+ "license": "BSD-3-Clause"
+ },
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -6267,6 +6331,15 @@
"node": ">=0.8.19"
}
},
+ "node_modules/inline-style-prefixer": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-7.0.1.tgz",
+ "integrity": "sha512-lhYo5qNTQp3EvSSp3sRvXMbVQTLrvGV6DycRMJ5dm2BLMiJ30wpXKdDdgX+GmJZ5uQMucwRKHamXSst3Sj/Giw==",
+ "license": "MIT",
+ "dependencies": {
+ "css-in-js-utils": "^3.1.0"
+ }
+ },
"node_modules/internal-slot": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",
@@ -6748,6 +6821,12 @@
"jiti": "lib/jiti-cli.mjs"
}
},
+ "node_modules/js-cookie": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz",
+ "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==",
+ "license": "MIT"
+ },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -7452,6 +7531,54 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/nano-css": {
+ "version": "5.6.2",
+ "resolved": "https://registry.npmjs.org/nano-css/-/nano-css-5.6.2.tgz",
+ "integrity": "sha512-+6bHaC8dSDGALM1HJjOHVXpuastdu2xFoZlC77Jh4cg+33Zcgm+Gxd+1xsnpZK14eyHObSp82+ll5y3SX75liw==",
+ "license": "Unlicense",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.4.15",
+ "css-tree": "^1.1.2",
+ "csstype": "^3.1.2",
+ "fastest-stable-stringify": "^2.0.2",
+ "inline-style-prefixer": "^7.0.1",
+ "rtl-css-js": "^1.16.1",
+ "stacktrace-js": "^2.0.2",
+ "stylis": "^4.3.0"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-dom": "*"
+ }
+ },
+ "node_modules/nano-css/node_modules/css-tree": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
+ "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==",
+ "license": "MIT",
+ "dependencies": {
+ "mdn-data": "2.0.14",
+ "source-map": "^0.6.1"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/nano-css/node_modules/mdn-data": {
+ "version": "2.0.14",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
+ "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
+ "license": "CC0-1.0"
+ },
+ "node_modules/nano-css/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/nano-spawn": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-1.0.3.tgz",
@@ -8095,6 +8222,41 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/react-universal-interface": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/react-universal-interface/-/react-universal-interface-0.6.2.tgz",
+ "integrity": "sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==",
+ "peerDependencies": {
+ "react": "*",
+ "tslib": "*"
+ }
+ },
+ "node_modules/react-use": {
+ "version": "17.6.0",
+ "resolved": "https://registry.npmjs.org/react-use/-/react-use-17.6.0.tgz",
+ "integrity": "sha512-OmedEScUMKFfzn1Ir8dBxiLLSOzhKe/dPZwVxcujweSj45aNM7BEGPb9BEVIgVEqEXx6f3/TsXzwIktNgUR02g==",
+ "license": "Unlicense",
+ "dependencies": {
+ "@types/js-cookie": "^2.2.6",
+ "@xobotyi/scrollbar-width": "^1.9.5",
+ "copy-to-clipboard": "^3.3.1",
+ "fast-deep-equal": "^3.1.3",
+ "fast-shallow-equal": "^1.0.0",
+ "js-cookie": "^2.2.1",
+ "nano-css": "^5.6.2",
+ "react-universal-interface": "^0.6.2",
+ "resize-observer-polyfill": "^1.5.1",
+ "screenfull": "^5.1.0",
+ "set-harmonic-interval": "^1.0.1",
+ "throttle-debounce": "^3.0.1",
+ "ts-easing": "^0.2.0",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-dom": "*"
+ }
+ },
"node_modules/reflect.getprototypeof": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
@@ -8210,6 +8372,12 @@
"node": ">=6"
}
},
+ "node_modules/resize-observer-polyfill": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
+ "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==",
+ "license": "MIT"
+ },
"node_modules/resolve": {
"version": "1.22.10",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
@@ -8286,6 +8454,15 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/rtl-css-js": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.16.1.tgz",
+ "integrity": "sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.1.2"
+ }
+ },
"node_modules/run-parallel": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@@ -8371,6 +8548,18 @@
"integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==",
"license": "MIT"
},
+ "node_modules/screenfull": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/screenfull/-/screenfull-5.2.0.tgz",
+ "integrity": "sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/semver": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
@@ -8418,6 +8607,15 @@
"node": ">= 0.4"
}
},
+ "node_modules/set-harmonic-interval": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz",
+ "integrity": "sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==",
+ "license": "Unlicense",
+ "engines": {
+ "node": ">=6.9"
+ }
+ },
"node_modules/set-proto": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz",
@@ -8639,6 +8837,15 @@
"tslib": "^2.0.3"
}
},
+ "node_modules/source-map": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
+ "integrity": "sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -8655,6 +8862,42 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/stack-generator": {
+ "version": "2.0.10",
+ "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.10.tgz",
+ "integrity": "sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==",
+ "license": "MIT",
+ "dependencies": {
+ "stackframe": "^1.3.4"
+ }
+ },
+ "node_modules/stackframe": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz",
+ "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==",
+ "license": "MIT"
+ },
+ "node_modules/stacktrace-gps": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-3.1.2.tgz",
+ "integrity": "sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==",
+ "license": "MIT",
+ "dependencies": {
+ "source-map": "0.5.6",
+ "stackframe": "^1.3.4"
+ }
+ },
+ "node_modules/stacktrace-js": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.2.tgz",
+ "integrity": "sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==",
+ "license": "MIT",
+ "dependencies": {
+ "error-stack-parser": "^2.0.6",
+ "stack-generator": "^2.0.5",
+ "stacktrace-gps": "^3.0.4"
+ }
+ },
"node_modules/stop-iteration-iterator": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
@@ -8871,6 +9114,12 @@
}
}
},
+ "node_modules/stylis": {
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz",
+ "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==",
+ "license": "MIT"
+ },
"node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -8980,6 +9229,15 @@
"node": ">=18"
}
},
+ "node_modules/throttle-debounce": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-3.0.1.tgz",
+ "integrity": "sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/tinyglobby": {
"version": "0.2.15",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
@@ -9041,6 +9299,12 @@
"node": ">=8.0"
}
},
+ "node_modules/toggle-selection": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
+ "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==",
+ "license": "MIT"
+ },
"node_modules/ts-api-utils": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
@@ -9054,6 +9318,12 @@
"typescript": ">=4.8.4"
}
},
+ "node_modules/ts-easing": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/ts-easing/-/ts-easing-0.2.0.tgz",
+ "integrity": "sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==",
+ "license": "Unlicense"
+ },
"node_modules/tsconfig-paths": {
"version": "3.15.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
diff --git a/package.json b/package.json
index 3458d02..d1469b9 100644
--- a/package.json
+++ b/package.json
@@ -24,7 +24,8 @@
"next": "15.5.3",
"react": "19.1.0",
"react-dom": "19.1.0",
- "react-hot-toast": "^2.6.0"
+ "react-hot-toast": "^2.6.0",
+ "react-use": "^17.6.0"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
diff --git a/src/api/index.ts b/src/api/index.ts
deleted file mode 100644
index 344a3d3..0000000
--- a/src/api/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-// 서버에서 불러오는 것
-
-// 공통불러오는 것
-// 페이지 불러오는 것
diff --git a/src/app/api/login/set-pre-login-path/route.ts b/src/app/api/login/set-pre-login-path/route.ts
new file mode 100644
index 0000000..248d5eb
--- /dev/null
+++ b/src/app/api/login/set-pre-login-path/route.ts
@@ -0,0 +1,18 @@
+import { NextRequest, NextResponse } from 'next/server';
+
+export async function POST(req: NextRequest) {
+ const { path } = await req.json(); // 클라이언트가 보내는 페이지 경로
+ const res = NextResponse.json({ ok: true });
+
+ res.cookies.set({
+ name: 'preLoginPath',
+ value: path,
+ path: '/',
+ maxAge: 60 * 30, // 30분
+ httpOnly: false, // JS에서 읽을 수 있게
+ secure: process.env.NODE_ENV === 'production',
+ sameSite: 'lax',
+ });
+
+ return res;
+}
diff --git a/src/app/community/[id]/page.tsx b/src/app/community/[id]/page.tsx
new file mode 100644
index 0000000..0604213
--- /dev/null
+++ b/src/app/community/[id]/page.tsx
@@ -0,0 +1,29 @@
+import CommentList from '@/domains/community/detail/comment/CommentList';
+import DetailComment from '@/domains/community/detail/comment/DetailComment';
+import DetailContent from '@/domains/community/detail/DetailContent';
+import DetailHeader from '@/domains/community/detail/DetailHeader';
+import DetailTitle from '@/domains/community/detail/DetailTitle';
+import DetailTabDesktop from '@/domains/community/detail/tab/DetailTabDesktop';
+import StarBg from '@/domains/shared/starBg/StarBg';
+
+function Page() {
+ return (
+
+ );
+}
+
+export default Page;
diff --git a/src/app/community/page.tsx b/src/app/community/page.tsx
index 84390d4..f22bf8d 100644
--- a/src/app/community/page.tsx
+++ b/src/app/community/page.tsx
@@ -1,37 +1,46 @@
-import CommunityFilter from '@/shared/components/community/CommunityFilter';
-import CommunityHeader from '@/shared/components/community/CommunityHeader';
-import CommunityTab from '@/shared/components/community/CommunityTab';
-import PostCard from '@/shared/components/community/PostCard';
-import WriteBtn from '@/shared/components/community/WriteBtn';
+import CommunityFilter from '@/domains/community/main/CommunityFilter';
+import CommunityTab from '@/domains/community/main/CommunityTab';
+import PostCard from '@/domains/community/main/PostCard';
+import WriteBtn from '@/domains/community/main/WriteBtn';
+
+import PageHeader from '@/domains/shared/pageHeader/PageHeader';
+import { Metadata } from 'next';
+
+export const metadata: Metadata = {
+ title: '커뮤니티',
+ description: '칵테일에 관한 모든 이야기',
+};
function Page() {
return (
-
-
-
+
);
}
export default Page;
diff --git a/src/app/community/write/page.tsx b/src/app/community/write/page.tsx
new file mode 100644
index 0000000..d52dcb0
--- /dev/null
+++ b/src/app/community/write/page.tsx
@@ -0,0 +1,5 @@
+function Page() {
+ return
;
+}
+
+export default Page;
diff --git a/src/app/design-system/page.tsx b/src/app/design-system/page.tsx
index ad3e5cb..048b3f6 100644
--- a/src/app/design-system/page.tsx
+++ b/src/app/design-system/page.tsx
@@ -6,12 +6,13 @@ import Input from '@/shared/components/InputBox/Input';
import { useState } from 'react';
import { customToast } from '@/shared/components/toast/CustomToastUtils';
import ModalLayout from '@/shared/components/modalPop/ModalLayout';
-import ConfirmPop from '@/shared/components/modalPop/ConfirmPop';
-import SelectBox from '@/shared/components/InputBox/SelectBox';
-import LikeBtn from '@/shared/components/like/LikeBtn';
-import Share from '@/shared/components/share/Share';
-import Keep from '@/shared/components/keep/Keep';
+import SelectBox from '@/domains/shared/select-box/SelectBox';
+
import Spinner from '@/shared/components/spinner/Spinner';
+import LikeBtn from '@/domains/community/components/like/LikeBtn';
+import Share from '@/domains/shared/share/Share';
+import Keep from '@/domains/shared/keep/Keep';
+import ConfirmModal from '@/shared/components/modalPop/ConfirmModal';
function Page() {
const [isModalOpen, setModalOpen] = useState(false);
@@ -128,7 +129,7 @@ function Page() {
모달팝업 내용
-
setConfirmOpen(false)}
title="Confirm제목"
@@ -141,12 +142,12 @@ function Page() {
Icons
like
-
+
Share
-
-
+
+
keep
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 16084e3..cef553c 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -18,10 +18,7 @@ export default function RootLayout({
-
-
- {children}
-
+
{children}
diff --git a/src/app/login/first-user/page.tsx b/src/app/login/first-user/page.tsx
deleted file mode 100644
index 7f902a4..0000000
--- a/src/app/login/first-user/page.tsx
+++ /dev/null
@@ -1,7 +0,0 @@
-import LoginRedirectHandler from '@/shared/components/auth/LoginRedirectHandler';
-
-function Page() {
- return
;
-}
-
-export default Page;
diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx
index d1e4fb8..d2548bb 100644
--- a/src/app/login/page.tsx
+++ b/src/app/login/page.tsx
@@ -1,7 +1,7 @@
import Image from 'next/image';
import type { Metadata } from 'next';
import loginBg from '@/shared/assets/images/login_bg.webp';
-import SocialLogin from './SocialLogin';
+import SocialLogin from '@/domains/login/main/SocialLogin';
export const metadata: Metadata = {
title: 'SSOUL | 로그인',
diff --git a/src/app/login/success/page.tsx b/src/app/login/success/page.tsx
index 15766eb..48cef7d 100644
--- a/src/app/login/success/page.tsx
+++ b/src/app/login/success/page.tsx
@@ -1,4 +1,4 @@
-import LoginRedirectHandler from '@/shared/components/auth/LoginRedirectHandler';
+import LoginRedirectHandler from '@/domains/login/components/LoginRedirectHandler';
function Page() {
return
;
diff --git a/src/app/login/user/first-user/page.tsx b/src/app/login/user/first-user/page.tsx
new file mode 100644
index 0000000..131a5e1
--- /dev/null
+++ b/src/app/login/user/first-user/page.tsx
@@ -0,0 +1,7 @@
+import LoginRedirectHandler from '@/domains/login/components/LoginRedirectHandler';
+
+function Page() {
+ return
;
+}
+
+export default Page;
diff --git a/src/app/login/user/success/page.tsx b/src/app/login/user/success/page.tsx
new file mode 100644
index 0000000..48cef7d
--- /dev/null
+++ b/src/app/login/user/success/page.tsx
@@ -0,0 +1,6 @@
+import LoginRedirectHandler from '@/domains/login/components/LoginRedirectHandler';
+
+function Page() {
+ return
;
+}
+export default Page;
diff --git a/src/app/recipe/[id]/page.tsx b/src/app/recipe/[id]/page.tsx
index 9d89ac4..8eec717 100644
--- a/src/app/recipe/[id]/page.tsx
+++ b/src/app/recipe/[id]/page.tsx
@@ -1,9 +1,11 @@
-import StarBg from '@/shared/components/starBg/StarBg';
+import DetailMain from '@/domains/recipe/details/DetailMain';
+import StarBg from '@/domains/shared/starBg/StarBg';
function page() {
return (
-
-
+
+
+
);
}
diff --git a/src/app/recipe/page.tsx b/src/app/recipe/page.tsx
index 9f5e417..e2383b7 100644
--- a/src/app/recipe/page.tsx
+++ b/src/app/recipe/page.tsx
@@ -1,10 +1,10 @@
-import PageHeader from '@/shared/components/pageHeader/PageHeader';
import { Metadata } from 'next';
-import Glass from '@/shared/assets/images/recipe_page_header.webp';
-import SelectBox from '@/shared/components/InputBox/SelectBox';
+import SelectBox from '@/domains/shared/select-box/SelectBox';
import Input from '@/shared/components/InputBox/Input';
-import CocktailList from '@/shared/components/recipePage/cocktailList/CocktailList';
-import Accordion from './components/Accordion';
+
+import Accordion from '../../domains/recipe/components/main/Accordion';
+import CocktailList from '@/domains/recipe/CocktailList';
+import PageHeader from '@/domains/shared/pageHeader/PageHeader';
export const metadata: Metadata = {
title: 'SSOUL | 칵테일레시피',
@@ -15,11 +15,7 @@ function Page() {
return (
diff --git a/src/app/recommend/page.tsx b/src/app/recommend/page.tsx
index e8575e9..ee68e29 100644
--- a/src/app/recommend/page.tsx
+++ b/src/app/recommend/page.tsx
@@ -1,7 +1,7 @@
+import ChatForm from '@/domains/recommend/components/ChatForm';
+import MyChat from '@/domains/recommend/components/MyChat';
+import SsuryChat from '@/domains/recommend/components/SsuryChat';
import Bg from '@/shared/assets/images/recommend_bg.webp';
-import ChatForm from './components/ChatForm';
-import SsuryChat from './components/SsuryChat';
-import MyChat from './components/MyChat';
function Page() {
return (
diff --git a/src/shared/components/community/CommunityFilter.tsx b/src/domains/community/CommunityFilter.tsx
similarity index 87%
rename from src/shared/components/community/CommunityFilter.tsx
rename to src/domains/community/CommunityFilter.tsx
index c8deedd..fa7b129 100644
--- a/src/shared/components/community/CommunityFilter.tsx
+++ b/src/domains/community/CommunityFilter.tsx
@@ -1,6 +1,7 @@
'use client';
-import SelectBox from '../InputBox/SelectBox';
+import SelectBox from '../shared/select-box/SelectBox';
+
function CommunityFilter() {
return (
+ );
+}
+
+export default CommunityHeader;
diff --git a/src/shared/components/community/CommunityTab.tsx b/src/domains/community/CommunityTab.tsx
similarity index 100%
rename from src/shared/components/community/CommunityTab.tsx
rename to src/domains/community/CommunityTab.tsx
diff --git a/src/shared/components/community/PostCard.tsx b/src/domains/community/PostCard.tsx
similarity index 67%
rename from src/shared/components/community/PostCard.tsx
rename to src/domains/community/PostCard.tsx
index cc672e8..2514a24 100644
--- a/src/shared/components/community/PostCard.tsx
+++ b/src/domains/community/PostCard.tsx
@@ -1,11 +1,13 @@
import Image from 'next/image';
import prePost from '@/shared/assets/images/prepost_img.webp';
-import PostLabel from './PostLabel';
+
+import PostInfo from './PostInfo';
+import Label from '../shared/label/Label';
function PostCard({ label }: { label: string }) {
return (
-
+
@@ -14,18 +16,7 @@ function PostCard({ label }: { label: string }) {
칵테일 처음 만들어 보는데 랄랄
가나다라마바사아자차카파타하
-
- - 실버븬
- - |
- - 3분 전
- - |
- - 조회 3
- - |
- - 댓글 3
-
+
+ {hasUserName && (
+ <>
+ 실버븬
+ |
+ >
+ )}
+ 3분 전
+ |
+ 조회 3
+ |
+ 댓글 3
+
+ );
+}
+
+export default PostInfo;
diff --git a/src/domains/community/WriteBtn.tsx b/src/domains/community/WriteBtn.tsx
new file mode 100644
index 0000000..f6a8752
--- /dev/null
+++ b/src/domains/community/WriteBtn.tsx
@@ -0,0 +1,27 @@
+'use client';
+
+import Write from '@/shared/assets/icons/edit_28.svg';
+import { useRouter } from 'next/navigation';
+
+type RouterType = ReturnType;
+
+function WriteBtn() {
+ const router = useRouter();
+
+ const handleClick = (router: RouterType) => {
+ router.push('/community/write');
+ };
+
+ return (
+
+ );
+}
+
+export default WriteBtn;
diff --git a/src/domains/community/components/comment/CommentBtn.tsx b/src/domains/community/components/comment/CommentBtn.tsx
new file mode 100644
index 0000000..b3f497f
--- /dev/null
+++ b/src/domains/community/components/comment/CommentBtn.tsx
@@ -0,0 +1,25 @@
+import CommentIcon from '@/shared/assets/icons/comment_28.svg';
+import { useState } from 'react';
+
+function CommentBtn({ size }: { size: 'sm' | 'md' }) {
+ const [isClick, setIsClick] = useState(false);
+
+ const handleClick = () => {
+ setIsClick(!isClick);
+ };
+ return (
+
+ );
+}
+export default CommentBtn;
diff --git a/src/shared/components/like/LikeBtn.tsx b/src/domains/community/components/like/LikeBtn.tsx
similarity index 76%
rename from src/shared/components/like/LikeBtn.tsx
rename to src/domains/community/components/like/LikeBtn.tsx
index 48cbf5c..0ba3be4 100644
--- a/src/shared/components/like/LikeBtn.tsx
+++ b/src/domains/community/components/like/LikeBtn.tsx
@@ -1,7 +1,7 @@
import LikeIcon from '@/shared/assets/icons/like_28.svg';
import { useState } from 'react';
-function LikeBtn() {
+function LikeBtn({ size }: { size: 'sm' | 'md' }) {
const [isClick, setIsClick] = useState(false);
const handleClick = () => {
@@ -10,7 +10,7 @@ function LikeBtn() {
return (
+ );
+}
+
+export default CocktailTag;
diff --git a/src/domains/community/detail/DetailContent.tsx b/src/domains/community/detail/DetailContent.tsx
new file mode 100644
index 0000000..bcf5f65
--- /dev/null
+++ b/src/domains/community/detail/DetailContent.tsx
@@ -0,0 +1,46 @@
+import Image from 'next/image';
+import prePost from '@/shared/assets/images/prepost_img.webp';
+
+import PostInfo from '../main/PostInfo';
+
+import CocktailTag from './CocktailTag';
+import DetailTabMobile from './tab/DetailTabMobile';
+
+function DetailContent() {
+ return (
+
+
+
+
+
+ 내용은 이거입니다 하하하
+ 그런가요 안녕하세요
+
+ 글입니다다다다다다다다다다다다
+ 내용은 이거입니다 하하하
+ 그런가요 안녕하세요
+
+ 글입니다다다다다다다다다다다다
+ 내용은 이거입니다 하하하
+ 그런가요 안녕하세요
+
+ 글입니다다다다다다다다다다다다
+ 내용은 이거입니다 하하하
+ 그런가요 안녕하세요
+
+ 글입니다다다다다다다다다다다다
+ 내용은 이거입니다 하하하
+ 그런가요 안녕하세요
+
+ 글입니다다다다다다다다다다다다
+
+
+
+
+
+
+
+ );
+}
+
+export default DetailContent;
diff --git a/src/domains/community/detail/DetailHeader.tsx b/src/domains/community/detail/DetailHeader.tsx
new file mode 100644
index 0000000..9eb4300
--- /dev/null
+++ b/src/domains/community/detail/DetailHeader.tsx
@@ -0,0 +1,13 @@
+import Label from '@/domains/shared/label/Label';
+import EditDelete from './EditDelete';
+
+function DetailHeader() {
+ return (
+
+ );
+}
+
+export default DetailHeader;
diff --git a/src/domains/community/detail/DetailTitle.tsx b/src/domains/community/detail/DetailTitle.tsx
new file mode 100644
index 0000000..1850aff
--- /dev/null
+++ b/src/domains/community/detail/DetailTitle.tsx
@@ -0,0 +1,12 @@
+import Profile from './Profile';
+
+function DetailTitle() {
+ return (
+
+ );
+}
+
+export default DetailTitle;
diff --git a/src/domains/community/detail/EditDelete.tsx b/src/domains/community/detail/EditDelete.tsx
new file mode 100644
index 0000000..a07aec5
--- /dev/null
+++ b/src/domains/community/detail/EditDelete.tsx
@@ -0,0 +1,25 @@
+function EditDelete({ use }: { use: 'post' | 'comment' }) {
+ return (
+
+
+ |
+
+
+ );
+}
+
+export default EditDelete;
diff --git a/src/domains/community/detail/Profile.tsx b/src/domains/community/detail/Profile.tsx
new file mode 100644
index 0000000..6a07849
--- /dev/null
+++ b/src/domains/community/detail/Profile.tsx
@@ -0,0 +1,12 @@
+function Profile() {
+ return (
+
+ );
+}
+
+export default Profile;
diff --git a/src/domains/community/detail/comment/CommentHeader.tsx b/src/domains/community/detail/comment/CommentHeader.tsx
new file mode 100644
index 0000000..822736a
--- /dev/null
+++ b/src/domains/community/detail/comment/CommentHeader.tsx
@@ -0,0 +1,17 @@
+import EditDelete from '../EditDelete';
+import Profile from '../Profile';
+
+function CommentHeader() {
+ return (
+
+ );
+}
+
+export default CommentHeader;
diff --git a/src/domains/community/detail/comment/CommentList.tsx b/src/domains/community/detail/comment/CommentList.tsx
new file mode 100644
index 0000000..137e2ec
--- /dev/null
+++ b/src/domains/community/detail/comment/CommentList.tsx
@@ -0,0 +1,26 @@
+import CommentHeader from './CommentHeader';
+
+function CommentList() {
+ return (
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+
+ );
+}
+
+export default CommentList;
diff --git a/src/domains/community/detail/comment/DetailComment.tsx b/src/domains/community/detail/comment/DetailComment.tsx
new file mode 100644
index 0000000..1f747df
--- /dev/null
+++ b/src/domains/community/detail/comment/DetailComment.tsx
@@ -0,0 +1,21 @@
+import Button from '@/shared/components/button/Button';
+import Input from '@/shared/components/InputBox/Input';
+
+function DetailComment() {
+ return (
+
+ );
+}
+
+export default DetailComment;
diff --git a/src/domains/community/detail/tab/DetailTabDesktop.tsx b/src/domains/community/detail/tab/DetailTabDesktop.tsx
new file mode 100644
index 0000000..d0ccdfa
--- /dev/null
+++ b/src/domains/community/detail/tab/DetailTabDesktop.tsx
@@ -0,0 +1,32 @@
+'use client';
+
+import Share from '@/domains/shared/share/Share';
+import CommentBtn from '../../components/comment/CommentBtn';
+import LikeBtn from '../../components/like/LikeBtn';
+
+function DetailTabDesktop() {
+ return (
+
+
+
+
+
+ 2
+
+
+
+ 2
+
+
+
+
+
+
+
+ );
+}
+
+export default DetailTabDesktop;
diff --git a/src/domains/community/detail/tab/DetailTabMobile.tsx b/src/domains/community/detail/tab/DetailTabMobile.tsx
new file mode 100644
index 0000000..042413d
--- /dev/null
+++ b/src/domains/community/detail/tab/DetailTabMobile.tsx
@@ -0,0 +1,25 @@
+'use client';
+
+import Share from '@/domains/shared/share/Share';
+import LikeBtn from '../../components/like/LikeBtn';
+
+function DetailTabMobile() {
+ return (
+
+ );
+}
+
+export default DetailTabMobile;
diff --git a/src/domains/community/main/CommunityFilter.tsx b/src/domains/community/main/CommunityFilter.tsx
new file mode 100644
index 0000000..8c33c81
--- /dev/null
+++ b/src/domains/community/main/CommunityFilter.tsx
@@ -0,0 +1,17 @@
+'use client';
+
+import SelectBox from '../../shared/select-box/SelectBox';
+
+function CommunityFilter() {
+ return (
+
+ );
+}
+
+export default CommunityFilter;
diff --git a/src/domains/community/main/CommunityHeader.tsx b/src/domains/community/main/CommunityHeader.tsx
new file mode 100644
index 0000000..9ef4ab6
--- /dev/null
+++ b/src/domains/community/main/CommunityHeader.tsx
@@ -0,0 +1,11 @@
+import PageHeader from '../../shared/pageHeader/PageHeader';
+
+function CommunityHeader() {
+ return (
+
+ );
+}
+
+export default CommunityHeader;
diff --git a/src/domains/community/main/CommunityTab.tsx b/src/domains/community/main/CommunityTab.tsx
new file mode 100644
index 0000000..06ea6ca
--- /dev/null
+++ b/src/domains/community/main/CommunityTab.tsx
@@ -0,0 +1,42 @@
+'use client';
+
+import tw from '@/shared/utills/tw';
+import { useState } from 'react';
+
+const tabItem = [
+ { title: '전체' },
+ { title: '레시피' },
+ { title: '팁' },
+ { title: '질문' },
+ { title: '자유' },
+];
+
+function CommunityTab() {
+ const [selectedIdx, setSelectedIdx] = useState(0);
+
+ return (
+
+
+
+ {tabItem.map(({ title }, idx) => (
+
+ ))}
+
+
+
+ );
+}
+
+export default CommunityTab;
diff --git a/src/domains/community/main/PostCard.tsx b/src/domains/community/main/PostCard.tsx
new file mode 100644
index 0000000..c08cbc1
--- /dev/null
+++ b/src/domains/community/main/PostCard.tsx
@@ -0,0 +1,35 @@
+import Image from 'next/image';
+import prePost from '@/shared/assets/images/prepost_img.webp';
+
+import PostInfo from './PostInfo';
+import Label from '../../shared/label/Label';
+
+function PostCard({ label }: { label: string }) {
+ return (
+
+
+
+
+
+
칵테일 만들 때 준비물
+
+
칵테일 처음 만들어 보는데 랄랄
+
가나다라마바사아자차카파타하
+
+
+
+
+
+
+
+
+ );
+}
+
+export default PostCard;
diff --git a/src/domains/community/main/PostInfo.tsx b/src/domains/community/main/PostInfo.tsx
new file mode 100644
index 0000000..80d7243
--- /dev/null
+++ b/src/domains/community/main/PostInfo.tsx
@@ -0,0 +1,22 @@
+function PostInfo({ hasUserName = false }: { hasUserName?: boolean }) {
+ return (
+
+ {hasUserName && (
+ <>
+ - 실버븬
+ - |
+ >
+ )}
+ - 3분 전
+ - |
+ - 조회 3
+ - |
+ - 댓글 3
+
+ );
+}
+
+export default PostInfo;
diff --git a/src/domains/community/main/WriteBtn.tsx b/src/domains/community/main/WriteBtn.tsx
new file mode 100644
index 0000000..6b60af7
--- /dev/null
+++ b/src/domains/community/main/WriteBtn.tsx
@@ -0,0 +1,30 @@
+'use client';
+
+import Write from '@/shared/assets/icons/edit_28.svg';
+import { useRouter } from 'next/navigation';
+import Button from '@/shared/components/button/Button';
+
+type RouterType = ReturnType
;
+
+function WriteBtn() {
+ const router = useRouter();
+
+ const handleClick = (router: RouterType) => {
+ router.push('/community/write');
+ };
+
+ return (
+
+ );
+}
+
+export default WriteBtn;
diff --git a/src/domains/login/components/LoginRedirectHandler.tsx b/src/domains/login/components/LoginRedirectHandler.tsx
new file mode 100644
index 0000000..79d03b1
--- /dev/null
+++ b/src/domains/login/components/LoginRedirectHandler.tsx
@@ -0,0 +1,87 @@
+'use client';
+
+import { useEffect, useState } from 'react';
+import { usePathname, useRouter } from 'next/navigation';
+import { customToast } from '@/shared/components/toast/CustomToastUtils';
+import { getCookie, removeCookie } from '@/domains/shared/auth/utils/cookie';
+import { useAuthStore } from '@/domains/shared/store/auth';
+import Spinner from '@/shared/components/spinner/Spinner';
+import WelcomeModal from '@/domains/login/components/WelcomeModal';
+
+function LoginRedirectHandler() {
+ const pathname = usePathname();
+ const router = useRouter();
+ const { user, updateUser } = useAuthStore();
+ const [loading, setLoading] = useState(true);
+ const [welcomeModalOpen, setWelcomeModalOpen] = useState(false);
+
+ useEffect(() => {
+ if (!user && loading) {
+ updateUser()
+ .then((fetchedUser) => {
+ if (!fetchedUser) {
+ router.replace('/login');
+ }
+ })
+ .catch(() => {
+ router.replace('/login');
+ })
+ .finally(() => setLoading(false));
+ } else {
+ setLoading(false);
+ }
+ }, [user, loading, updateUser, router]);
+
+ useEffect(() => {
+ if (!user || loading) return;
+
+ const preLoginPath = getCookie('preLoginPath') || '/';
+ // 로그인 상태인데 이전 페이지가 /login이면 메인으로 이동
+ if (user && preLoginPath === '/login') {
+ router.replace('/');
+ removeCookie('preLoginPath');
+ return;
+ }
+
+ // 첫 유저일 경우 모달 오픈
+ if (pathname.startsWith('/login/user/first-user')) {
+ setWelcomeModalOpen(true);
+ }
+ // 기존 유저일 경우
+ else if (pathname.startsWith('/login/user/success')) {
+ customToast.success(`${user.nickname}님 \n 로그인 성공 🎉`);
+ router.replace(preLoginPath);
+ removeCookie('preLoginPath');
+ }
+ }, [pathname, user, loading, router]);
+
+ // 환영 모달 닫힐 때 이동
+ const handleCloseWelcomeModal = () => {
+ setWelcomeModalOpen(false);
+ const preLoginPath = getCookie('preLoginPath') || '/';
+ removeCookie('preLoginPath');
+ router.replace(preLoginPath);
+ };
+
+ if (loading) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+ <>
+ {/* 첫 유저 모달 */}
+ {user && (
+
+ )}
+ >
+ );
+}
+export default LoginRedirectHandler;
diff --git a/src/domains/login/components/LogoutConfirm.tsx b/src/domains/login/components/LogoutConfirm.tsx
new file mode 100644
index 0000000..b7f5f26
--- /dev/null
+++ b/src/domains/login/components/LogoutConfirm.tsx
@@ -0,0 +1,20 @@
+import ConfirmModal from '@/shared/components/modalPop/ConfirmModal';
+
+interface Props {
+ open: boolean;
+ onClose: () => void;
+ onLogout: () => void;
+}
+
+function LogoutConfirm({ open, onClose, onLogout }: Props) {
+ return (
+
+ );
+}
+export default LogoutConfirm;
diff --git a/src/shared/components/auth/Welcome.tsx b/src/domains/login/components/WelcomeModal.tsx
similarity index 71%
rename from src/shared/components/auth/Welcome.tsx
rename to src/domains/login/components/WelcomeModal.tsx
index b479480..f4cd3b1 100644
--- a/src/shared/components/auth/Welcome.tsx
+++ b/src/domains/login/components/WelcomeModal.tsx
@@ -6,21 +6,21 @@ import Button from '@/shared/components/button/Button';
import ModalLayout from '@/shared/components/modalPop/ModalLayout';
import Ssury from '@/shared/assets/ssury/ssury_jump.webp';
import { useRouter } from 'next/navigation';
-import { useModalStore } from '@/shared/@store/modal';
-import { useAuthStore } from '@/shared/@store/auth';
-function Welcome() {
- const router = useRouter();
- const { user } = useAuthStore();
- const { welcomeModal, closeWelcomeModal } = useModalStore();
+interface Props {
+ userNickname: string;
+ open: boolean;
+ onClose: () => void;
+}
- if (!welcomeModal.open || !user) return null;
+function Welcome({ userNickname, open, onClose }: Props) {
+ const router = useRouter();
return (
@@ -28,7 +28,7 @@ function Welcome() {
type="button"
color="purple"
onClick={() => {
- closeWelcomeModal();
+ onClose();
router.push('/recipe');
}}
>
@@ -37,7 +37,7 @@ function Welcome() {