Closed hyeyoonS closed 1 month ago
나의 프로필에서 프로필 변경과 닉네임 변경을 위해 리액트 훅 폼을 사용했습니다.
사용한 이유 리액트 훅 폼은 폼의 상태를 간편하게 관리할 수 있습니다. 이 훅은 폼 필드의 값을 추적하고, 유효성 검사, 에러 메시지 관리 등을 손쉽게 처리할 수 있도록 도와줍니다.
유효성 검사: 내장된 유효성 검사 기능을 제공하여 폼 필드의 유효성을 쉽게 검사할 수 있습니다. 코드에서 NICKNAME_RULES를 사용하여 닉네임 필드의 유효성 검사를 설정하였고, setError를 통해 커스텀 에러 메시지를 표시합니다.
이점 코드 간결화: 폼 상태 관리와 유효성 검사 로직을 간소화하여 코드가 간결해지고 가독성이 높아집니다. 유연성: useForm 훅을 사용하면 폼의 모든 상태와 이벤트를 완벽하게 제어할 수 있습니다. watch, setValue, handleSubmit 등 다양한 메서드를 제공하여 필요에 맞게 폼을 구성할 수 있습니다.
🧐 엥... 제어 컴포넌트, 비제어 컴포넌트가 뭐야....
<input>
, <textarea>
, <select>
와 같은 폼 엘리먼트는 일반적으로 사용자의 입력을 기반으로 자신의 state를 관리하고 업데이트합니다.
React에서는 변경할 수 있는 state가 일반적으로 컴포넌트의 state 속성에 유지되며 setState()에 의해 업데이트됩니다.
폼을 렌더링하는 React 컴포넌트는 폼에 발생하는 사용자 입력값을 제어합니다. 이러한 방식으로 React에 의해 값이 제어되는 입력 폼 엘리먼트를 “제어 컴포넌트
(controlled component)“라고 합니다.//제어 컴포넌트 예시
import React, { useState } from 'react';
const UseInput = () => {
const [input, setInput] = useState("");
const onChangeValue = (e) => {
setInput(e.target.value);
};
return (
<div>
<input onChange={onChangeValue} />
</div>
);
}
export default UseInput;
// 사용자가 입력한 값과 저장되는 값이 실시간으로 동기화 됩니다.
// ⇒ 이러한 방법으로 데이터를 전부 받아올 수 있어 유효성 검사에 탁월하지만
// 데이터를 하나하나 다 받아오므로 `비효율적`이거나 `속도가 느릴 수 있는 단점`이 있습니다.
폼을 구현
하는데 제어 컴포넌트를 사용하는 것이 좋습니다.
비제어 컴포넌트는 DOM 자체에서 폼 데이터가 다루어집니다.
제어 컴포넌트에서는 모든 state 업데이트에 대한 이벤트 핸들러를 작성해야 하지만, 비제어 컴포넌트는 ref
를 사용하여 DOM
에서 폼 값을 가져올 수 있습니다.//비제어 컴포넌트 예시
import React, { useRef } from 'react';
const UseRefInput = () => {
const inputRef = useRef(null);
const onSubmit = () => {
console.log(inputRef.current.value);
};
return (
<div>
<input ref={inputRef} />
<button type="submit" onClick={onSubmit}>
로그인
</button>
</div>
);
}
export default UseRefInput;
// ref는 값을 업데이트 하여도 리랜더링 되지 않는 특성이 있습니다.
// 따라서 입력이 모두 되고 난 후 ref를 통해 `값을 한번에 가져와서` 활용합니다.
// ⇒ state로 값을 관리하지 않기 때문에 값이 바뀔 때마다 리렌더링을 하지 않고 값을 한번에 가져올 수 있는 성능 상의 이점이 있습니다.
ref
를 사용하여 값을 가져올 때, 해당 값이 업데이트되는 타이밍을 정확히 알기 어렵습니다.React Hook Form은 비제어 컴포넌트의 장점을 살리면서도, 위에서 언급한 동기화와 데이터 유효성 검사의 문제를 보완할 수 있습니다.
최적화된 렌더링: React Hook Form은 입력 값이 변경될 때마다 폼 전체를 리렌더링하지 않고, 필요한 부분만 리렌더링합니다. 이는 성능을 최적화하고, 비제어 컴포넌트의 렌더링 최적화 장점을 제공합니다.
유효성 검사 비제어 컴포넌트를 사용하면서도 유효성 검사를 쉽게 추가할 수 있습니다. 입력 값이 변경될 때마다 유효성 검사를 실행하거나, 폼 제출 시 한 번에 유효성 검사를 실행할 수 있어 유연한 처리가 가능합니다.
간편한 데이터 접근:
React Hook Form은 useForm
훅을 통해 폼 데이터를 쉽게 접근하고 관리할 수 있게 합니다. register
함수로 각 입력 필드를 등록하면, 폼 데이터와 유효성 검사 결과를 쉽게 다룰 수 있습니다.
const {
register,
handleSubmit,
formState: { errors, isValid },
setValue,
getValues,
watch,
clearErrors,
setError } = **useForm**<IFormInput>({
defaultValues: { firstMeet: null, birthday: null, weight: null },
mode: "onTouched"
});
useForm
훅을 사용해 폼 상태를 초기화하고, 유효성 검사 모드를 설정했습니다.
defaultValues
는 폼의 초기 값을 지정하며, mode
는 필드가 터치될 때 유효성 검사를 실행하도록 합니다.
register
, handleSubmit
, formState
등 다양한 유틸리티를 제공하여 폼 필드를 등록하고, 제출 핸들러를 설정하며, 폼의 상태와 에러 메시지를 관리할 수 있습니다.
(↓↓↓아래는 사용한 유틸리티에 대한 설명)
{...register("title", {
required: "제목을 입력해주세요.",
maxLength: { value: 15, message: "최대 15자까지 작성할 수 있습니다." },
<input
className={styles.writeInput}
{...register("petName", {
...PET_NAME_RULES,
validate: {
petNameVerify: () => {
if (isPetNameConfirm) return true;
return "중복확인을 해주세요.";
},
},
onChange: () => setIsPetNameConfirm(false),
})}
placeholder={PET_PLACEHOLDER.name}
/>
watch : 필드의 값을 실시간으로 변경 (setState와 비슷한 동작)
실시간으로 감시
합니다. 값이 변경될 때마다 해당 값을 반환합니다.일부 필드의 유효성 검사
watch("petName") 는 petName 필드의 현재 값을 실시간으로 반환합니다.
폼 제출은 모든 데이터를 한번에 하지만, 사용자 편의를 위하여 폼을 2개의 섹션으로 나누었습니다.
첫번째 섹션에서 필수 입력 값이 모두 입력되어야만 다음 섹션으로 넘어갈 수 있도록 구현하기 위해, watch
를 이용해서 일부 유효성 검사를 실행했습니다.
const isSectionValid = watch("petName") && watch("type") && watch("breed") !== "" && isPetNameConfirm;
사용 예시(2): 현재 값을 실시간으로 반영
사용자가 어떤 버튼을 클릭했는지 실시간으로 반영됩니다.
const section2 = (
<>
{/* 성별 */}
<label className={styles.label}>성별*</label>
<GenderSelection register={register} watch={watch} />
{/* 중성화 여부 */}
<label className={styles.label}>중성화 여부*</label>
<NeuteringSelection register={register} watch={watch} />
handleSubmit(onSubmit)
는 onSubmit
함수가 폼 제출 시 호출되도록 합니다.const onSubmit: SubmitHandler<IFormInput> = async (data) => {
const request = {
name: data.petName,
type: data.type,
breed: data.breed,
gender: data.gender,
isNeutered: data.neutering === "Y" ? true : false,
birth: data.birthday,
firstMeetDate: data.firstMeet,
weight: data.weight,
registeredNumber: data.registeredNumber === "" ? null : data.registeredNumber,
};
const formData = new FormData();
const blob = new Blob([JSON.stringify(request)], { type: "application/json" });
formData.append("petRequest", blob);
formData.append("petImage", data.image);
const res = await postPet({ formData });
if (res !== null) {
queryClient.invalidateQueries({ queryKey: ["pets"] });
openModalFunc();
}
};
제출된 데이터를 가공하여 서버에 전송합니다. FormData를 사용해 이미지 파일과 JSON 데이터를 함께 전송합니다.
(+ 서버 응답에 따라 쿼리 캐시를 무효화하고 openModalFunc()를 실행)
setError
:setError("petName", { type: "manual", message: "중복된 이름입니다." })
는 petName
필드에 수동으로 에러 메시지를 설정합니다.clearErrors
:clearErrors("petName")
로 petName
필드에 있던 에러를 제거합니다. const checkPetNameMutation = useMutation({
mutationFn: (name: string) => checkPetName({ name }),
onSuccess: (res) => {
if (!res) return setError("petName", { type: "duplicate", message: PET_ERROR_MESSAGE.nameDuplicate });
setIsPetNameConfirm(true);
clearErrors("petName");
},
});
React Hook Form은 React에서 폼 상태 및 유효성 검사를 쉽게 관리할 수 있도록 도와주는 라이브러리입니다. 이 라이브러리는 성능 최적화, 유연성, 간편한 사용을 목표로 설계되었습니다. React Hook Form을 사용하면 최소한의 리렌더링과 코드로 복잡한 폼을 구현할 수 있습니다.
React hook form은 비제어 컴포넌트로 입력폼을 관리할 수 있도록하는 라이브러리입니다. 이전에 React의 useState를 사용하여 입력폼을 관리할 수 있었습니다. 리액트가 값을 제어하여 제어 컴포넌트라고 불리는데, 실시간으로 값을 동기화하기 때문에 입력값이 변경될 때마다 리렌더링이 발생하는 특징이 있습니다. 입력폼이 많아지면 불필요하게 렌더링 되는 부분이 늘어나고 유효성검사까지 할 경우 에러 상태를 관리할 state까지 늘어나 유지보수가 어렵습니다.
📎 질문
리액트 훅폼을 사용하셨네요. 어디에서 왜 사용했나요? 이점이 뭔가요?
✏ 구술 답변 키워드
✏ 서술 답변
리액트훅폼이란?
비제어 컴포넌트 방식으로 구현된 폼 관리 라이브러리이다. 폼의 유효성 검사와 폼 상태관리를 간단하고 효율적으로 처리할 수 있다.
리액트 훅폼 어디에 왜 사용하는가?
기존 제어컴포넌트로 폼을 다루기 위해서는 인풋요소 하나마다 state를 선언하고 각각의 핸들링함수와 에러를 위한 state, 유효성 검사함수 등이 전부 각각 존재해야했다. 리액트에서는 state가 변할때 리렌더링이 발생한다. 이런 제어컴포넌트 방식으로 폼을 관리함에 따라, 입력의 내용이나 유효성검사 결과가 바뀜 등에 따라 폼전체에 대한 불필요한 렌더링이 무수히 많이 일어나는 문제가 있었다.
리액트훅폼을 이용해 비제어 컴포넌트 방식으로 폼을 다루면 제어컴포넌트 방식으로 폼을 관리할때에 비해 관리해야할 state수가 크게 감소하여 상태관리를 단순화 할 수 있고 state로 인해 발생하던 전체 리렌더링도 감소하여 성능 최적화를 할 수 있다.
추가로, 만약 리액트훅폼 라이브러리 없이 직접 ref와 useRef를 이용해 비제어 컴포넌트로 폼을 관리하고자 한다면, ref로 참조하고 있는 DOM의 업데이트 타이밍을 알기 어렵거나, DOM의 동시접근이나 동시변경으로 인한 타이밍과 동기화 이슈를 겪을 수 있다. 또한 비제어컴포넌트는 폼 제출 시점에 단 한 번 유효성 검사를 실행하기 때문에 비제어컴포넌트 만으로는 실시간 피드백이 어렵다.
리액트훅폼은 쉽고 짧고 직관적인 API를 가져 쉽게 폼 상태를 관리하고 유효성 검사를 할 수 있다.