6.6 Forms
0) Intro
지금부터는 form을 관리하는 방법에 대해 알아보려고 한다. form은 보통 언제 사용이 될까? 대부분의 웹페이지는 회원가입과 로그인을 첫 화면에 띄워주는데 여기서 사용되는 것이 form형식이다.
form에서 꼭 필요한 기능에는 ‘입력값 검사’가 있다. 우리가 전에 했던 과제들에서 입력값을 끌어오는 것이 가능했다. 하지만, 값을 끌어오고, 그 값을 검사하는 것은 조금 더 복잡한 일이었다.
우리는 form의 대표적인 기능인 입력값 검사나, form의 input을 확인하기 위해 React hook form을 사용해보려고 한다.
1) Basic Validation
라이브러리를 사용하기 전에 그 필요성을 느끼기 위해 라이브러리 없이 값을 검사하는 방법을 먼저 소개해보려고 한다.
const [error, setError]=useState("");
const onSubmit=(event:React.FormEvent<HTMLFormElement>)=>{
event.preventDefault();
{toDo.length>10? console.log(toDo):
setError("to short!");
console.log(error);
}onSubmit:form이 제출되었을 때 실행될 함수다.- 입력값인 toDo의 길이에 따라서 콘솔에 toDo를 출력하거나, 에러메세지를 출력하는 것을 볼 수 있다.
하지만, 지금은 input이 todo하나이기 때문에 useState를 하나만 생성하고, onSubmit함수도 하나만 생성하면 되는 것이지만, input이 많아지만 각각의 input에 따른 useState를 만들어줘야 하고, 각각에 onSubmit을 달아줘야 하니, 너무 많은 코드가 재사용없이 작성되고 있는 것을 알 수 있다.
정리하면
매 번 input에 value, event listener를 각각 달아줘야 한다.→ 코드의 재사용성이 떨어진다. (입력하면 값을 끌어오는 게 다인데, 동일한 부분을 계속 새로 선언해주어야 한다. )
2) useForm
지금부터는 useForm훅을 사용해보자.
(1) Import && Setting
npm install react-hook-formimport { useForm } from "react-hook-form";
function ToDoList(){
return <div>
<form>
<input placeholder="write todo"/>
<button>add</button>
</form>
</div>
}(2) register
useForm이 제공하는 register함수의 기능과, 사용 방법에 대해 알아보자.
const {register}=useForm();register는 이런식으로 받아올 수 있다. 이렇게 받아온 register함수는. watch함수와 함께 사용할 수 있다.
function ToDoList(){
const {register, watch}=useForm();
console.log(watch());
return <div>
<form >
<input {...register('todo')} placeholder="write todo"/>
<input {...register('email')} placeholder="write todo"/>
<button>add</button>
</form>
</div>
}register(’todo’): register함수는 name값과 함께 실행한다.register함수는 여러번 선언하지 않아도 된다. = 위에서 한 번만 선언해서 계속 재사용해도 상관 없다.watch: register함수가 붙어있는 모든 input(todo, email…)중 어느 하나라도 값이 바뀌면 그 값을 실시간으로 반영해서 value객체를 생성해준다.
ex)
```tsx
{
"todo": "hello",
"email": "test@example.com"
}
```개인적으로 register를 매번 생성해줄 필요가 없다는 점이 신기했다.
useState같은 경우 만들고 싶은 모든 값들에 대해서 매번 새로 만들어줘야 하는데,
useForm은 그냥 도구를 주고 이를 계속 사용할 수 있도록 해주기에 차이점이 신기하게 느껴졌던 것 같다.
무엇보다 useState는 ‘값’을 중심으로 반환값이 구성되어 있지만, useForm은 ‘함수’를 중심으로 반환값이 구성되어 있기 때문에 재사용성의 차이가 발생하고 있는 것 같다.
6.7 Form Validation
0) Intro
지금까지는 단순히 input값을 ‘관찰’만 하였다. 전에 했던 코드랑 비교를 하면, input값이 변함에 따라서 value를 업데이트 해주는 정도까지만 한 것이다.
이번 강의에서는 form을 html에 의존하지 않고, Js로 관리해야 하는 이유와, 그 차이점에 대해 우선 알아보려고 한다.
또한, 지금까지는 value를 관찰하는 데 그쳤다면, 여기서는 그 값을 검사하는 방법에 대해 알아보려고 한다.
처음부터 내맘대로 검사를 만들기 보단, 가장 대표적인 ‘길이 검사’와, ‘required검사’ (=필수 제출 여부 검사)를 진행하면 좋을 것 같다.
1) handleSubmit (from useForm)
일단 useForm 객체 안의 handleSubmit함수에 대해 알아보자. 지금까지는 ‘관찰’에 대해서만 배웠다면, 지금부터는 onSubmit를 눌렀을 때 사용할 수 있는 기능들에 대해 알아볼 것이다.
전에는 onSubmit에 그냥 함수를 할당해주었다. 그래서 submit버튼을 누르면 Js에서 그 함수를 직접 실행하도록 해주는 구조였다.
하지만, 지금은 제출버튼이 눌리면, handleSubmit이라는 함수가 실행되는 구조다.
(1) code
const { register, handleSubmit } = useForm();
const onValid = (data: any) => {
console.log(data);
};
<form onSubmit={handleSubmit(onValid)}>handleSubmit: 원래 onSubmit에 할당되어 있는 함수는, submit만 되면 ‘바로’실행이 되도록 했지만, onSubmit에 handleSubmit함수를 호출하면, ‘조건을 모두 만족했을 때’만 submit이 된다 → 그럼 조건이 만족되었는지는 어떻게 확인하냐고 물어볼 수 있는데 이건 조금만 있다가 확인해보자.onValid: handleSubmit 안에 있는 함수로 데이터를 받아서 출력하는 함수다.→ 여기서 생기는 의문은 onValid로 어떤 값이 들어오는지다.
(2) data
{
"todo": "Buy groceries",
"email": "test@example.com"
}form에 들어있는 모든 input의 정보가 담겨있다.
(3) Structure
따라서 handleSubmit이 돌아가는 구조는
- 조건을 만족하면 → handleSubmit이 실행된다
- handleSubmit이 실행될 때는 그 안에 있는 함수(=onValid)에 form input에 대한 모든 정보가 전달된다.
- 따라서 onSubmit에다가
- onValid함수에 form data를 가진 객체를 담아 실행시키고
- 조건부로 실행 시키는 기능 두 개가 추가된 것이다.
(4) sum
복습할 지점은
- onSubmit 함수에
handleSubmit을 달았을 때 어떻게 실행되는 지를 확인해야 한다. - → parameter자리에 있는 함수가 실행될 것이다. (with form data)
2) Dependency (Js, Html)
form에 required를 부여하는 방식은 두 가지가 있다
(참고로 required는 form을 ‘반드시 제출’해야 한다는 뜻)
(1) differences
<input {...register('email', {required:true})} placeholder="email"/>//depending on Js
<input {...register('email')} required placeholder="email"/>//depending on HtmlJS: register함수 안에 하나의 객체를 만들어 값을 부여한다.Html: register함수와 동등한 자격으로 부여한다.
의존성에 따라서 기능상 어떤 차이를 보이는지 정리해보자.
HTMLHtml에 의존하면, 일단 form이 required를 만족하지 않아도 일단 값이 제출이 되고 본다. 경고를 띄우긴 하지만, 그래도 제출은된다.
JSJS에 의존하면, Html과 달리, 칸이 비어있을 때 제출이 안되도록 막을 수 있다. 또한, 제출했을 때 비어있던 칸에 커서가 자동으로 올라가도록 할 수 있다.
(2) Conclusion
의존성에 따른 이런 기능상의 차이 때문에 Js에 의존하여 register함수 안에 작성하는 것이 더 유리하다.
3) Length Validation
아까 form의 값으로 길이제한을 했던 코드를 라이브러리를 사용하여 만들어보려고 한다.
(1) Previous Code
const [error, setError]=useState("");
const onSubmit=(event:React.FormEvent<HTMLFormElement>)=>{
event.preventDefault();
{toDo.length>10? console.log(toDo):
setError("to short!");
console.log(error);
}기존의 코드는
submit → value확인 → 3항 연산자로 value에 따라서 에러 or value출력
이런 과정을 밟고 있다.
(2) using register function
<input {...register('email', {required:true, minLength:10})} placeholder="email"/>동일한 코드를 구현하기 위해서는 register에 minLength만 적어주면 된다.
register의 두 번째 함수는 input value의 Validtion과 관련된 모든 값을 집어넣는 것 같다. (required도 사실은 조건이자 검사 요소이니..)
그래서 handleSubmit을 실행시키면, register 두 번째 parameter에 있는 조건들을 하나씩 확인해서 제출을 할지 말지 결정하는 것 같다.
암튼 register함수 안에 minLength라는 값이 이미 저장되어 있으니. minLength:10을 적어주면 자동으로 HandleSubmit이 검사를 해줄 것이다.
(3) Compare&&Merit
just React- → submit →value가져오기 → value검사 → 에러메세지
useFormuseForm이 편리한 이유는 handleSubmit와 register를 이용하여 값을 얻고, 검사하는 기능을 자동화 시켰기 때문에 값을 검사하기 위해서는 그냥 validation 을 이용해주면 된다는 이점이 있다.- (= 검사하고 싶은 input에 Validation을 입력하면 된다)
- (전체적인 구조) : onSubmit안의 handlSubmit함수가 값을 검사 → handle Submit은 각 input의 value를 가지고 와서 register의 두 번째에 있는 조건들로 검사를 해봄
4) Checking Errors
const {register, handleSubmit, formState}=useForm();
console.log(formState.errors);(1) Error Object
useForm에서 formState를 가지고 오면, 에러를 확인할 수 있다.

- 프로퍼티 이름 (=email) : register에 부여한
name type: 만족하지 못한 validation의 이름을 적어준다.- 지금 발생한 에러는, 이메일에 required를 부여해놓고, 값을 입력하지 않아 발생한 것이다.

지금 에러는 password에 minLength를 부여해놓고, 지키지 않아서 발생한 것이다.
에러의 객체는
- register에 부여한 name를 변수명으로 프로퍼티로 갖는다
type: 옆에는, 에러의 종류(=register 두 번째 parameter에 부여했던 제목)이 뜬다.
(2) Error Messages
단순히 에러의 유형만 뜨도록 하는 것이 아니라 에러 메세지도 설정하고 싶다.
이렇게 하기 위해서는 required값과, minLength값에 단순히 숫자와 Bool을 입력하는 것이 아니라, 다른 값을 입력해야 한다.
{
required:"email is required",
minLength:{
value:10,
message:"email must be longer than 10"
}required: 값을 따로 안적었던 키워드는 바로 에러 메세지를 적어준다.minLength: 값이 존재했던 minLength는 값을 객체로 묶어 message 필드와 함께 보낸다.
(3) 2nd paramter of register
이렇게 보고 나니, register 두 번째 요소에 전달해야 할 값들의 특징에 대해 조금 정리가 되고 있다.
field: validation들을 이름으로 가진 객체를 보낼 것이다valuevalue: minLength같이 값이 필요한 경우 부여한다.message: 에러메세지를 적어서 보내준다.
결국 ‘비교 기준’과 이 기준을 만족하지 못했을 때의 에러메세지까지 모두 담고 있는 객체인 것이다.
6.8 Form Errors
1) Including Validation
우리는 입력값에 특정 문자가 포함되어 있거나, 포함되지 않도록 해야 하는 경우들이 있다.
- 예를 들어, naver메일로만 가입이 가능하다면, @naver.com이 반드시 포함되어 있어야 한다.
- 일반적인 비밀번호는 숫자와, 특수문자, 영어 대문자 모두가 하나 이상씩 포함되어 있어야 한다.
- 아이디나 닉네임을 생성할 때는 특정 문자열을 포함하지 말아야 한다.
(1) Regular Expressions (정규식)
RegExp는 문자열이 어떻게 생겨야 하는 지를 설명해주는 것이다.
정규식을 pattern의 값에 부여해주면, 이 또한 Validation으로 이용할 수 있다.
(2) Site

옆에 보면 이런 식으로 무언가를 포함할 것, 무언가가 있는 지 검사할 것 등 다양한 예시가 나와있다.
ex) naver메일인지 검사하는 방법
/^[A-Za-z0-9._%+-]+@naver.com$/→ 아무거나 앞에 오고, 뒤에 @naver.com이 반드시 와야 한다.
(3) Implementing
{
required:"email is required",
minLength:{
value:10,
message:"email must be longer than 10"
},
pattern :{
value:/^[A-Za-z0-9._%+-]+@naver.com$/,
message:`mail has to be naver mail`
}
}이렇게 생성한 정규식은 pattern의 값에 부여할 수 있다. ( 전에 했던 것과 마찬가지로 에러메세지랑 같이 보냈다)
const {
register,
handleSubmit,
formState: { errors },
} = useForm<IForm>({
defaultValues: {
email: "@naver.com",
},
});formState:{errors}: 내가 아까 formState를 그냥 가져와서 formState.errors로 사용했는데, 애초에 formState에서 error만 빼오고 있다는 뜻이다.useForm- send
objectas parameter defaultValues: { email…} : email이라는 input에 기본값을 설정할 것임.
- send
<IForm>: interface로 Form에서 받을 값을 종류를 정리해준다.
(4) Sum
결국 등호를 기준으로 왼쪽은, 내가 useForm 훅에서 꺼내온 함수들과 상태 값들
오른쪽은 useForm 훅을 실행해서 form을 생성하는 부분과 관련되어 있다.
2) displaying Error message
현재 formState의 error를 error변수로 받았기 때문에 바로 사용이 가능하다.
<span>{error?.email?.message}</span>error?: 에러가 있는가?email?: 그렇다면, 이메일에서 에러가 났는가?message: 그렇다면 메세지는 무조건 있을 것이다. 메세지 출력
6.9 Custom Validation
이 장에서는 내맘대로 Validation을 생성하는 방법에 대해 알아보려고 한다.
1) requirments
우리가 validation을 설정할 땐 뭘 고려해야 할까?
howto validate?type: formState.errors의 제목으로 사용되는 type을 설정해야 한다.errormessage
2) method
이제까지는 form이 submit되는 순간 값들을 검사했다. 하지만 지금부터는 사용자가 입력할 때 마다 검사를 실행해보고 싶다.
onValid가 유효성을 검사하는 함수이기 때문에 이를 이용해서 에러를 만들어보자.
난 지금 password input과, password confirmation input의 값이 불일치 할 때 에러메세지를 띄워보려고 한다.
const {
register,
handleSubmit,
formState: { errors },
setError
} = useForm<IForm>({
defaultValues: {
email: "@naver.com",
},
});
const onValid=(data:IForm)=>{
if(data.passWord!==data.passWordConfirm)
setError("passWord",{
type:"confirmation Error",
message:"Password Cofirmation Error"},
{shouldFocus:true});
}useForm에서 setError변수를 끌고 온다.
setError:if(data.passWord!==data.passWordConfirm): how to validate에 해당하는 부분. 조건문의 조건에 기준을 부여한다.
또한, data에는 input의 이름이 들어간 객체가 있고, 각 필드의 값은 input value랑 동일하니까 data.passWord와 같이 input값을 끌고올 수 있다.
- `setError` : 지금부터 명시해야 하는 부분은 type, name, message이다.
- type : "confirmation Error"
- message : error message
3) Comparison
(1) Standard Error
minLength:{
value:10,
message:"email must be longer than 10"
}type이 name과 동일한 이름으로 자동설정 된다.
(2) Custom Error
setError("passWord",{
type:"confirmation Error",
message:"Password Cofirmation Error"},
{shouldFocus:true});passWord: nametype: error typemessage: error message
4) Excluding specific words
아까는 정규식과 pattern을 활용하여 input값을 검사했는데, 지금부터는 validate를 활용하여 특정 단어를 포함하지 못하게 해보려고 한다.
<input {...register('passWord', {
required:true,
validate :(value)=>true
})} placeholder="passWord"/>register의 두 번째 요소로 validator를 보내려고 하는데(지금 true써있는 자리에), validator에는 bool이나 문자열을 반환하는 함수가 들어가야 한다.
validate:(value)=> value.includes('pongdang')?"no pongdang allowed":truevalue: value of the inputarr.includes(word): 문자열이 word를 포함하고 있는지 확인하는 함수 → 포함하고 있다면 true, 아니면 false를 반환한다.- 퐁당이 포함되어 있다면 → 문자열 반환 = error message로 자동 인식
- 퐁당이 안포함되어 있다면 → Bool반환 = True값은 validation을 통과했다는 뜻인가봄
5) Sum
2, 4번의 custom validation의 성격이 조금 다르다.
2번의 validation은 사용자의 실시간 입력을 감지하기 위해 onValid값에서 코드를 작성하였다.
4번은, handleSubmit가 제출했을 때 잡아줄 것을 기대하며 register에 두 번째 요소에 할당을 하였다.
둘이 만들고 싶어하는 에러의 차이에 따라서, 사용하고자 하는 기능들(handleSubmit을 이용할 것인가, 유효성검사 함수를 이용할 것인가…) 다른 위치에서 코드를 작성하고 있다. 이 차이를 조금 더 고민하며 머릿속에 정리하면 좋을 것 같다.
'프로그래밍 > React' 카테고리의 다른 글
| # React-Js Master Class #7-1 Trello Clone (0) | 2025.03.06 |
|---|---|
| # React-Js Master Class #6-3 State Management (0) | 2025.02.28 |
| # React-Js Master Class #6-1 State Management (0) | 2025.02.22 |
| # React-Js Master Class #5-3 Crypto Tracker (0) | 2025.02.17 |
| # React-Js Master Class #5-2 Crypto Tracker (0) | 2025.02.11 |