프로젝트 진행 중,
어떤 목록들을 선택하고 그 목록들을 한 화면에서 나타내어줘야하는 상황에서,
내가 선택한 목록들이 많을 경우에 한 화면에 모든 목록들을 나열하기엔 UI적인 면에서 좋지 않다고 판단되었다.
그래서 해결 방법을 찾던 도중 팀원에게 carousel을 사용해보는건 어떠냐 라는 이야기를 들었고,
여러 자료들을 참고하여 carousel을 구현해 보았다.

먼저 캐러셸을 만들 수 있는 방법은 다양하다.
1. 라이브러리를 활용한다.

- React-Slick

- React-Material-Ui-Carousel

- React Responsive Carousel

 

2. Reat-Hook 을 사용해 직접 구현한다.

 

나는 현재 React를 공부하고 있는 사람이고, 아직 초보자이기 때문에, React 라이브러리를 활용하는것도 좋지만, 직접 로직을 이해하며 코드를 짜보는것이 좋겠다고 판단되었다.

그래서 2번 방법을 택하였고, React Hook과 프로젝트에서 활용하고 있는 css 라이브러리 styled component 를 활용하여 제작해보았다.

 

 

그럼 먼저 carousel에 구조에 대해서 살펴보도록 하겠다.

기술 선택 목록 - carousel 구조 설명

허접한 내 그림 실력에 큰 아량을 베풀어 주길 바라며..

위 사진에서 검은색으로 표시된 부분이 바로 기술을 표현할 전체 Container라고 보면 되고,
초록색 부분이 실제 기술들이 보여지는 부분,

그리고 핑크색 부분이 선택한 기술들이 담겨져 있는 Container라고 보면 된다.

 

나는 양 끝에 있는 화살표 버튼을 눌렀을 경우 뒤에 남겨져 있는 기술들이 보여지길 바랬고, 화살표에 어떠한 onClick 이벤트를 주어야겠다고 생각했다.

 // 현재 슬라이드를 나타내는 useState
  const [currentSlide, setCurrentSlide] = useState(0);
// 초록색 부분에서 보여지는 목록 개수
  const viewingSkill = 4;
  // 현재 슬라이드를 제외한 Slide 개수 (skillsImg는 임의로 만들어준 mock data(선택 항목)이다.)
  const TOTAL_SLIDES = skillsImg.length / viewingSkill - 1;
 // 첫번째 슬라이드
  const firstSlide = 0;
  const handleClickNextSlide = () => {
    if (currentSlide >= TOTAL_SLIDES) { // 더이상 이동할 슬라이드가 없다면
      setCurrentSlide(firstSlide); // 첫번째 슬라이드로 이동. ->useState 활용
    } else {
      setCurrentSlide(currentSlide + 1); // 아니라면 다음 슬라이드로 이동.->useState 활용 
    }
  };
  const handleClickPrevSlide = () => {
    if (currentSlide === firstSlide) { // 이전버튼을 눌렀는데, 첫번째 슬라이드라면
      setCurrentSlide(TOTAL_SLIDES); // 마지막 슬라이드로 이동 ->useState 활용
    } else {
      setCurrentSlide(currentSlide - 1); // 아니라면 전 슬라이드로 이동 ->useState 활용
    }
  };

그렇게 버튼 이벤트가 발생했을 시 실행시키기 위해 작성한 코드가 바로 윗 코드이다.

자세한 설명은 주석으로 작성해 주었다.

이렇게 해준 뒤, 내가 선택한 목록들을 보여주기 위해 어떠한 특정 DOM을 선택해주는 useRef또한 사용해줘야했다.

 const slideRef = useRef(null); // slideRef라는 객체 useRef를 활용하여 생성.
  
  useEffect(() => { // useEffect를 활용하여 
    slideRef.current.style.transition = 'all 0.5s ease-in-out'; // 어느 슬라이드를 보여주고 있는지 지정함.
    slideRef.current.style.transform = `translateX(-${currentSlide}00%)`; // 가로 슬라이드이기 때문에 translateX를 활용하였다.
  }, [currentSlide]);

translateX 을 활용하여 가로로 핑크색 항목들이 이동할 수 있게 했고, 위와 같이 useRef와 useEffect를 활용하여 이동하는 활동을 주었다.

그런 후 , return 부분을 보면

<S.SkillBoard> //  검은색 전체 Container부분
  <S.LeftAngle onClick={handleClickPrevSlide} />
    <S.SkillContainer> // 초록색 선택 목록이 보여지는 부분
       <S.SkillSlide ref={slideRef}> // 선택한 항목이 담겨있는 핑크색 Container
           {skillsImg.map(({ imageUrl, id }) => ( // 내가 선택한 목록들을 임시로 만들어둔 mock data
             <S.SkillImage key={id} src={imageUrl} /> // 선택 목록에 대한 이미지
           ))}
        </S.SkillSlide>
   </S.SkillContainer>
  <S.RightAngle onClick={handleClickNextSlide} />
</S.SkillBoard>

위와 같이 작성 했다.
검은색 SkillBoard로 감싸주고,
초록색 SkillContainer에서는 overflow:hidden; 이라는 css 속성을 주어 범위가 넘어갔을 시 보여지지 않게 했다.
핑크색 SkillSlide에서는 mock data인 skillsImg라는 데이터를 담을 수 있게 범위를 목록에 맞춰 설정해주었고,
.map 을 사용하여 배열에 있는 값을 가지고 올 수 있게 했다.
또한 각각 옆으로 이동하는 버튼에 대해서는 이미지로 불러온 다음, onClick시 아까 만든 함수들이 실행 될 수 있도록 했다.

styled-components 에 대해서도 살펴보자.

// * : 이동 버튼 포함 검은색Container
export const SkillBoard = styled.div`
  position: relative;
  display: flex;
  justify-content: space-between;
  align-items: center;

  height: 20%;
  width: 100%;
  padding: 5px 0px; // height, width, padding은 적절하게 조절하기 !!
`;
// 초록색 Container
export const SkillContainer = styled.div`
  position: relative;
  display: flex;
  align-items: center;

  height: 100%;
  width: 100%;
  overflow: hidden; // 넘어가는 부분은 안보이게 해주었다.
`;
// 핑크색 Container 
export const SkillSlide = styled.div`
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;

  height: 100%;
  width: 100%;
  gap: 12px; // 각 목록들 사이의 간격 조정. 이것 또한 객체화 해주는게 좋음.
`;
// 선택 항목 Img 크기 지정
export const SkillImage = styled.img`
  object-fit: cover;
  width: 52px;
  height: 52px;
  border-radius: 50%; // 위 그림에서와 같이 동그란 형태로 표현
  box-shadow: 1px 1px 1px 1px #cdcdcd; // 옅은 회색으로 그림자 효과 줌.
`;


// LeftAngle과 RightAngle에 관한 부분은 이미지를 선언 한 후 그 이미지에 대한 적절한 크기 값을 적용해주면 된다.

초록색에서는 overflow:hidden이 포인트이고, 핑크색에서는 gap이 포인트 인거 같다. 물론 gap을 활용 안해도 되지만, 초반에 array mock data를 불러왔을 때 나는 항목들의 이미지가 너무 붙어있어서 어찌 해야하나 고민을 많이 한 거 같다.

처음 css를 다뤄보다보니 모르는 부분이 많았지만, 점점 익숙해지고 재미가 있다고 느껴서 앞으로 꾸준히 학습하며 실력을 늘려봐야겠다.

 

이렇게 위와 같은 방법으로 코드를 작성하면 손쉽게 라이브러리를 사용하지 않고도 carousel을 구현 할 수 있다 !


추가로...

코드를 공부하면서 짜다 보니, 아무래도 변수명을 내 마음대로 작성한 경향이 있었다.
(다른 팀원이 알아보기 어렵고, 숫자들에 대하여 객체화 시켜 네이밍 처리를 해주지 않으니 리펙토링에도 어려웠다.)
추후 pr을 날리고 code review를 받을때 네이밍에 관한 부분에 대한 review를 받았다. 관련 내용을 읽어보고 난 후 네이밍 관련 부분을 수정하였고, 추후 네이밍에 관한 부분도 블로그에 작성해주면 좋을 거 같다고 생각했다. 코드짜는거와 별개로 네이밍도 항상 고민이 많은 거 같다. 익숙해지기 위해선 관련 자료들도 찾아보고 공부한 후 많이 해봐야할 거 같다는 생각이 들었다.

 

 

 

 

이번 프로젝트에서 css도 처음 다뤄보고 styled-components도 계속 활용 해보고 있는데, 초반에 배우는 과정 중에서 팀원에게 많이 피해를 준거 같아 너무 미안한 마음이 크게 들었다.

하지만 지금은 열심히 공부하여 익숙해진 상태이고, css 는 앞으로 계속해서 접해보면 익숙해지지 않을까 싶다.
storybook 도 이번기회에 사용해 보았는데, 이 또한 많이 익숙해진 상태이다. 익숙해졌지만 완벽하진 않으니 앞으로 많이 사용해보고 익히며 부족한 실력을 길러야 겠다. (필요하다면 styled-components와 css 그리고 storybook에 대해서도 정리해보자!)






참고 자료

peppermint100.log-[JS]React Hooks로 Carousel Slider 만들기

1004walnut - [React] useState, useEffect, useRef을 이용하여 캐러셀 슬라이더 구현하기

 

코넥트 프로젝트를 진행하던 도중 나의 리액트 선생님이신 윤호님께 관심사의 분리선언적 프로그래밍에 대해서 듣게 되었다. 굉장히 흥미롭고 혁신적인 내용이었고, 앞으로 내가 코딩 함에 있어서 굉장히 중요한 요소로 작용할 거 같다는 느낌이 팍팍 들어 이번기회에 여러가지 자료를 찾아보고 공부해본 후 내용을 정리해볼까 한다.

관심사의 분리란?                                                                                                         

간단한 공부를 위한 코드작성이나 규모가 작다면 모르겠지만, 우리는 코드를 작성하다보면 하나의 함수, 변수, 클래스, 컴포넌트에게 너무나 많은일을 한번에 부여하게 되는 경우가 생기게 된다. 그렇게 긴 코드를 작성하게 되면 읽는 사람은 물론 나 자신도 이게..뭐어..라고..했지...? 하는 상황이 발생하게 된다.

 

바로 이런 상황에서 필요한 것이 '관심사의 분리' 이다. 관심사의 분리는 말그대로 한번에 한가지 일만 하도록 단위를 잘게 나누어서 단위별로 하나의 관심사만 갖도록 하자! 라는 것을 의미한다.

이렇게 단위를 잘게 나누면 코드를 딱 봤을때 한눈에 파악하기 쉽기때문에 오류가 생겼을때나 수정이 필요할때 빠르게 해결할 수 있다는 장점이 있다. 이런것을 멋진말로 Divide & Conquer(분할 & 정복)이 용이하다 라고 한다.

관심사의 분리가 적절히 된 코드는 결합도가 낮고 응집도가 높다고 표현하곤 하는데,
여기서에 결합도는 '각각의 코드가 서로 얽혀있지 않다!' 를 의미하고
응집도는 '유사한 내용끼리 잘 묶여있다!' 를 의미한다.
그래서 우리는 결합도와 응집도 사이에 그 적절한 지점을 찾아 관심사의 분리를 적용해주어야 한다. 

 

 


프론트엔드 개발에서의 관심사의 분리는 View(UI적인 부분) 와 Logic의 분리로 이루어진다.

 

아직 관심사의 분리도 덜 되고 초보여서 엉망징창인 로그인 페이지에서의 내 코드를 데리고 와 일단 설명을 해보자면...

View 부분

먼저 View와 관련된 코드에서는 input 태그를 활용하여 입력받을 수 있는 창이 뜨게끔 만들어주었다. 위 사진을 보면 한 함수 안에서는 오직 UI와 관련 된 부분만 적혀져 있다는 것을 알 수 있다. 그러면 로직과 관련 된 부분은 어디에 있냐? 

Logic 부분

바로 여기에서 관리를 해주고 있다. 위 View와 관련된 코드에서 input창으로 받은 값들을 props로 넘겨 로직과 관련 된 부분은 모두 여기서 관리를 해주고 있는것이다. 이 페이지 전체를 보여주진 못했지만 UI 와 관련 된 부분은 이 함수에선 다루고 있지 않다.

위와 같이 분리 하면 한 함수, 컴포넌트, 클래스 당 코드가 간단해지고 오류 해결 수정등이 용이해진다. 

내 코드가 아직 많이 부족하다고 말한 이유는 props 관리를 중앙집중적으로 해주고 있지 않아, 나중에 props들이 반복되고 많아져 코드가 길어지게 될 가능성이 높고, UI적인 부분에서 register 함수를 보면 이메일 입력 관련 규약 부분이 어떤 역할을 수행하고 있는지 한눈에 알아보기 어렵기도 하기 때문이다.

위와 같이 props와 관련 된 문제는 사실상 상태관리 라이브러리 (redux, recoil)를 사용해주면 가장 쉽게 해결할 수 있긴 한데, 우리는 프론트엔드 담당자이기 이전에 개발자 이기때문에 라이브러리를 최대한 사용하지 않고 context api를 활용하여 기본 단계부터 천천히 이해해보고 구현해보자고 이야기 하였다.

register 함수(react-hook-form에서의 기능)도 로직과 관련된 부분이 포함되어져있기 때문에 라이브러리를 사용하지 않고 직접 custom hook을 제작하여 관심사의 분리를 해나갈 예정이다.



같은 팀원분의 코드는 너무나 완벽하게 정리가 되어 있는 상태이지만,
아직 내 코드는.... 완벽하게 관심사의 분리가 안 되어 있다... 하하
너무너무 민망하지만... 설명을 위해...가지고 왔ㄷ..ㅏ

 

이렇게 View와 Logic으로 나눠줌으로써 한 컴포넌트에선 하나의 책임만 지게 되고 이는 재활용하기에도 쉽고 다른 컴포넌트들과 조합하기에도 쉬워졌다.
custom hook을 제작하는 방법 또한 관심사의 분리라고 말할 수 있는게, 로직을 독립적인 함수로 분리하여 여러곳에서 재활용 할 수 있는 형태로 만들어지기 때문이다.
이렇게 위와 같이 관심사의 분리를 해주고 props들은 중앙집중적으로 관리 해주면 좋은 형태의 코드를 만들 수 있을 것이다.

선언적 프로그래밍                                                                                                              

더 좋은 코드를 만들자에서의 또 다른 키워드는 바로 명령형 코드 / 선언형 코드 이다.
먼저 명령형 코드가 무엇이고 선언형 코드가 무엇인지에 대하여 살펴보자.


명령형은 WHAT?을 의미하고 선언형은 HOW?를 의미한다.

잉? 이게 뭔소리여 싶은 사람들은 밑에 글을 조금 더 살펴보자.

(사실 뭐 내가 너무 간단하게 설명해서 여기서 바로 뭔 말인지 알아들으면 당신은... 천재..)

내가 너무너무 존경하는 진유림님 블로그 글에서의 코드를 잠깐 빌려오면,

function double(arr) {
  let results = []
  for (let i = 0; i < arr.length; i++) {
    results.push(arr[i] * 2)
  }
  return results
}

arr 배열에 있는 모든 숫자를 하나씩 제곱하여 results 배열에 넣어주어라! 라는 명령형 코드.

function double(arr) {
  return arr.map(item => item * 2)
}

모든 arr배열에서의 숫자가 제곱된다! 라는 선언형 코드.

위 두 코드를 살펴보면 HOW? WHAT?이 무슨 의미였는지 감이 잡힐 것이다.

명령형 코드에선 어떻게 코드가 동작되는지에 초점이 잡혀져 있고 선언형에서는 이 코드가 무엇인지에 조금 더 초점이 잡혀져 있다.

 

그렇다고 해서 선언형 코드에 WHAT만 담겨져 있냐? 그건 아니다.

선언형 코드명령형 코드에서 HOW부분이 숨겨져 있는 형태라고 생각하면 편할 것이다.

 

오~~그럼, WHAT?에 관한 부분만 노출하면 그것은 선언형 코드라고 할 수 있는건가~? 근데 그건 또 아니다.

온전히 WHAT에 관한 부분만 노출하게 되면, HOW에 관련된 부분이 명시되어 있지 않으니, 코드가 잘못 사용되거나 꼬여버릴 수 있기 때문이다. 그렇기 때문에 선언적 함수는 HOW에 관련된 부분이 적절하게 나타나져 있고, 언제 불러도 같은 결과를 줄 수 있는 함수! 여야 한다.

1. HOW에 관련된 부분이 적절하게 추상화 되어져 있고

2. 언제 불러도 같은 결과를 적절히 줄 수 있고

3. WHAT이 함수명에 적절히 표현 되었으면

그제서야 그것이 선언형 코드 라고 말 할 수 있는것이다.

위와 같이 선언적으로 코드를 작성하게 되면 읽기도 편하고 오류수정, 재사용에도 편하게 된다는 장점이 있다.

명령형으로 코드를 작성하는 것도 좋지만 아무래도 읽는 시간이 좀 더 걸리게 되니....

그래서 나도 앞으로 코드를 짤 때 선언적인 형태의 코드로 작성 해볼 예정이다.

 

 



프로젝트를 진행하며 초반부분과는 다르게 어떻게 구현할것인가 뿐만아니라 코드의 재사용성과 같은 리펙토랑 관련 부분들을 어떻게 향상 시킬 것이냐에 대해서 좀 더 많이 고민하게 되는 것 같다. 같이 프로젝트를 진행하며 좋은 정보들을 가지고 함께 이야기 나눠준 윤호님께 감사드리고 앞으로 나도 이번주에 공부하여 알게된 관심사의 분리와 선언형 코드를 활용하려 코드를 작성해야겠다고 생각했다.


 

 

 

 

관심사의 분리 참고  - 
https://velog.io/@kykim_dev/%EA%B4%80%EC%8B%AC%EC%82%AC%EC%9D%98-%EB%B6%84%EB%A6%ACSeparation-of-Concerns-SoC%EC%99%80-Custom-Hook

https://velog.io/@eddy_song/separation-of-concerns

선언적 프로그래밍 참고 - 

https://milooy.github.io/dev/220810-abstraction-and-declarative-programming/

 

어제 react-hook-form에 관하여 공식문서를 보고 조금 더 공부하다가 useForm에서의 onChange모드를 발견하게 되어 이를 활용해 회원정보 제출페이지를 제작해보기로 결심하였다.

 

먼저 설명에 앞서 나의 프로젝트 컴포넌트들의 구조를 간단히 설명해보자면, 

회원정보입력페이지 구조

react-router-dom에서 Route태그를 활용해 입력받아야하는 정보들별로 페이지를 구분하였고, 이를 상위컴포넌트에서 한번에 호출하여 관리하고 있다.

이러한 환경속에서 공통적으로 쓰이게 되는 hook들은 상위컴포넌트에 선언해주었고, useForm 또한 상위 컴포넌트에 선언해주었다.

const {
   register
  } = useForm({
    mode: 'onChange',
  });

이렇게 onChange모드로 설정해주면, 실시간 유효성 검사가 가능하므로, register함수안에 입력되고 있는 정보가 어떠한 조건으로 인하여 만족하지 않는다면 오류 message를 보이게 할 수 있었다.

<input
        {...register('nickname', {
          minLength: {
            value: 2,
            message: '닉네임을 입력해주세요',
          },
          required: true,
        })}
        type="text"
        placeholder="nickname"
      />
      <span>{errors?.nickname?.message}</span>

나는 다음과 같이 register 함수의 기능중 하나인 minLength를 사용하여 자리수 제한을 주었고, 자리수 제한에 도달하지 못했을 때 message를 출력할 수 있게 해주었다. (이 과정에서 상위컴포넌트에 stateForm:{errors}를 useForm안에 선언해주어야한다.)

 

 

 

에러메세지를 도출하는 과정은 onChange모드로 해결이 됐지만, 나는 에러메세지를 보고도 무시하고 다음으로 넘어가는 경우들을 위해 버튼을 비활성화 하는 과정 또한 필요하다고 생각했다. 

 

react-hook-form에 관하여 좀 더 찾아보던 중, getFieldState 라는것을 발견하게 됐는데, 이 기능은 register함수에서의 상태 값을 실시간으로 불러 올 수 있는 역할을 제공해주었다. 공식문서에서의 설명을 좀 더 보충하자면 nested field에서 활용하기에 유용하다고 한다.

 

나는 getFieldState를 활용하는 방법은 다음과 같다.

const {
    getFieldState,
    register,
    formState,
    formState: { errors },
    handleSubmit,
  } = useForm({
    mode: 'onChange',
    defaultValues: {},
  });

먼저 getFieldStateformState를 상위컴포넌트에 선언해준 후(모든 공유하는 값들은 props로 전달.) 

getFieldState('nickname', formState);
const fieldState = getFieldState('nickname');

이렇게 formStateregister함수에 선언된 'nickname'을 함께 getFieldState로 하위컴포넌트에 선언해 주었다.

 

그럼 getFieldState가 어떤 state값들을 받아오는지 확인하기 위해 먼저 fieldState를 console창에 찍어보자.

fieldState를 console창에 찍어보았을 때.
input창에 값을 입력해 넣으니, isDirty가 true로 변경되었다.

위와 같이 invalid, isDirty, isTouched 이렇게 3개의 값과 error까지 확인 할 수 있었고 나는 input창에 값을 입력했을때 버튼이 활성화 되게 하고싶었기 때문에 isDirty를 활용하면 되겠다고 생각했다.

 

나는 fieldState.isDirty의 값이 false일때 button을 disabled=true 시켜주었고, 만약 true 라면 disabled= false로 변경해주어 버튼이 다시 원래대로 작동 되게 코드를 짜보았다.

getFieldState를 활용하면 이렇게 실시간 register 함수의 값을 다룰 수 있었고, 그에 따른 기능도 부여해 줄 수 있었다.


아직 react-hook-form에 대해서 완벽히 숙지했다고 판단하지 않아, 추후에 useContext 기능도 함께 사용해보고 방금 사용한 getFieldState도 복습하며 좀 더 완벽히 알았을 때 전체적인 코드와 함께 정리글을 한번 더 올려야겠다고 생각했다.

프로젝트를 진행하다가 회원가입 정보를 받고 제출을 할때 form태그를 사용해야하는 경우가 생겼다.

form 태그를 활용하기 위해 공부해 보던 중 많이 사용 된다는 react-hook-form을 알게 되었고, 사용법에 대해 간단히 정리해보고자 한다.

 

Register

이 메소드는 input 값이나 선택한 요소 또는 적용 validation을 가지고 오는 역할을 제공해준다. 

밑에 register함수의 사용법을 살펴보면 좀 더 이해하기 쉬운데,

<input 
  onChange={onChange} 
  onBlur={onBlur} 
  name={name} 
  ref={ref} 
/>

예를 들어 이런식으로 작성해줘야했던 input 태그를

const { onChange, onBlur, name, ref } = register('firstName'); 

<input {...register('firstName')} />

이런식으로 위에 register 함수를 선언하고 input 태그에 선언해주면 좀 더 간단한 형식의 input태그가 완성된다.

 

register 함수를 활용하면 input의 유효성 검사도 쉽게 할 수 있게 된다.

 

좀 더 다양한 활용 코드를 살펴보면,

<input
  {...register("test", {
    minLength: {
      value: 1,
      message: 'error message'
    }
  })}
/>

 

최소길이를 설정하여 넘게 되면 에러메세지를 보내줌.

 

<input
  {...register("test", {
    pattern: {
      value: /[A-Za-z]{3}/,
      message: 'error message'
    }
  })}
/>

A부터 z까지 RegExp 개체를 활용하여 value값을 지정해줌. + 에러메세지 포함.

<input
        {...register('email', {
          required: 'Email is required',
          pattern: {
            value: /^[_a-z0-9-]+(.[_a-z0-9-]+)*@(?:\w+\.)+\w+$/i,
            message: '이메일 형식으로 입력해주세요.',
          },
        })}
        placeholder="email"
/>

실제 프로젝트 코드에 활용된 이메일 입력 input 태그.

 

위와같은 경우들을 살펴 볼 수 있다.

handleSubmit

handleSubmit은 이름에서도 유추할 수 있듯이, form을 submit하는 상황에서 활용 되는 기능이다.

이 함수는 form validation이 성공적으로 제출 되면 form의 data를 받아오게 된다.

const onSubmit = () => {
  throw new Error("에러가 발생했습니다.");
};

<>
  <form
    onSubmit={(e) => {
      handleSubmit(onSubmit)(e)
      // 여기에 넘겨받을 데이터 입력을 위한 코드를 작성하여 줄 수 있다.
      .catch(() => {});
    }}
  />
</>;

이런식으로 form태그 상에서 handleSubmit을 선언해주고,

handleSubmit(onSubmit)();

handleSubmit또한 함수 내부에 선언하여 주면, form 속에 data를 제출 할 수 있게 된다.

여기서 넘겨받은 e를 console.log로 출력해주면 form형식으로 넘겨받은 data가 console 창에 찍히는 것을 확인 할 수 있다.

setError

이 함수를 register 함수에서 에러를 선언할 때 register함수에서 작성할 수 없었던 에러처리 관련 코드들을 추가하는데 도움을 제공하여 준다.

register('registerInput', { minLength: 4 }});
setError('registerInput', { type: 'custom', message: 'custom message' });

전체적인 코드를 살펴보자면,

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

const App = () => {
  const { register, setError, formState: { errors } } = useForm();

  return (
    <form>
      <input {...register("test")} />
      {errors.test && <p>{errors.test.message}</p>}

      <button
        type="button"
        onClick={() => {
          setError("test", { type: "focus" }, { shouldFocus: true });
        }}
      >
        Set Error Focus
      </button>
      <input type="submit" />
    </form>
  );
};

위 코드에선 버튼이 클릭 되었을 때, 에러가 발생하게 되고, 에러메세지는 input 태그 다음 p태그로 감싸져 있는 부분에서 발생하게 된다. 이와 같이 setError를 useForm에서 선언해주게 되면, 다양한 조건들을 setError 함수로 선언하여 error가 발생하는 시점을 다양화 시킬 수 있게 된다. 

if (password !== verifiedPassword) {
      setError('verifiedPassword', { message: 'Password is not same' }, { shouldFocus: true });
 }

예를 들자면 코넥트 프로젝트에선 비밀번호와 비밀번호 확인 문자열이 동일하지 않을 경우에 setError를 호출 하게 지정해 놓았다.

 

실시간 onChange 값 검사

use-react-form 기술을 정리하던 도중 발견 된 사실인데, 실시간으로 유효성 검사가 가능한 방법이 있다는 것을 알게 되었다.

useForm({ mode: "onChange" });

이렇게 useForm의 모드를 onChange로 설정해주면, 실시간으로 정보가 업데이트 되어 조건을 만족시키지 않았을 때 마다 에러메세지가 계속해서 뜬다고 한다.
input에 validation을 지정해준 후, form으로 감싼 태그 안에 error 코드를 선언해주면 계속해서 onChange 되는 값을 비교하여 에러메세지를 띄울 수 있다.

실제 프로젝트에서 실시간 유효성 검사를 할 수 있는 mode가 있다는 사실을 모르고, useForm 을 직접 custom하여 활용하려고 코드를 고치고 있었는데, mode에 관련 된 부분도 조금 더 찾아보고 반영하여 코드를 작성하면 좋을 것 같다.

 

 

 


위에서 활용된 코드들 상당수가 react-hook form 공식문서에서 가지고 온 코드들이다. setErrors만 봐도 하나의 error가 아닌 여러개의 error들을 출력할 때는 또 다른 방법이 있는데, 그 코드 또한 공식문서에 굉장히 잘 나와져 있다.
이 외에 더 다양한 이 라이브러리 기능들이나 더 완벽한 이해를 바란다면 공식문서를 읽어보는것을 추천한다.

 

+ Recent posts