간단한 ToDo리스트를 만들어 볼 것이다.
ToDo form 생성 : React Hook Form 라이브러리 사용
form을 관리하고 유효성 검사를 하는데 성능이 뛰어나기 때문에 많이 사용되는 라이브러리이다.
리액트만 사용하는 것보다 훨씬 편하다.
우선 react hook form을 설치한다.
npm i react-hook-form
const { register, watch, handleSubmit, formState } = useForm<IForm>({defaultValues:{email:"@naver.com"}});
이 훅은
register
watch
handleSubmit
formState
setError
setValue
등 다양한 함수를 제공한다.
register함수
console.log(register("toDo"))

register 함수는 onBlur, onChange 이벤트 핸들러를 갖고 있다.
input태그에 해당 값을 연산자를 사용하여 간단히 넣어주면 쉽게 form 형식을 만들 수 있다.
<input {...register("toDo")} placeholder="Write a to do" />
watch 함수
console.log(watch());

따로 state를 만들 필요가 없다.
handleSubmit 함수
폼의 유효성을 검증하고 제출한다.
이 함수는 데이터가 유효할 때 호출되는 함수와 데이터가 유효하지 않을 때 호출되는 함수를 인자로 받는다.
<form onSubmit={handleSubmit(onValid,noValid)/>
유효성을 검증하고 싶을 때는 조건설정을 HTML태그 내에서 할 수도 있고 js에서 할 수도 있다.
js로 할 경우 객체를 사용하여 메세지를 보낼 수 있는데 이 메세지는 에러메세지로 확인을 할 수 있다.
조건 설정 값은 정규식으로 표현할 수도 있다.
//HTML으로
<input {...register("email")} required placeholder="Email" />
//JS으로
<input {...register("email", { required: true })} placeholder="Email" />
<input {...register("password1", {
required: "Password is required",
minLength: { value: 5, message: "Your password is too short." },
})}
/>
<input
{...register("email", {
pattern: {
value: /^[A-Za-z0-9._%+-]+@naver.com/g,
message: "네이버 이메일만 가능합니다.",
},
})}
/>
규칙을 직접 커스텀하여 유효성 검증을 실행할 수도 있다.
서버에 아이디 중복을 확인하거나 할 때 사용한다.
<input
{...register("firstName", {
required: "write here",
validate: (value) =>
!value.includes("김") || "김은 사용할 수 없습니다.",
})}
placeholder="First Name"
/>
formState 함수
formState를 통해서 에러 객체를 얻을 수 있다.
이 객체에서는 어떤 에러가 어디서 났는 지 알 수 있다.
console.log(formState.errors);

사용자 화면에 에러 보여주기 위해 간단히 표현할 수 있다.
//const {formState:{errors}} = useForm();
<span>{errors?.password?.message}</span>
setError( ) : 에러발생
form 작성 시 비밀번호 재확인 등 실시간으로 에러를 발생해야 할 경우가 있다.
제출 시 데이터가 유효할 때 호출되는 함수에 조건을 설정하여 에러를 발생시킬 수 있다.
데이터 항목의 이름과 에러 메세지를 인자로 보낸다.
shouldFocus 옵션은 에러가 발생하고 form에서 특정 input태그에 강제로 focus 시키게 하는 것이다.
const onValid = (data: any) => {
if (data.password !== data.password1) {
setError("password1",
{ message: "비밀번호가 일치하지 않습니다." },
{ shouldFocus: true },
);
}
};
추가적인 에러가 필요하다면 항목의 이름을 새로 지어주고 인터페이스에 데이터타입을 지정한 후 사용할 수 있다.
setValue( ): 데이터 항목 값 업데이트
submit 후 값을 리셋시키고 싶을 때 이 함수를 사용할 수 있다.
setValue("email", "");
ToDo 추가하기
인터페이스를 만든다.
interface IToDo {
text: string;
id: number;
category: "TO_DO" | "DOING" | "DONE";
}
atom을 만들고, atom의 데이터타입이 해당 인터페이스의 배열임을 알려준다.
const toDoState = atom<IToDo[]>({
key: "toDo",
default: [],
});
recoil로 atom값을 받아오고 submit할 때마다 toDo가 추가된 새로운 atom배열을 set한다.
const [toDos, setToDos] = useRecoilState(toDoState);
setToDos((oldToDos) => [
{ text: data.toDo, id: Date.now(), category: "TO_DO" }, ...oldToDos,
]);
map함수를 이용하여 toDos를 화면에 렌더링 시킨다.

리팩토링은 간간히 필쑤..
조각조각 나눠보자..
카테고리 변경
위의 이미지에서 각 todo의 카테고리를 변경할 수 있게 해보자.
버튼을 경우에 따라 다르게 보이도록 한다.
(?) 이게 어떻게 성립하는 것인가,,?
{category !== "DOING" && <button>Doing</button>}
{category !== "TO_DO" && <button>To Do</button>}
{category !== "DONE" && <button>Done</button>}
각 버튼에 name 속성을 넣고 동일하게 클릭 이벤트 리스너를 설정한다.
{category !== "DOING" && <button name="DOING" onClick={onClick}>Doing</button>}
클릭 이벤트 핸들러를 만든다.
각 버튼의 name을 받아올 수 있다.
const onClick = (event: React.MouseEvent<HTMLButtonElement>) => {
const { name } = event.currentTarget;
};
(?) () => () 과 그냥 함수를 호출하는 것의 차이..?
이제 state를 mutate 시켜주기 위해서 클릭 이벤트 핸들러 내에 setToDos( )를 호출한다.
우선 카테고리 수정을 원하는 todo의 인덱스를 찾는다.
카테고리 수정이 완료된 새로운 배열을 리턴한다.
setToDos((oldToDos) => {
const targetIdx = oldToDos.findIndex((toDo) => toDo.id == id);
const newToDo = { text, id, category: name as IToDo["category"] };
return [
...oldToDos.slice(0, targetIdx),
newToDo,
...oldToDos.slice(targetIdx + 1),
];
});
카테고리별로 state 분류 : Recoil의 Selector 사용

Selector은 atom의 output을 변형하는 도구이다.
atom은 단순 배열일 뿐이고 selector을 사용해야지만 동일한 atom 내에서 state를 원하는대로 변형할 수 있다.
selector을 만들어보자.
selector의 get 속성은 변형한 값을 반환할 수 있게 해준.
get 속성이 매개변수로 받는 옵션 중 get 함수는 atom을 selector내부로 불러올 수 있게 한다.
export const toDoSelector = selector({
key: "toDoSelector",
get: ({ get }) => {
const toDos = get(toDoState);
return [
toDos.filter((toDo) => toDo.category === "TO_DO"),
toDos.filter((toDo) => toDo.category === "DOING"),
toDos.filter((toDo) => toDo.category === "DONE"),
];
},
});
state 값을 받아오는 것처럼 selector도 useRecoilValue()를 통해서 호출할 수 있다.
이제 각 배열을 받아 원하는 대로 사용할 수 있다.
const [toDo, doing, done] = useRecoilValue(toDoSelector);
카테고리 선택해서 보기
한번에 모든 카테고리를 렌더링 하는 것보단 한번에 하나의 카테고리만 렌더링 할 수 있게 해보자
사용자가 현재 선택한 카테고리 state를 만들어서 원하는 카테고리만 선택할 수 있도록 한다.
카테고리 state를 만든다.
export const categoryState = atom({
key: "category",
default: "TO_DO",
});
HTML에 select태그를 추가하고 카테고리를 옵션으로 넣어준다.
클릭하자마자 이벤트를 감지하기 위해서 onInput 리스너를 사용한다.
<select value={category} onInput={onInput}>
<option value="TO_DO">To Do</option>
<option value="DOING">Doing</option>
<option value="DONE">Done</option>
</select>
onInput 이벤트 핸들러를 생성한다.
이때 category state를 설정하는 함수로 변경값을 처리한다.
const onInput = (event: React.FormEvent<HTMLSelectElement>) => {
setCategory(event.currentTarget.value as any);
};
이제 변하는 카테고리에 따라 해당 카테고리에 todo 생성을 할 수 있도록 해준다.
현재 category state를 받아와서 toDo state 값을 변경하는 setToDo 함수에 값을 삽입하면 된다.
const category = useRecoilValue(categoryState);
setToDos((oldToDos) => [
{ text: toDo, id: Date.now(), category: category },
...oldToDos,
]);
상수값 관리 : Enum(열거타입) 사용
enum은 TS에서 제공하는 자료형이다. 객체를 사용하는 것과 비슷하다.
카테고리 이름과 같은 상수를 관리하기 위해서 enum 자료형을 사용한다.
숫자형으로 값을 저장하기 때문에 문자형으로 저장하고 싶다면 따로 처리를 해주어야 한다.
export enum Categories {
"TO_DO" = "TO_DO",
"DOING" = "DOING",
"DONE" = "DONE",
}
+) Selector의 Set 속성
set속성은 atom의 직접적인 수정이 가능하다 .
첫번째 인자는 옵션 객체이고 두번째 인자는 새로운 입력 값이다.
export const hourSelector = selector<number>({
key: "hours",
get: ({ get }) => {
const minutes = get(minuteState);
return minutes / 60;
},
set: ({ set }, newValue) => {
const minutes = Number(newValue) * 60;
set(minuteState, minutes);
},
});
set을 사용해보자.
useRecoilState 함수로 불러올 때 첫번째 값에는 get의 리턴 값이 담기고 두번째 값에는 set을 부르는 함수가 담긴다.
const [hours, setHours] = useRecoilState(hourSelector);
input 태그의 onChange 이벤트 리스너를 설정한다.
const onHoursChange = (event:React.FormEvent<HTMLInputElement>) => {
setHours(+event.currentTarget.value);
}
// +String -> Number
'Tech > TypeScript' 카테고리의 다른 글
[Typescript / TS] Frame motion을 사용한 애니메이션 (0) | 2023.01.29 |
---|---|
[Typescript / TS] ToDo 칸반보드 만들기 (0) | 2023.01.23 |
[Typescript / TS] Recoil을 사용한 state 관리 (0) | 2023.01.16 |
[Typescript / TS] 코인트래킹 어플 만들기 (0) | 2023.01.07 |
[Typescript / TS] 타입스크립트 기초 (0) | 2023.01.02 |
간단한 ToDo리스트를 만들어 볼 것이다.
ToDo form 생성 : React Hook Form 라이브러리 사용
form을 관리하고 유효성 검사를 하는데 성능이 뛰어나기 때문에 많이 사용되는 라이브러리이다.
리액트만 사용하는 것보다 훨씬 편하다.
우선 react hook form을 설치한다.
npm i react-hook-form
const { register, watch, handleSubmit, formState } = useForm<IForm>({defaultValues:{email:"@naver.com"}});
이 훅은
register
watch
handleSubmit
formState
setError
setValue
등 다양한 함수를 제공한다.
register함수
console.log(register("toDo"))

register 함수는 onBlur, onChange 이벤트 핸들러를 갖고 있다.
input태그에 해당 값을 연산자를 사용하여 간단히 넣어주면 쉽게 form 형식을 만들 수 있다.
<input {...register("toDo")} placeholder="Write a to do" />
watch 함수
console.log(watch());

따로 state를 만들 필요가 없다.
handleSubmit 함수
폼의 유효성을 검증하고 제출한다.
이 함수는 데이터가 유효할 때 호출되는 함수와 데이터가 유효하지 않을 때 호출되는 함수를 인자로 받는다.
<form onSubmit={handleSubmit(onValid,noValid)/>
유효성을 검증하고 싶을 때는 조건설정을 HTML태그 내에서 할 수도 있고 js에서 할 수도 있다.
js로 할 경우 객체를 사용하여 메세지를 보낼 수 있는데 이 메세지는 에러메세지로 확인을 할 수 있다.
조건 설정 값은 정규식으로 표현할 수도 있다.
//HTML으로
<input {...register("email")} required placeholder="Email" />
//JS으로
<input {...register("email", { required: true })} placeholder="Email" />
<input {...register("password1", {
required: "Password is required",
minLength: { value: 5, message: "Your password is too short." },
})}
/>
<input
{...register("email", {
pattern: {
value: /^[A-Za-z0-9._%+-]+@naver.com/g,
message: "네이버 이메일만 가능합니다.",
},
})}
/>
규칙을 직접 커스텀하여 유효성 검증을 실행할 수도 있다.
서버에 아이디 중복을 확인하거나 할 때 사용한다.
<input
{...register("firstName", {
required: "write here",
validate: (value) =>
!value.includes("김") || "김은 사용할 수 없습니다.",
})}
placeholder="First Name"
/>
formState 함수
formState를 통해서 에러 객체를 얻을 수 있다.
이 객체에서는 어떤 에러가 어디서 났는 지 알 수 있다.
console.log(formState.errors);

사용자 화면에 에러 보여주기 위해 간단히 표현할 수 있다.
//const {formState:{errors}} = useForm();
<span>{errors?.password?.message}</span>
setError( ) : 에러발생
form 작성 시 비밀번호 재확인 등 실시간으로 에러를 발생해야 할 경우가 있다.
제출 시 데이터가 유효할 때 호출되는 함수에 조건을 설정하여 에러를 발생시킬 수 있다.
데이터 항목의 이름과 에러 메세지를 인자로 보낸다.
shouldFocus 옵션은 에러가 발생하고 form에서 특정 input태그에 강제로 focus 시키게 하는 것이다.
const onValid = (data: any) => {
if (data.password !== data.password1) {
setError("password1",
{ message: "비밀번호가 일치하지 않습니다." },
{ shouldFocus: true },
);
}
};
추가적인 에러가 필요하다면 항목의 이름을 새로 지어주고 인터페이스에 데이터타입을 지정한 후 사용할 수 있다.
setValue( ): 데이터 항목 값 업데이트
submit 후 값을 리셋시키고 싶을 때 이 함수를 사용할 수 있다.
setValue("email", "");
ToDo 추가하기
인터페이스를 만든다.
interface IToDo {
text: string;
id: number;
category: "TO_DO" | "DOING" | "DONE";
}
atom을 만들고, atom의 데이터타입이 해당 인터페이스의 배열임을 알려준다.
const toDoState = atom<IToDo[]>({
key: "toDo",
default: [],
});
recoil로 atom값을 받아오고 submit할 때마다 toDo가 추가된 새로운 atom배열을 set한다.
const [toDos, setToDos] = useRecoilState(toDoState);
setToDos((oldToDos) => [
{ text: data.toDo, id: Date.now(), category: "TO_DO" }, ...oldToDos,
]);
map함수를 이용하여 toDos를 화면에 렌더링 시킨다.

리팩토링은 간간히 필쑤..
조각조각 나눠보자..
카테고리 변경
위의 이미지에서 각 todo의 카테고리를 변경할 수 있게 해보자.
버튼을 경우에 따라 다르게 보이도록 한다.
(?) 이게 어떻게 성립하는 것인가,,?
{category !== "DOING" && <button>Doing</button>}
{category !== "TO_DO" && <button>To Do</button>}
{category !== "DONE" && <button>Done</button>}
각 버튼에 name 속성을 넣고 동일하게 클릭 이벤트 리스너를 설정한다.
{category !== "DOING" && <button name="DOING" onClick={onClick}>Doing</button>}
클릭 이벤트 핸들러를 만든다.
각 버튼의 name을 받아올 수 있다.
const onClick = (event: React.MouseEvent<HTMLButtonElement>) => {
const { name } = event.currentTarget;
};
(?) () => () 과 그냥 함수를 호출하는 것의 차이..?
이제 state를 mutate 시켜주기 위해서 클릭 이벤트 핸들러 내에 setToDos( )를 호출한다.
우선 카테고리 수정을 원하는 todo의 인덱스를 찾는다.
카테고리 수정이 완료된 새로운 배열을 리턴한다.
setToDos((oldToDos) => {
const targetIdx = oldToDos.findIndex((toDo) => toDo.id == id);
const newToDo = { text, id, category: name as IToDo["category"] };
return [
...oldToDos.slice(0, targetIdx),
newToDo,
...oldToDos.slice(targetIdx + 1),
];
});
카테고리별로 state 분류 : Recoil의 Selector 사용

Selector은 atom의 output을 변형하는 도구이다.
atom은 단순 배열일 뿐이고 selector을 사용해야지만 동일한 atom 내에서 state를 원하는대로 변형할 수 있다.
selector을 만들어보자.
selector의 get 속성은 변형한 값을 반환할 수 있게 해준.
get 속성이 매개변수로 받는 옵션 중 get 함수는 atom을 selector내부로 불러올 수 있게 한다.
export const toDoSelector = selector({
key: "toDoSelector",
get: ({ get }) => {
const toDos = get(toDoState);
return [
toDos.filter((toDo) => toDo.category === "TO_DO"),
toDos.filter((toDo) => toDo.category === "DOING"),
toDos.filter((toDo) => toDo.category === "DONE"),
];
},
});
state 값을 받아오는 것처럼 selector도 useRecoilValue()를 통해서 호출할 수 있다.
이제 각 배열을 받아 원하는 대로 사용할 수 있다.
const [toDo, doing, done] = useRecoilValue(toDoSelector);
카테고리 선택해서 보기
한번에 모든 카테고리를 렌더링 하는 것보단 한번에 하나의 카테고리만 렌더링 할 수 있게 해보자
사용자가 현재 선택한 카테고리 state를 만들어서 원하는 카테고리만 선택할 수 있도록 한다.
카테고리 state를 만든다.
export const categoryState = atom({
key: "category",
default: "TO_DO",
});
HTML에 select태그를 추가하고 카테고리를 옵션으로 넣어준다.
클릭하자마자 이벤트를 감지하기 위해서 onInput 리스너를 사용한다.
<select value={category} onInput={onInput}>
<option value="TO_DO">To Do</option>
<option value="DOING">Doing</option>
<option value="DONE">Done</option>
</select>
onInput 이벤트 핸들러를 생성한다.
이때 category state를 설정하는 함수로 변경값을 처리한다.
const onInput = (event: React.FormEvent<HTMLSelectElement>) => {
setCategory(event.currentTarget.value as any);
};
이제 변하는 카테고리에 따라 해당 카테고리에 todo 생성을 할 수 있도록 해준다.
현재 category state를 받아와서 toDo state 값을 변경하는 setToDo 함수에 값을 삽입하면 된다.
const category = useRecoilValue(categoryState);
setToDos((oldToDos) => [
{ text: toDo, id: Date.now(), category: category },
...oldToDos,
]);
상수값 관리 : Enum(열거타입) 사용
enum은 TS에서 제공하는 자료형이다. 객체를 사용하는 것과 비슷하다.
카테고리 이름과 같은 상수를 관리하기 위해서 enum 자료형을 사용한다.
숫자형으로 값을 저장하기 때문에 문자형으로 저장하고 싶다면 따로 처리를 해주어야 한다.
export enum Categories {
"TO_DO" = "TO_DO",
"DOING" = "DOING",
"DONE" = "DONE",
}
+) Selector의 Set 속성
set속성은 atom의 직접적인 수정이 가능하다 .
첫번째 인자는 옵션 객체이고 두번째 인자는 새로운 입력 값이다.
export const hourSelector = selector<number>({
key: "hours",
get: ({ get }) => {
const minutes = get(minuteState);
return minutes / 60;
},
set: ({ set }, newValue) => {
const minutes = Number(newValue) * 60;
set(minuteState, minutes);
},
});
set을 사용해보자.
useRecoilState 함수로 불러올 때 첫번째 값에는 get의 리턴 값이 담기고 두번째 값에는 set을 부르는 함수가 담긴다.
const [hours, setHours] = useRecoilState(hourSelector);
input 태그의 onChange 이벤트 리스너를 설정한다.
const onHoursChange = (event:React.FormEvent<HTMLInputElement>) => {
setHours(+event.currentTarget.value);
}
// +String -> Number
'Tech > TypeScript' 카테고리의 다른 글
[Typescript / TS] Frame motion을 사용한 애니메이션 (0) | 2023.01.29 |
---|---|
[Typescript / TS] ToDo 칸반보드 만들기 (0) | 2023.01.23 |
[Typescript / TS] Recoil을 사용한 state 관리 (0) | 2023.01.16 |
[Typescript / TS] 코인트래킹 어플 만들기 (0) | 2023.01.07 |
[Typescript / TS] 타입스크립트 기초 (0) | 2023.01.02 |