기존 프로젝트의 회원가입 & 로그인은 아래와 같이 global state로 유저 email, 비밀번호, 이름 등의 정보를 관리하여 한번에 signup 혹은 login API를 호출할 때, body에 json 형태로 담아 호출하는 방식으로 진행하였다.
//ridge.ts export const signupState = newRidgeState({ email: '', password: '', name: '', username: '', hiddenUsername: '', description: '', }); //signupPage.tsx const SignUpPage = () => { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [valid, setValid] = useState<boolean>(true); const [validPassword, setValidPassword] = useState<boolean>(true); ............ 생략 ............ <Button tw={{ borderRadius: 'rounded-2xl' }} variant="outlined" disabled={!email || !password || !valid || !validPassword} onClick={async () => { await signupState.set((prev) => ({ ...prev, email, password, })); > Sign up </Button> }
global state로 관리하는 방식의 경우, 각 페이지 마다 값을 저장하고 있는 state들이 많아서 코드의 유지보수 및 가시성이 떨어지는 단점이 생기는데 React-Hook-Form의 useForm을 사용하면, 이러한 문제를 해결할 수 있다.
아래와 같이 로그인에 필요한 구성 요소(email 혹은 id, password)의 데이터 타입을 interface 형태로 지정해준다.
interface FormValues { email: string; password: string; }
아래와 같이 LoginPage 안에 useForm을 선언하고 타입은 1에서 지정한 FormValues를 활용한다.
export const LoginPage = () => { const { setError, register, handleSubmit, formState: { errors }, } = useForm<FormValues>(); return( <>로그인 페이지</> ); }
2에서 했던 LoginPage에 <form></form>의 형식으로 로그인 Form을 구성한다. form 태그의 속성중 onSubmit를 사용하여 버튼 onClick 이벤트 시 onSubmit에 위치한 로그인 API를 호출하게 한다.
export const LoginPage = () => { const { setError, register, handleSubmit, formState: { errors }, } = useForm<FormValues>(); ........ 생략 ........ return ( <form className="p-4 flex flex-col space-y-4" onSubmit={handleSubmit((data) => { api .post('/users/valid-email-login', { email: data.email }) .then(() => login(data) .then(() => push('/')) .catch( (e) => e.response.status === 401 && setError('password', { message: '*비밀번호가 잘못되었습니다.', }) ) ) .catch( (e) => e.response.status === 409 && setError('email', { message: '*가입된 이메일이 아닙니다.' }) ); })} > <TextField label="이메일" type="email" placeholder="이메일을 입력해주세요." helper={errors.email?.message} {...register('email', { required: '이메일을 입력해주세요' })} /> <TextField label="비밀번호" type="password" placeholder="비밀번호를 입력해주세요." helper={errors.password?.message} {...register('password', { required: '비밀번호를 입력해주세요' })} /> <Button text="로그인" className="filled-brand-2" /> <Button to="/signup" text="회원가입" className="bg-gray-100" /> </form> ) }
* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.
useForm 사용하여 로그인 & 회원가입 구현 - 1.로그인
기존 프로젝트의 회원가입 & 로그인은 아래와 같이 global state로 유저 email, 비밀번호, 이름 등의 정보를 관리하여 한번에 signup 혹은 login API를 호출할 때, body에 json 형태로 담아 호출하는 방식으로 진행하였다.
//ridge.ts export const signupState = newRidgeState({ email: '', password: '', name: '', username: '', hiddenUsername: '', description: '', }); //signupPage.tsx const SignUpPage = () => { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [valid, setValid] = useState<boolean>(true); const [validPassword, setValidPassword] = useState<boolean>(true); ............ 생략 ............ <Button tw={{ borderRadius: 'rounded-2xl' }} variant="outlined" disabled={!email || !password || !valid || !validPassword} onClick={async () => { await signupState.set((prev) => ({ ...prev, email, password, })); > Sign up </Button> }
global state로 관리하는 방식의 경우, 각 페이지 마다 값을 저장하고 있는 state들이 많아서 코드의 유지보수 및 가시성이 떨어지는 단점이 생기는데 React-Hook-Form의 useForm을 사용하면, 이러한 문제를 해결할 수 있다.
1. FormValues의 구성 요소 타입 지정하기
아래와 같이 로그인에 필요한 구성 요소(email 혹은 id, password)의 데이터 타입을 interface 형태로 지정해준다.
interface FormValues { email: string; password: string; }
2. LoginPage안에 useForm 선언하기
아래와 같이 LoginPage 안에 useForm을 선언하고 타입은 1에서 지정한 FormValues를 활용한다.
export const LoginPage = () => { const { setError, register, handleSubmit, formState: { errors }, } = useForm<FormValues>(); return( <>로그인 페이지</> ); }
3. LoginPage에 <Form 태그> 추가하기
2에서 했던 LoginPage에 <form></form>의 형식으로 로그인 Form을 구성한다. form 태그의 속성중 onSubmit를 사용하여 버튼 onClick 이벤트 시 onSubmit에 위치한 로그인 API를 호출하게 한다.
export const LoginPage = () => { const { setError, register, handleSubmit, formState: { errors }, } = useForm<FormValues>(); ........ 생략 ........ return ( <form className="p-4 flex flex-col space-y-4" onSubmit={handleSubmit((data) => { api .post('/users/valid-email-login', { email: data.email }) .then(() => login(data) .then(() => push('/')) .catch( (e) => e.response.status === 401 && setError('password', { message: '*비밀번호가 잘못되었습니다.', }) ) ) .catch( (e) => e.response.status === 409 && setError('email', { message: '*가입된 이메일이 아닙니다.' }) ); })} > <TextField label="이메일" type="email" placeholder="이메일을 입력해주세요." helper={errors.email?.message} {...register('email', { required: '이메일을 입력해주세요' })} /> <TextField label="비밀번호" type="password" placeholder="비밀번호를 입력해주세요." helper={errors.password?.message} {...register('password', { required: '비밀번호를 입력해주세요' })} /> <Button text="로그인" className="filled-brand-2" /> <Button to="/signup" text="회원가입" className="bg-gray-100" /> </form> ) }
'개발 > ReactJS' 카테고리의 다른 글