-
Notifications
You must be signed in to change notification settings - Fork 2
✨ Feat: 회원 가입 폼 구현 #57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
4d8dac4
9bfd136
05514f7
1da4aac
e930b95
e2dc7e6
b2b1923
11cbdbf
77f8b33
246b6fa
d560b41
44db71f
f9c4ee6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| 'use client' | ||
|
|
||
| import SignupForm from '@/app/features/auth/components/SignupForm' | ||
| export default function Signup() { | ||
| return ( | ||
| <> | ||
| <SignupForm /> | ||
| </> | ||
| ) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| 'use client' | ||
|
|
||
| import Input from '@components/Input' | ||
| import { cn } from '@lib/cn' | ||
| import { useState } from 'react' | ||
| import { useForm } from 'react-hook-form' | ||
|
|
||
| import { useConfirmPasswordValidation } from '../hooks/useConfirmPasswordValidation' | ||
| import { useSignupSubmit } from '../hooks/useSignupSubmit' | ||
| import { signupValidation } from '../schemas/signupValidation' | ||
| import { SignupFormData } from '../types/auth.type' | ||
|
|
||
| export default function SignupForm() { | ||
| const { | ||
| register, | ||
| handleSubmit, | ||
| trigger, | ||
| getValues, | ||
| formState: { errors, isSubmitting, isValid }, | ||
| } = useForm<SignupFormData>({ | ||
| mode: 'onChange', | ||
| defaultValues: { | ||
| email: '', | ||
| nickname: '', | ||
| password: '', | ||
| confirmPassword: '', | ||
| }, | ||
| }) | ||
|
|
||
| const [isChecked, setIsChecked] = useState(false) | ||
| const { submit } = useSignupSubmit() | ||
| const validation = useConfirmPasswordValidation(getValues) | ||
|
|
||
| function handleAgree() { | ||
| setIsChecked((prev) => !prev) | ||
| } | ||
|
|
||
| return ( | ||
| <form className="flex flex-col gap-16" onSubmit={handleSubmit(submit)}> | ||
| <Input | ||
| labelName="이메일" | ||
| type="email" | ||
| placeholder="이메일을 입력해 주세요" | ||
| autoComplete="email" | ||
| {...register('email', signupValidation.email)} | ||
| hasError={!!errors.email} | ||
| errorMessage={errors.email?.message} | ||
| /> | ||
| <Input | ||
| labelName="닉네임" | ||
| type="text" | ||
| placeholder="닉네임을 입력해 주세요" | ||
| autoComplete="off" | ||
| {...register('nickname', signupValidation.nickname)} | ||
| hasError={!!errors.nickname} | ||
| errorMessage={errors.nickname?.message} | ||
| /> | ||
| <Input | ||
| labelName="비밀번호" | ||
| type="password" | ||
| placeholder="8자 이상 입력해 주세요" | ||
| autoComplete="new-password" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이렇게 적어주면 어떻게 자동완성이 이루어지는건가용?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아니요 그 반대 입니다!
이 속성을 명시 하지 않았을 경우 브라우저에서는 과거에 입력한 데이터를 기준으로 자동 완성을 띄우게 됩니다. 이러면 UX 측면으로도 혼란을 야기할 수 있기 때문에 해당 속성을 사용하게 되었습니다. |
||
| {...register('password', { | ||
| ...signupValidation.password, | ||
| onChange: () => trigger('confirmPassword'), | ||
| })} | ||
| hasError={!!errors.password} | ||
| errorMessage={errors.password?.message} | ||
| /> | ||
| <Input | ||
| labelName="비밀번호 확인" | ||
| type="password" | ||
| placeholder="비밀번호를 한번 더 입력해주세요" | ||
| autoComplete="new-password" | ||
| {...register('confirmPassword', validation)} | ||
| hasError={!!errors.confirmPassword} | ||
| errorMessage={errors.confirmPassword?.message} | ||
| /> | ||
Insung-Jo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| <div className="flex items-center gap-8"> | ||
| <input | ||
| type="checkbox" | ||
| id="terms" | ||
| className="size-16 rounded-4" | ||
| onChange={handleAgree} | ||
| checked={isChecked} | ||
| /> | ||
| <label className="text-base" htmlFor="terms"> | ||
| 이용약관에 동의합니다. | ||
| </label> | ||
| </div> | ||
| <button | ||
| type="submit" | ||
| className={cn( | ||
| 'mt-8 h-50 w-full rounded-8 text-lg font-medium text-white', | ||
| isValid && isChecked && !isSubmitting | ||
| ? 'BG-blue' | ||
| : 'BG-blue-disabled', | ||
| )} | ||
| disabled={isSubmitting || !isValid || !isChecked} | ||
| > | ||
| 회원가입 | ||
| </button> | ||
| </form> | ||
| ) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import { SignupFormData } from '../types/auth.type' | ||
|
|
||
| export function useConfirmPasswordValidation(getValues: () => SignupFormData) { | ||
| return { | ||
| required: '비밀번호를 한번 더 입력해 주세요.', | ||
| validate: (value: string) => { | ||
| const password = getValues().password | ||
| return value === password || '비밀번호가 일치하지 않습니다' | ||
| }, | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| import axios from 'axios' | ||
| import { useRouter } from 'next/navigation' | ||
| import { toast } from 'sonner' | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 토스트 사용해주셨군용 |
||
|
|
||
| import { SignupRequest } from '../types/auth.type' | ||
| import { useAuth } from './useAuth' | ||
|
|
||
| export function useSignupSubmit() { | ||
| const { signup } = useAuth() | ||
| const router = useRouter() | ||
|
|
||
| async function submit(data: SignupRequest) { | ||
| try { | ||
| await signup(data) | ||
| toast.success('가입이 완료되었습니다!') | ||
| router.push('/login') | ||
| } catch (e: unknown) { | ||
Insung-Jo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if (axios.isAxiosError(e)) { | ||
| const message = e.response?.data?.message | ||
| toast.error(message ?? '회원가입에 실패하였습니다.') | ||
| } else { | ||
| toast.error('알 수 없는 에러 발생') | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return { submit } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| export const signupValidation = { | ||
| email: { | ||
| required: '이메일을 입력해 주세요.', | ||
| pattern: { | ||
| value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, | ||
| message: '유효한 이메일 주소를 입력해주세요', | ||
| }, | ||
| }, | ||
| password: { | ||
| required: '비밀번호를 입력해 주세요.', | ||
| minLength: { | ||
| value: 8, | ||
| message: '비밀번호는 최소 8자 이상이어야 합니다.', | ||
| }, | ||
| }, | ||
| nickname: { | ||
| required: '닉네임을 입력해 주세요.', | ||
| pattern: { | ||
| value: /^[a-zA-Z가-힣]{1,10}$/, | ||
| message: '한글 또는 영어만 입력할 수 있으며, 최대 10자까지 가능합니다.', | ||
| }, | ||
| }, | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기는 어떤 내용을 포함하고 있는걸까용?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
정확히 어느 부분을 말씀하신 건지는 파악할 수 없으나
만약
...register를 말씀하신 거라면 기본적으로register(name, options)를 호출해 반환된 값을props로 전달하는 형태입니다.이때
register의 반환 값은{ ref, name, onChange, onBlur }형태의 객체이며options에는 다양한 유효성 검증과 관련된 옵션이 있습니다!만약 전체 옵션이 궁금하시다면 공식 문서를 참고하시면 됩니다!