From 1efc145e3dd9b74e90ed27ffc7240f2da2707144 Mon Sep 17 00:00:00 2001 From: jiminyee Date: Mon, 16 Dec 2024 23:49:19 +0900 Subject: [PATCH 01/13] =?UTF-8?q?=F0=9F=92=84=20=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=EA=B0=80=EC=9E=85=20=ED=8E=98=EC=9D=B4=EC=A7=80=20UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/signup/index.tsx | 88 +++++++++++++++++++++++++++--------------- 1 file changed, 56 insertions(+), 32 deletions(-) diff --git a/pages/signup/index.tsx b/pages/signup/index.tsx index 0e3432d..5c6d213 100644 --- a/pages/signup/index.tsx +++ b/pages/signup/index.tsx @@ -1,12 +1,15 @@ import { useState } from 'react'; - +import { useRouter } from 'next/router'; import InputField from '@/components/Input'; +import Button from '@/components/Button'; const SignUp: React.FC = () => { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [passwordConfirm, setPasswordConfirm] = useState(''); + const [isSubmitting, setIsSubmitting] = useState(false); const [name, setName] = useState(''); + const router = useRouter(); const handleEmailChange = (e: React.ChangeEvent) => { setEmail(e.target.value); @@ -28,44 +31,65 @@ const SignUp: React.FC = () => { const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); + if (isSubmitting) return; // 이미 요청 중인 경우 중복 요청 방지 alert('가입이 완료되었습니다'); }; return ( -
- +
+ +

+ 회원가입 +

+ - + - + - - + + +
+ 이미 회원이신가요?{' '} + + 로그인하기 + +
+ +
); }; From dc5317977245076b404bebd7a8cc3c80185b54b7 Mon Sep 17 00:00:00 2001 From: haksoo Date: Mon, 16 Dec 2024 16:49:01 +0900 Subject: [PATCH 02/13] =?UTF-8?q?=F0=9F=93=A6=20react-quill-new=20?= =?UTF-8?q?=ED=8C=A8=ED=82=A4=EC=A7=80=20=EC=84=A4=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 84 +++++++++++++++++++++++++++++++++++++++++++++-- package.json | 3 +- 2 files changed, 84 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 23e59c5..dc6b76e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,8 @@ "next": "^15.1.0", "postcss-nesting": "^13.0.1", "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "react-quill-new": "^3.3.3" }, "devDependencies": { "@eslint/eslintrc": "^3.2.0", @@ -2905,6 +2906,12 @@ "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2916,7 +2923,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true, "license": "Apache-2.0" }, "node_modules/fast-glob": { @@ -4099,6 +4105,30 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "license": "MIT" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -4551,6 +4581,12 @@ "dev": true, "license": "BlueOak-1.0.0" }, + "node_modules/parchment": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/parchment/-/parchment-3.0.0.tgz", + "integrity": "sha512-HUrJFQ/StvgmXRcQ1ftY6VEZUq3jA2t9ncFN4F84J/vN0/FPpQF+8FKXb3l6fLces6q0uOHj6NJn+2xvZnxO6A==", + "license": "BSD-3-Clause" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -5055,6 +5091,35 @@ ], "license": "MIT" }, + "node_modules/quill": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/quill/-/quill-2.0.3.tgz", + "integrity": "sha512-xEYQBqfYx/sfb33VJiKnSJp8ehloavImQ2A6564GAbqG55PGw1dAWUn1MUbQB62t0azawUS2CZZhWCjO8gRvTw==", + "license": "BSD-3-Clause", + "dependencies": { + "eventemitter3": "^5.0.1", + "lodash-es": "^4.17.21", + "parchment": "^3.0.0", + "quill-delta": "^5.1.0" + }, + "engines": { + "npm": ">=8.2.3" + } + }, + "node_modules/quill-delta": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-5.1.0.tgz", + "integrity": "sha512-X74oCeRI4/p0ucjb5Ma8adTXd9Scumz367kkMK5V/IatcX6A0vlgLgKbzXWy5nZmCGeNJm2oQX0d2Eqj+ZIlCA==", + "license": "MIT", + "dependencies": { + "fast-diff": "^1.3.0", + "lodash.clonedeep": "^4.5.0", + "lodash.isequal": "^4.5.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/react": { "version": "19.0.0", "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", @@ -5083,6 +5148,21 @@ "dev": true, "license": "MIT" }, + "node_modules/react-quill-new": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/react-quill-new/-/react-quill-new-3.3.3.tgz", + "integrity": "sha512-jxbm1QUJlkuGUpc9/GUgGw5USLHdp43H0M7AufqS3V+zRLng9uqLeVBGjXYqEbUKi8QVOM4SClSV3F7kVNj68w==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.21", + "quill": "~2.0.2" + }, + "peerDependencies": { + "quill-delta": "^5.1.0", + "react": "^16 || ^17 || ^18 || ^19", + "react-dom": "^16 || ^17 || ^18 || ^19" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", diff --git a/package.json b/package.json index 83b86a9..89772cf 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,8 @@ "next": "^15.1.0", "postcss-nesting": "^13.0.1", "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "react-quill-new": "^3.3.3" }, "devDependencies": { "@eslint/eslintrc": "^3.2.0", From 310ec73c817a680c90e55a289519df550e9f287b Mon Sep 17 00:00:00 2001 From: haksoo Date: Mon, 16 Dec 2024 16:50:54 +0900 Subject: [PATCH 03/13] =?UTF-8?q?=E2=9C=A8=20TextEditor=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80,=20editor=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/TextEditor.tsx | 50 +++++++++++++++++++++++++++++++++++++ pages/test/editor.tsx | 52 +++++++++++++++++++++++++++++++++++++++ styles/globals.css | 33 +++++++++++++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 components/TextEditor.tsx create mode 100644 pages/test/editor.tsx diff --git a/components/TextEditor.tsx b/components/TextEditor.tsx new file mode 100644 index 0000000..cff4d89 --- /dev/null +++ b/components/TextEditor.tsx @@ -0,0 +1,50 @@ +import dynamic from 'next/dynamic'; + +import 'react-quill-new/dist/quill.snow.css'; + +// ref: https://www.npmjs.com/package/react-quill-new +const QuillEditor = dynamic(() => import('react-quill-new'), { + ssr: false, + loading: () =>

편집기 불러오는 중...

, +}); + +interface Props { + value?: string; + onChange: (value: string) => void; +} + +/** + * 텍스트 에디터 컴포넌트 + * @param {object} props + * @param {string} props.value - 초기 값 + * @param {function} props.onChange - 값 변경시 콜백 함수 + * @returns {JSX.Element} + */ +export default function TextEditor({ value = '', onChange }: Props) { + const modules = { + toolbar: { + container: [ + [{ header: [1, 2, 3, false] }], + ['bold', 'italic', 'underline'], + [{ align: null }, { align: 'center' }, { align: 'right' }], + [{ list: 'ordered' }, { list: 'bullet' }], + ['blockquote', 'link', 'image'], + ], + }, + }; + + const handleChange = (value: string) => { + onChange(value); + }; + + return ( + + ); +} diff --git a/pages/test/editor.tsx b/pages/test/editor.tsx new file mode 100644 index 0000000..f89958c --- /dev/null +++ b/pages/test/editor.tsx @@ -0,0 +1,52 @@ +import { useState } from 'react'; + +import TextEditor from '@/components/TextEditor'; + +const cellStyle = 'px-4 py-2'; +const trStyle = 'border-b'; + +export default function Editor() { + const [value, setValue] = useState(''); + + const handleChange = (v: string) => { + console.log('value', v); + setValue(v); + }; + + return ( +
+ + + + + + + + + + + + + +
+ props + + example +
+
    +
  • value: string
  • +
  • onChange: (value: string) => void
  • +
+
+
+ +
+
+

+ 참고: 에디터의 크기는 에디터 부모의 100%가 적용되니 부모의 + 크기를 정의하시면 됩니다. +

+
+
+ ); +} diff --git a/styles/globals.css b/styles/globals.css index 5a6ef0a..b3d8f34 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -51,3 +51,36 @@ body { color: var(--gray-500); background: var(--background); } + +/* quill editor custom style */ +.quill-custom { + @apply grid h-full w-full; + grid-template-rows: 1fr max-content; + + .ql-editor { + @apply p-0; + } + .ql-editor.ql-blank::before { + @apply left-0 not-italic text-gray-400; + } + .ql-container { + @apply overflow-auto font-sans text-16; + } + .ql-container.ql-snow { + @apply border-0; + } + .ql-toolbar.ql-snow { + @apply order-last rounded-full border-gray-200 text-gray-400; + } + .ql-snow .ql-stroke { + stroke: var(--gray-400); + } + .ql-snow .ql-fill, + .ql-snow .ql-stroke.ql-fill { + fill: var(--gray-400); + } + .ql-snow .ql-picker.ql-header .ql-picker-label::before, + .ql-snow .ql-picker.ql-header .ql-picker-item::before { + color: var(--gray-400); + } +} From 03b9485e6e3ed4f9dbae5453d3b6b1a8bcbd7b76 Mon Sep 17 00:00:00 2001 From: haksoo Date: Mon, 16 Dec 2024 17:08:57 +0900 Subject: [PATCH 04/13] =?UTF-8?q?=F0=9F=94=A8=20=EC=97=90=EB=94=94?= =?UTF-8?q?=ED=84=B0=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EB=A1=9C=EA=B7=B8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/test/editor.tsx | 2 +- styles/globals.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/test/editor.tsx b/pages/test/editor.tsx index f89958c..63064b8 100644 --- a/pages/test/editor.tsx +++ b/pages/test/editor.tsx @@ -9,7 +9,7 @@ export default function Editor() { const [value, setValue] = useState(''); const handleChange = (v: string) => { - console.log('value', v); + // console.log('value', v); setValue(v); }; diff --git a/styles/globals.css b/styles/globals.css index b3d8f34..647f854 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -64,7 +64,7 @@ body { @apply left-0 not-italic text-gray-400; } .ql-container { - @apply overflow-auto font-sans text-16; + @apply overflow-y-auto font-sans text-16; } .ql-container.ql-snow { @apply border-0; From 5d65eee99c086a2ca200bf4447c02c5e1b1682e5 Mon Sep 17 00:00:00 2001 From: haksoo Date: Mon, 16 Dec 2024 20:29:07 +0900 Subject: [PATCH 05/13] =?UTF-8?q?=F0=9F=90=9B=20=20jsDoc=20return=20?= =?UTF-8?q?=EB=B6=80=EB=B6=84=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/TextEditor.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/components/TextEditor.tsx b/components/TextEditor.tsx index cff4d89..d3e0be7 100644 --- a/components/TextEditor.tsx +++ b/components/TextEditor.tsx @@ -18,7 +18,6 @@ interface Props { * @param {object} props * @param {string} props.value - 초기 값 * @param {function} props.onChange - 값 변경시 콜백 함수 - * @returns {JSX.Element} */ export default function TextEditor({ value = '', onChange }: Props) { const modules = { From 274c861aa78ef6abe3672b4e57b940540f02aae8 Mon Sep 17 00:00:00 2001 From: jiminyee Date: Tue, 17 Dec 2024 13:34:23 +0900 Subject: [PATCH 06/13] =?UTF-8?q?=E2=9C=A8=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20API=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/signup.ts | 38 ++++++++++++++++++++++++++++++++++++++ pages/signup/index.tsx | 37 +++++++++++++++++++++++++++++++------ 2 files changed, 69 insertions(+), 6 deletions(-) create mode 100644 api/signup.ts diff --git a/api/signup.ts b/api/signup.ts new file mode 100644 index 0000000..8b7f4e1 --- /dev/null +++ b/api/signup.ts @@ -0,0 +1,38 @@ +interface SignupRequest { + email: string; + password: string; + name: string; +} + +interface SignupResponse { + success: boolean; + message?: string; +} + +export const getSignupData = async ( + data: SignupRequest +): Promise => { + try { + const response = await fetch( + `${process.env.NEXT_PUBLIC_API_URL}/auth/signup`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + ...data, + passwordConfirmation: data.password, // 비밀번호 확인 필드 추가 + }), + } + ); + + const result = await response.json(); + return result; + } catch (error) { + return { + success: false, + message: '서버 오류가 발생했습니다.', + }; + } +}; diff --git a/pages/signup/index.tsx b/pages/signup/index.tsx index 5c6d213..0e19e57 100644 --- a/pages/signup/index.tsx +++ b/pages/signup/index.tsx @@ -2,6 +2,7 @@ import { useState } from 'react'; import { useRouter } from 'next/router'; import InputField from '@/components/Input'; import Button from '@/components/Button'; +import { getSignupData } from 'api/signup'; const SignUp: React.FC = () => { const [email, setEmail] = useState(''); @@ -29,12 +30,36 @@ const SignUp: React.FC = () => { setName(e.target.value); }; - const handleSubmit = (e: React.FormEvent) => { + const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); - if (isSubmitting) return; // 이미 요청 중인 경우 중복 요청 방지 - alert('가입이 완료되었습니다'); + if (isSubmitting) return; + + try { + setIsSubmitting(true); + const response = await getSignupData({ + email, + password, + name, + }); + + if (response.success) { + alert('회원가입이 완료되었습니다.'); + router.push('/login'); + } else { + alert(response.message || '회원가입에 실패했습니다.'); + } + } catch (error) { + alert('회원가입 중 오류가 발생했습니다.'); + console.error('Signup error:', error); + } finally { + setIsSubmitting(false); + } }; + const isFormValid = Boolean( + email && password && passwordConfirm && name && password === passwordConfirm + ); + return (
@@ -75,10 +100,10 @@ const SignUp: React.FC = () => { /> From 48575b7402d6a80e22c1a467c4d6f9ce4507b9e6 Mon Sep 17 00:00:00 2001 From: tare <59001439+junghwa1996@users.noreply.github.com> Date: Tue, 17 Dec 2024 15:56:07 +0900 Subject: [PATCH 07/13] =?UTF-8?q?=F0=9F=93=A6=20daypicker=20=ED=8C=A8?= =?UTF-8?q?=ED=82=A4=EC=A7=80=20=EB=88=84=EB=9D=BD=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 이전 test/index 에러 수정 --- package-lock.json | 38 ++++++++++++++++++++++++++++++++++++++ package.json | 2 ++ pages/test/index.tsx | 13 ++----------- 3 files changed, 42 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index dc6b76e..54e1e40 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,9 +11,11 @@ "@next/eslint-plugin-next": "^15.1.0", "@tanstack/react-query": "^5.62.7", "axios": "^1.7.9", + "date-fns": "^4.1.0", "next": "^15.1.0", "postcss-nesting": "^13.0.1", "react": "^19.0.0", + "react-day-picker": "^9.4.4", "react-dom": "^19.0.0", "react-quill-new": "^3.3.3" }, @@ -56,6 +58,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@date-fns/tz": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@date-fns/tz/-/tz-1.2.0.tgz", + "integrity": "sha512-LBrd7MiJZ9McsOgxqWX7AaxrDjcFVjWH/tIKJd7pnR7McaslGYOP1QmmiBXdJH/H/yLCT+rcQ7FaPBUxRGUtrg==", + "license": "MIT" + }, "node_modules/@emnapi/runtime": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz", @@ -2035,6 +2043,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", @@ -5129,6 +5147,26 @@ "node": ">=0.10.0" } }, + "node_modules/react-day-picker": { + "version": "9.4.4", + "resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-9.4.4.tgz", + "integrity": "sha512-1s+jA/bFYtoxhhr8M0kkFHLiMTSII6qU8UfDFprRAUStTVHljLTjg4oarvAngPlQ1cQAC+LUb0k/qMc+jjhmxw==", + "license": "MIT", + "dependencies": { + "@date-fns/tz": "^1.2.0", + "date-fns": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/gpbl" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/react-dom": { "version": "19.0.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", diff --git a/package.json b/package.json index 89772cf..5c1ccdc 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,11 @@ "@next/eslint-plugin-next": "^15.1.0", "@tanstack/react-query": "^5.62.7", "axios": "^1.7.9", + "date-fns": "^4.1.0", "next": "^15.1.0", "postcss-nesting": "^13.0.1", "react": "^19.0.0", + "react-day-picker": "^9.4.4", "react-dom": "^19.0.0", "react-quill-new": "^3.3.3" }, diff --git a/pages/test/index.tsx b/pages/test/index.tsx index 2360eaf..3d338fc 100644 --- a/pages/test/index.tsx +++ b/pages/test/index.tsx @@ -1,3 +1,5 @@ +import { useState } from 'react'; + import Button from '@/components/Button'; import Dropdown from '@/components/Dropdown'; import LinkBar from '@/components/LinkBar'; @@ -59,12 +61,6 @@ export default function Test() { - - inputfield - - - - Button @@ -96,11 +92,6 @@ export default function Test() { LinkBar - - inputfield - - - From a6cb43277b9b69c48410f96035e4064eb877b160 Mon Sep 17 00:00:00 2001 From: jiminyee Date: Tue, 17 Dec 2024 18:20:24 +0900 Subject: [PATCH 08/13] =?UTF-8?q?=F0=9F=94=A5=20=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=EA=B0=80=EC=9E=85=20API=20=ED=8C=8C=EC=9D=BC=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/signup.ts | 38 -------------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 api/signup.ts diff --git a/api/signup.ts b/api/signup.ts deleted file mode 100644 index 8b7f4e1..0000000 --- a/api/signup.ts +++ /dev/null @@ -1,38 +0,0 @@ -interface SignupRequest { - email: string; - password: string; - name: string; -} - -interface SignupResponse { - success: boolean; - message?: string; -} - -export const getSignupData = async ( - data: SignupRequest -): Promise => { - try { - const response = await fetch( - `${process.env.NEXT_PUBLIC_API_URL}/auth/signup`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - ...data, - passwordConfirmation: data.password, // 비밀번호 확인 필드 추가 - }), - } - ); - - const result = await response.json(); - return result; - } catch (error) { - return { - success: false, - message: '서버 오류가 발생했습니다.', - }; - } -}; From 886a28d5c273d50fa7a9993ae2c1fce49ca3f797 Mon Sep 17 00:00:00 2001 From: jiminyee Date: Tue, 17 Dec 2024 22:09:20 +0900 Subject: [PATCH 09/13] =?UTF-8?q?=F0=9F=94=A7=20=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=EA=B0=80=EC=9E=85=20UI=20=EC=9E=91=EC=97=85=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C=20without=20GNB=20=EC=9D=B8=ED=92=8B=20=EC=9C=A0?= =?UTF-8?q?=ED=9A=A8=EC=84=B1=EA=B2=80=EC=82=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/Input.tsx | 15 +++++++++---- pages/signup/index.tsx | 50 +++++++++++++++++++++++++----------------- 2 files changed, 41 insertions(+), 24 deletions(-) diff --git a/components/Input.tsx b/components/Input.tsx index 6c36ca8..cd12aa2 100644 --- a/components/Input.tsx +++ b/components/Input.tsx @@ -11,6 +11,7 @@ interface InputFieldProps { label?: string; compareValue?: string; layout?: 'vertical' | 'horizontal'; + onValidation?: (isValid: boolean) => void; } function InputField({ @@ -21,6 +22,7 @@ function InputField({ label, compareValue, layout = 'vertical', + onValidation, }: InputFieldProps) { const { errorMessage, validate } = useValidation({ type, @@ -40,17 +42,20 @@ function InputField({ }; const handleBlur = () => { - if (layout === 'vertical') - // 가로모드 에러 확인 비활성화 - validate(value); + if (layout === 'vertical') { + const error = validate(value); + onValidation?.(!error && value.length > 0); + } }; const handleChange = (e: React.ChangeEvent) => { onChange(e); if (layout === 'vertical' && errorMessage) { - validate(e.target.value); + const error = validate(e.target.value); + onValidation?.(!error && e.target.value.length > 0); } }; + const getInputType = () => { if (type === 'name') { return 'text'; @@ -59,6 +64,7 @@ function InputField({ } return type; }; + //스타일에 따른 클래스 const variantClass = { containerVertical: 'mb-[24px] flex flex-col gap-[10px]', @@ -71,6 +77,7 @@ function InputField({ 'bg-gray-100 focus:border-green-200 focus:ring-1 focus:ring-green-200', errorText: 'text-12 text-red-100', }; + const labelClass = layout === 'horizontal' ? variantClass.labelHorizontal diff --git a/pages/signup/index.tsx b/pages/signup/index.tsx index 0e19e57..e966e04 100644 --- a/pages/signup/index.tsx +++ b/pages/signup/index.tsx @@ -2,14 +2,19 @@ import { useState } from 'react'; import { useRouter } from 'next/router'; import InputField from '@/components/Input'; import Button from '@/components/Button'; -import { getSignupData } from 'api/signup'; -const SignUp: React.FC = () => { +function SignUp(): React.ReactElement { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [passwordConfirm, setPasswordConfirm] = useState(''); const [isSubmitting, setIsSubmitting] = useState(false); const [name, setName] = useState(''); + const [validFields, setValidFields] = useState({ + name: false, + email: false, + password: false, + passwordConfirm: false, + }); const router = useRouter(); const handleEmailChange = (e: React.ChangeEvent) => { @@ -30,35 +35,34 @@ const SignUp: React.FC = () => { setName(e.target.value); }; + const handleValidation = (field: string, isValid: boolean) => { + setValidFields((prev) => ({ + ...prev, + [field]: isValid, + })); + }; + const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); - if (isSubmitting) return; + if (isSubmitting || !isFormValid) return; + setIsSubmitting(true); try { - setIsSubmitting(true); - const response = await getSignupData({ + console.log('Form submitted:', { email, password, + passwordConfirm, name, }); - - if (response.success) { - alert('회원가입이 완료되었습니다.'); - router.push('/login'); - } else { - alert(response.message || '회원가입에 실패했습니다.'); - } + router.push('/login'); } catch (error) { - alert('회원가입 중 오류가 발생했습니다.'); - console.error('Signup error:', error); + console.error('회원가입 중 오류가 발생했습니다:', error); } finally { setIsSubmitting(false); } }; - const isFormValid = Boolean( - email && password && passwordConfirm && name && password === passwordConfirm - ); + const isFormValid = Object.values(validFields).every(Boolean); return (
@@ -72,6 +76,7 @@ const SignUp: React.FC = () => { value={name} onChange={handleNameChange} placeholder="이름을 입력해 주세요" + onValidation={(isValid) => handleValidation('name', isValid)} /> { value={email} onChange={handleEmailChange} placeholder="이메일을 입력해 주세요" + onValidation={(isValid) => handleValidation('email', isValid)} /> { value={password} onChange={handlePasswordChange} placeholder="비밀번호를 입력해 주세요" + onValidation={(isValid) => handleValidation('password', isValid)} /> { onChange={handlePasswordConfirmChange} placeholder="비밀번호를 다시 입력해 주세요" compareValue={password} + onValidation={(isValid) => + handleValidation('passwordConfirm', isValid) + } />
); -}; +} export default SignUp; From 216a531821529264438014992b57a4468eb6c4a3 Mon Sep 17 00:00:00 2001 From: tare <59001439+junghwa1996@users.noreply.github.com> Date: Tue, 17 Dec 2024 23:42:17 +0900 Subject: [PATCH 10/13] =?UTF-8?q?=F0=9F=90=9B=20eslint=20auto=20import=20s?= =?UTF-8?q?ort?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/signup/index.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pages/signup/index.tsx b/pages/signup/index.tsx index e966e04..8f4ca56 100644 --- a/pages/signup/index.tsx +++ b/pages/signup/index.tsx @@ -1,7 +1,8 @@ -import { useState } from 'react'; import { useRouter } from 'next/router'; -import InputField from '@/components/Input'; +import { useState } from 'react'; + import Button from '@/components/Button'; +import InputField from '@/components/Input'; function SignUp(): React.ReactElement { const [email, setEmail] = useState(''); From c42b7ded0ee78e346552fd19230935284d72b8f0 Mon Sep 17 00:00:00 2001 From: jiminyee Date: Wed, 18 Dec 2024 01:18:51 +0900 Subject: [PATCH 11/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=EA=B0=80=EC=9E=85=20=ED=99=94=EB=A9=B4=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/Input.tsx | 4 +- pages/signup/index.tsx | 114 +++++++++++++++++++++-------------------- 2 files changed, 60 insertions(+), 58 deletions(-) diff --git a/components/Input.tsx b/components/Input.tsx index cd12aa2..933e858 100644 --- a/components/Input.tsx +++ b/components/Input.tsx @@ -67,8 +67,8 @@ function InputField({ //스타일에 따른 클래스 const variantClass = { - containerVertical: 'mb-[24px] flex flex-col gap-[10px]', - containerHorizontal: 'mb-[24px] w-[239px] flex items-center gap-[10px]', + containerVertical: 'flex flex-col gap-[10px]', + containerHorizontal: 'w-[239px] flex items-center gap-[10px]', labelVertical: 'text-14 text-gray-500', labelHorizontal: 'text-14 text-gray-400 w-[60px] flex-shrink-0', base: 'px-[20px] py-[10px] h-[45px] w-[400px] rounded-md text-[14px] text-gray-500 placeholder:text-14 focus:outline-none mo:w-[355px]', diff --git a/pages/signup/index.tsx b/pages/signup/index.tsx index 8f4ca56..9acf2aa 100644 --- a/pages/signup/index.tsx +++ b/pages/signup/index.tsx @@ -1,8 +1,8 @@ import { useRouter } from 'next/router'; import { useState } from 'react'; - import Button from '@/components/Button'; import InputField from '@/components/Input'; +import Link from 'next/link'; function SignUp(): React.ReactElement { const [email, setEmail] = useState(''); @@ -68,61 +68,63 @@ function SignUp(): React.ReactElement { return (
-

- 회원가입 -

- handleValidation('name', isValid)} - /> - - handleValidation('email', isValid)} - /> - - handleValidation('password', isValid)} - /> - - - handleValidation('passwordConfirm', isValid) - } - /> - -
- 이미 회원이신가요?{' '} - - 로그인하기 - +
+

+ 회원가입 +

+ handleValidation('name', isValid)} + /> + handleValidation('email', isValid)} + /> + handleValidation('password', isValid)} + /> + + handleValidation('passwordConfirm', isValid) + } + /> + +
+ 이미 회원이신가요?{' '} + + 로그인하기 + +
{' '}
From 8764c995398fc839a512e1b300a66892590cd4e5 Mon Sep 17 00:00:00 2001 From: jiminyee Date: Wed, 18 Dec 2024 01:49:52 +0900 Subject: [PATCH 12/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=EA=B0=80=EC=9E=85=20=ED=99=94=EB=A9=B4=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=EA=B0=84=EA=B2=A9=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/signup/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/signup/index.tsx b/pages/signup/index.tsx index 9acf2aa..e1e7e5e 100644 --- a/pages/signup/index.tsx +++ b/pages/signup/index.tsx @@ -112,11 +112,11 @@ function SignUp(): React.ReactElement { disabled={!isFormValid} isLoading={isSubmitting} variant="primary" - className="mt-[30px] h-[45px] w-[400px] mo:w-[355px]" + className="mt-[6px] h-[45px] w-[400px] mo:w-[355px]" > 가입하기 -
+
이미 회원이신가요?{' '} Date: Wed, 18 Dec 2024 02:28:30 +0900 Subject: [PATCH 13/13] =?UTF-8?q?=F0=9F=9A=A8=20Lint=20=EC=98=A4=EB=A5=98?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pages/signup/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pages/signup/index.tsx b/pages/signup/index.tsx index e1e7e5e..c3e4dcb 100644 --- a/pages/signup/index.tsx +++ b/pages/signup/index.tsx @@ -1,8 +1,9 @@ +import Link from 'next/link'; import { useRouter } from 'next/router'; import { useState } from 'react'; + import Button from '@/components/Button'; import InputField from '@/components/Input'; -import Link from 'next/link'; function SignUp(): React.ReactElement { const [email, setEmail] = useState('');