24일차 폼 라이브러리

osdsoonhyun·2023년 3월 13일
0

코드캠프

목록 보기
16/22
  1. 폼을 자동으로 만들어준다고? -> React-Hook-Form
  2. Validation 라이브러리? -> Yup
  3. 재사용을 위한 공통컴포넌트 만들기! -> Common-Component

HOF와 로컬스토리지를 활용한 기능 - 비회원 장바구니

  • 클릭한 내용을 로컬스토리지 장바구니에 담는다. 백엔드는 로그인을 하지 않았기 때문에 누구인지 알 수 없으니, 자기 자신 컴퓨터의 로컬스토리지에 담는다.

  • 빈 배열 basket에 클릭한 내용을 담고 로컬스토리지에 setItem을 이용하여 저장한다. 그러나 이대로 저장하면 문제가 발생한다. 배열이나 객체는 localStorage, sessionStorage에는 문자열 밖에 못 들어가기 때문에 JSON.stringify(baskets)를 이용하여 문자열로 바뀐 뒤 저장시킨다.

  • 이렇게하면, 장바구니에 데이터를 저장할 때마다 빈 배열에 저장되므로 기존의 값들이 사라진다. 이 문제를 해결하기 위해 localStorage에서 기존의 장바구니를 가지고 온다. 만약 기존의 장바구니가 없으면 빈 배열을 만들어서 가지고 온다. localStorage에서 가져왔기 때문에 문자열이므로 다시 JSON.parse(...)로 배열형태로 변환시켜 가져온다.

  • 이렇게해서 장바구니에 상품을 담고 가지고 오는 부분까지 완료하였으나, 클릭하면 매번 푸시가 되기 때문에 매번 담았던 것을 계속 담게되는 문제가 생긴다. 그래서 한번 담았던 것은 빼도록 한다. 그렇게 하기 위해 filter를 이용하여 담은 물품의 id와 기존 장바구니 안의 물품의 id와 비교하여 있다면 alert을 띄우고 return 하여 해당 물품에 장바구니에 담기는 것을 skip한다.

왜 데이터를 로컬스토리지에 저장할까?

  • 브라우저를 끄더라도 로컬스토리지에 저장한 데이터는 계속 유지가 되지만, 세션스토리지에 저장하게 되면 브라우저를 껏다 켰을 때 데이터가 삭제되기 때문이다. 각 브라우저 저장소의 특성을 고려해 기획 의도에 적합한 저장소를 선택하면 된다.

만약 로그인을 한다면?

  • 지금은 비회원으로 장바구니에 담았지만, 로그인을 하게 된다면 백엔드와 연동을 하게 될 때 로그인한 유저의 로컬스토리지에 장바구니에 데이터가 있으면, 데이터를 꺼내어 유저의 장바구니에 넣어주도록 백엔드에 요청하여 자동으로 담아줄 수 있지만, 이것은 회사의 기획의도에 따른다.

비회원 장바구니 보기 기능

만약 내 장바구니 보기를 하려면?


1. 내 장바구니 보기 페이지를 만드려면, localStorage에 저장한 데이터를 가지고 와서 state에 넣어준다.
2. 프론트엔드 서버에서 프리렌더링이 이루어질 때에는 localStorage가 존재하지 안히 때문에 오류가 발생한다.
-> useEffect를 사용하여 브라우저에서 페이지가 마운트 될 때에만 해당 코드를 실행시킨다.
3. 로컬 스토리지에 넣은 데이터를 넣은 state를 map을 이용하여 화면에 뿌려주면 된다.

폼 라이브러리

  • 폼 라이브러리 변천사 : react-form -> redux-form -> formik -> react-hook-form

React-hook-form이란?

  • 폼 라이브러리를 이용하여 그동안 우리가 onChangeInput, state 등을 만들어서 직접 관리했던 폼들을 미리 만들어 놓고, 라이브러리 형태로 사용할 수 있도록 해주어 코드가 굉장히 비약적으로 짧아지는 것을 두 눈으로 확인했다!
  • 위의 다양한 라이브러리 중 최근에 함수형 컴포넌트에서 가장 많이 사용되는 react-hook-form을 사용하기로 했다.

React-Hook-Form의 구조 - 비제어 컴포넌트와 제어 컴포넌트

  • 비제어 컴포넌트 : 바닐라 자바스크립트 처럼 sumit함수를 실행할 때 ref 로 input값을 한번에 변경합니다. onChange가 일어나지 않는다.

  • 제어 컴포넌트 : 사용자의 입력을 기반으로 state를 실시간으로 관리(setState 사용). 쉽게 말해 입력이 될 때마다 state값이 실시간으로 변경됩니다.

  • 제어 컴포넌트는 버튼색 바꾸는 isActive 경우 제어컴포넌트를 활용하여 입력할 때마다 state를 업데이트 해주고 변경을 체크해야 하기 때문에 제어 컴포넌트를 사용한다.

  • 이전까지는 제어 컴포넌트 방식이였으나, react-hook-form은 비제어 컴포넌트 방식으로 입력할 때마다 state가 바뀌지 않아 리렌더링 없이 사용하여 성능이 빠르다.

  • 게시글처럼 많은 양의 입력값이 있어서, 입력할 때마다 렌더링이 일어나기 때문에 데이터가 큰, 텍스트가 많은 데이터 사용시 성능이 느려지기 때문에 비제어 컴포넌트가 효율적이다.

  • 그러나 react-hook-fomr은 비제어 컴포넌트이기 때문에 한번만 렌더해서 보내기 때문에 정확성이 떨어진다.

  • 따라서 중요한 데이터를 입력받는 페이지는 제어 컴포넌트를 사용해 변경사항에 대한 정확도를 높여주시는게 좋다.

form 태그

  • form 태그는 input에 적힌 내용을 전송해주는 기능이 있다.

  • react-hook-form 에서는 이 기능을 이용한다.

  • button 태그의 type에 reset을 주게 되면 클릭시에 폼 안에 있는 인풋값을 초기화 한다.

  • submit을 주시게 되면 form태그에 바인딩된 submit 함수를 실행시키게 된다.

  • 버튼 타입의 기본은 submit이다. 따라서 폼안에서 사용하게 되면, 따로 명시 하지 않아도 submit의 기능을 하게 된다.

  • 만일 form 태그 내에서 form과 상관없는 버튼을 만들어야 한다 하면, type을 button으로 해야 한다.

form 내부의 button type 간단 정리
reset : form 내부의 input 값이 모두 삭제 된다.
submit : form 내부의 input 값이 백엔드로 보내진다. → 기본값
button : 나만의 버튼을 만들고 싶을때 사용한다.

react-hook-form 동작원리

  • React-hook-form을 사용한 작성은 ajax(비동기 javascript통신)으로 지금까지 만들었던 방식과는 조금 다르다.

  • 먼저 입력값을 받을 Input태그들을 form이라는 태그로 감싸준다.

  • 그리고 변경되는 input에 대한 처리는 useForm()으로 state를 등록하는데 필요한 모든 기능(onChange,onClick, onError 등)이 담겨있는 register를 가져와 input에 스프레드 시켜준다.
    스프레드를 시켜주며 register(“”)안에 input의 이름, 즉 state변수명도 넣어준다.
    이렇게 담긴 값들은 formState에 담는다!

  • 최상단에 감싸진 form 태그에는 onSubmit()이라는 함수를 연결 시켜주었고, 이렇게 연결해준 onSubmit이 작동될 수 있로고 form 태그 내부에 button태그를 넣어주어 버튼을 누르면 onSubmit에 담은 함수가 실행이 되도록 한다.
    (버튼의 default type은 submit이기 때문에 다른 기능이 들어간 버튼을 만들어 추가적으로 넣어줄 때는 type을 button으로 주어 사용해야 한다)

  • 그리고 여기서 중요한 것은 onSubmit에 만든 함수를 넣어줄때는 등록한 데이터를 담아서 실행해야하기 때문에, hook-form에서 제공하는 handleSubmit으로 감싸서 실행해야 합니다.

react-hook-form 사용하기

  • button 등록하기를 누르게 되면 button 타입의 default는 submit이기 때문에 form 태그의 onSubmit이 실행되는데 onClickSubmit 함수를 실행하게 된다.

  • button 등록하기를 눌렀을 때 다른 함수를 실행시키고 싶다면 button 태그의 type 속성을 'button'으로 변경해주고 onClick 속성을 추가해야 한다.

  • {...register('writer')} 태그를 추가한다.

  • 비제어 컴포넌트이기 때문에 입력할 때마다 writer state에 저장되지 않고 handleSubmit이 onClickSubmit으로 입력한 register의 state로 writer, title, contents를 전달해준다.

  • form 라이브러리를 사용하여 함수도 하나만 만들고 state도 안 만들어도 되기 때문에 효율적이다.

register, handleSubmit, form

register : state를 등록하는데 필요한 모든 기능이 들어있다.

handleSubmit : resister에 적힌 state를 등록해주는 함수 이다.

form : 실제 html에 있는 input들을 묶어주는 태그이다.

전체코드

import { useForm } from "react-hook-form";

export default function ReactHookFormPage() {
  const { register, handleSubmit } = useForm();

  // 등록하기 함수 -> handleSubmit이 조종해주는 함수 입니다.
  const onClickSubmit = (data)=>{
    console.log(data)
  }

  return(
    <form onSubmit={handleSubmit(onClickSubmit)}>
      <input type="text" {...register("writer")}/>
      <input type="text" {...register("title")}/>
      <input type="text" {...register("contents")}/>
      <button type="reset"> 등록하기 </button>
    </form>
  )
}

검증라이브러리(yup)

yup 이란?

  • 지금까지는 검증을 할 때 우리가 직접 검증단계를 만들어 진행해왔다. 하지만 실제 검증 과정은 조금 더 복잡하고 까다롭게 진행된다.
  • 검증과정에는 숫자인지, 문자인지, 최소 8자리인지, 특수문자가 들어가는 지 등의 단계 들이 있습니다.
  • yup은 이렇게 까다로운 검증과정을 대신해주는 라이브러리 이다.

yup Github(공식문서 따로없음)

  • yup과 같은 검증 라이브러리는 보통 form과 함께 사용한다.
  • 따라서 리액트 훅 폼과 함께 사용할 것 이며, 함께 사용하실땐 리액트 훅 폼의‘Schema Validation’을 잘 보고 함께 사용해주시면 된다.

react-hook-form과 yup 함께 사용하기

  • yup관련 코드를 작성하고, resolver에 연결을 해주면 폼과 yup을 함께 사용하기위한 준비는 완료된다.
  • ‼️ resolver에는 yup이외에도 다른 검증 라이브러리를 사용할 수 있습니다. ‼️
import * as yup from 'yup'
import {useForm} from 'react-hook-form'
import {yupResolver} from '@hookform/resolvers/yup'


// yup 에러메세지 생성해주기 -> 제어 컴포넌트 형태로 사용해야 합니다.
const schema = yup.object().shape({
	myWriter : yup
				.string()
				.email('이메일 형식이 적합하지 않습니다.')
				.required('필수 입력값입니다.'),
	myPassword : yup
  					.string()
					.min(4,'비밀번호는 최소 4자리 이상입니다.')
					.max(15,'비밀번호는 최대15자리 입니다.')
					.required('필수 입력값 입니다.') 
})

const {register , handleSubmit, formState} = useForm({
  // schema는 위에서 만들어 둔 schema입니다.
  resolver : yupResolver(schema),
  mode : "onChange"
})
  • 에러메세지는 조건 괄호 안에 넣어준다.
  • 만약 작성시 에러메세지를 바로 밑에 넣어주기 위해 비제어 컴포넌트이므로 변경이 필요하다.
  • mode: 'onChange'를 추가하여 변경할 때마다 검증을 하여 리렌더링 시켜준다.

yup에 정규표현식 추가하기
yup.string().matches(/ 원하는 정규표현식! /)

schema란?
→ 구조를 의미
보통 하나의 구조를 schema라고 하는데 만들어둔 하나의 yup을 schema라고 하기로 한다.

공통컴포넌트 재사용

  • 위 작성자, 제목, 내용, 비밀번호 input 컴포넌트와 button 컴포넌트를 공통컴포넌트로 빼서 import 하는 것이 코드 수를 줄여주진 못하지만, 나중에 테마를 바꾼다던지, 변경이 필요할 때 한번에 변경이 가능하다.
  • 유지보수성을 높이기 위해 효율적으로 리팩토링하기

소소한 꿀팁

타입스크립트 - 타입

  • IBoard의 타입에 사용하지 않는 타입이 많기 때문에 Pick을 이용하여 사용하고자 하는 타입만 사용하였다.
  • 나중에 사용하지 않을 타입이 몇 개 안된다면, Omit을 이용하여 빼는 것이 효율적이다.

localStorage 확인하기

  • 개발자 도구 - Application에 들어가서 localStorage에 들어온 것을 확인할 수 있다.

리팩토링

  • 지금은 성능보다는 리팩토링 관점을 신경써서 한 컴포넌트에 70줄 안넘어가도록 하는 것이 좋다.

form (Feat.button)

  • button type='reset'은 폼 안에 있는 input들을 초기화시켜줘!

  • input이 지워진다고 state가 지워지는 것은 아니다.

  • button type='submit'form onSubmit={백엔드 api주소}로 자동으로 input 안의 데이터들이 날아간다.

  • button type='button'으로 해야 onClick~ 을 적용할 수 있다.

  • form으로 감싸게 되면 button 태그의 default는 type='submit'이 되기 때문에 주의해야 한다.

0개의 댓글