
구글 폼을 클론 코딩한 프로젝트를 개발했다. 데브코스 4기 스터디원들과 함께 한 발표 스터디에서 react-hook-form 에 대한 소개를 진행했고, 직접 프로젝트에 적용해보고 싶어서 구글 설문 폼 클론 코딩을 선택했다.
클론 코딩을 진행하며 sjoleee 님의 프로젝트 를 많이 참고했다.
워낙 유명한 페이지를 클론 코딩한 것이라 많은 설명은 필요 없을 것 같다. 개발 위주로 프로젝트 소개를 진행하면 사실 상 기술스택과 기능소개 밖에 없을 듯 하다.
기술 스택은 다음과 같다.
React Typescriptreact-hook-formRedux/toolkitChakra UI & EmotionSCSS 기반의 Emotion 이 손에 잘 익은 상태라 빠르게 토이 프로젝트를 진행하기에 적절하다고 생각했다.2월부터 5월 초까지 개발을 진행했고 약 3개월이 걸렸다. react-hook-form 을 어떻게, 왜 써야하는지를 파악했다면 설계의 방향을 제대로 잡고 더 빠른 개발이 가능했을 것이란 생각이 든다.
자세한 기능은 README.md 에 작성해 놓았다.
배운 게 많은 프로젝트였다. 처음 사용하는 기술 스택이야 데브코스를 진행하며 겪은 팀 프로젝트에서도 많이 있었지만 팀원들의 도움 없이 내가 선택한 라이브러리와 프레임워크 사용법을 일일이 공부하며 적용하다 보니 배운 점이 많았다.
항상 프로젝트 하나를 끝내고 나면 많이 배웠다! 라고 생각 하게 되는데 이 점이 즐거우면서도 방대한 양의 공부에 눈 앞이 캄캄할 정도다. 아무튼 해당 프로젝트를 진행하며 고민하거나 겪은 에러에 대해 얘기해보겠다.
가장 시간을 많이 잡아먹은 요인 중 하나다. Redux 와 react-hook-form 의 역할 분배를 제대로 하지 못해서 "react-hook-form 의 용도가 뭐더라...?" 하는 스턴 상태가 오래 지속되었다.
- react-hook-form 을 쓰면 렌더링이 적어지는데 폼 생성 페이지와 폼 응답 페이지 둘 다에서 써야하는 것 아닌가?
- 그럼 useForm 은 대체 어디서 써야하지?
이를 끝내준 질문이 바로 react-hook-form이 언제 쓰이는가 였다.
여태 나는 react-hook-form 이 불필요한 input 의 렌더링을 방지하기 위해 쓰는 것으로 알고 있었다. 하지만 회원가입 양식이나 로그인 양식을 작성할 때 react-hook-form 에서 유효성 검사를 하고 input 의 값들을 가져오는데 쓰였던 걸 생각하자 클론 코딩에서도 비동기적으로 값을 가져오기 위해 써야겠다고 생각이 바뀌었다.
그래서 동기적으로 계속 값을 업데이트 해야하는 요소 (카드 제목, 카드 타입 등) 들은 Redux 로 관리하고, 비동기적으로 값을 업데이트 해야하는 요소 (사용자 응답) 은 react-hook-form 으로 관리해야한다는 결론에 다다랐다.
역할 분리를 하고 나니 기존에 설계했던 컴포넌트를 갈아엎어야했다. Redux 가 관리하는 컴포넌트와 react-hook-form 이 관리하는 컴포넌트의 역할을 적절하게 지정해주기 전까지 컴포넌트를 어떻게 설계해야할지 막막해서 많은 시간을 이 주제에 대해 고민하며 보냈는데 설계 방향이 잡히자마자 개발 진척이 빠르게 이뤄져서 놀랐다.
사실상 1번의 문제와 비슷하다. 페이지를 어떻게 나눌 것인지, 각 컴포넌트를 어떻게 관리할 것인지를 고민하는 게 이 프로젝트의 가장 큰 고민거리였다는 생각이 든다. 레이아웃으로 어떻게 나눠야할지는 이제 감이 오지만, 아직 컴포넌트에 얼마만큼 의존성을 주입하고 어떻게 관리할 것인가에 대한 견해는 부족하다. React Component 설계에 대한 여러 글을 더 참조해보고, 프로젝트에 어떻게 대입할 수 있을지 응용하는 능력을 길러야겠다.
사실 설계 부분에서 위해 위에서 언급했던 sjoleee 님의 프로젝트를 참고했고 많이 배울 수 있었다. 개인적으로 스타일링 컴포넌트와 일반 컴포넌트를 어떻게 분리하는가를 고민한 적 있었는데 S.Component 의 형태로 네이밍을 하는 것을 보고 감탄했다. 다른 마음에 드는 방법이 생기기 전까지 자주 애용할 거 같다.
대부분 기능 구현에서의 에러가 대부분이었고, 결국 해결한 기능은 아래에 있는 배운점 에서 서술하려 한다.
딱 하나 세팅에 있어서 헤맸던 부분이 있다. Emotion 라이브러리를 Typescript 초반에 잘 인식하지 못했다. styled 를 자동 import 해오지 않아서 이 문제로 골머리를 앓았는데 다행히 moduleResolution 의 값을 node 로 설정해주자 이 현상을 사라졌다. (적용하고 좀 지나서 해결이 되었던 거 같다.)
찾아보니 moduleResolution 이 파일을 해석하는 해상도를 결정해준다고 했다. 최근에 함께 스터디를 하는 팀원이 비슷한 문제로 고민했는데 이를 해결해줄 수 있어 뿌듯했다. 😀
처음으로 React.memo 를 적용해보았다. Redux 의 shallowEqual 을 쓰면 바로 아래에 있는 프로퍼티를 비교해줌으로써 불필요한 리렌더링을 막아준다고 하는데, 결국 최상단에서 카드들을 렌더링 하는 cards 가 업데이트 되면 <Card /> 컴포넌트들이 리렌더링 되므로 렌더링 최적화를 위해 <Card /> 컴포넌트에 React.memo 를 적용해서 같은 prop 을 가진 컴포넌트는 리렌더링이 발생하지 않도록 했다.
React.memo 의 용도를 이론으로만 알고 있다가 직접 써보니 앞으로 어떤 상황에 써야할지 대략 감이 잡혔다.
적용하기 전

적용한 후

직접 부딪히면서 배우다 보니 API 문서를 읽는 것 보다 사용 방법을 빠르게 익힐 수 있었다.
1. register 메서드와 Controller 의 분리
순수한 input 태그에는 register 메서드를 사용해 useForm 에 등록해주고, state 기반으로 동작하는 컴포넌트엔 Controller 를 적용해 비동기적으로 값을 업데이트 했다.
2. useForm 의 mode 프로퍼티
유효성 검사를 onBlur 시에 처리하고 싶은 경우 mode 프로퍼티의 값을 onBlur 로 주면 된다. 여기서 끝나는 것이 아니라, register 와 Controller 에 있는 onBlur 메서드를 onBlur 나 onChange 등에 적절히 삽입해주어야 유효성 검사가 제대로 일어난다.
const methods = useForm({ mode: 'onBlur '});
3. FormProvider 와 useFormContext
useForm 의 메서드를 일일이 prop 으로 전달하지 않도록 Context API 처럼 동작하는 프로바이더를 자체적으로 제공한다.
FormProvider 를 사용해 useForm 의 반환값을 하위 컴포넌트로 내려주고, 하위 컴포넌트에선 useFormContext 를 사용해 메서드를 뽑아 쓸 수 있다.
// Parent
const methods = useForm();
return (
<FormProvider {...methods} >
<Children />
</FormProvider>
)
// Children
const { register } = useFormContext();
4. 유효성 검사
유효성 검사를 하기 위해 다양한 반환값을 사용해볼 수 있었다. trigger formState getFieldState 등을 필요한 유효성 검사를 추가하려고 했다.
또 그 과정에서 reqruied 나 validate 등을 직접 설정해주며 value 값으로 어떤 값이 들어오는지를 파악해 볼 수 있었다.
react-hook-form 이 폼을 쉽게 다룰 수 있도록 도와주며 렌더링 측면에서 이익이 된다는 이점을 톡톡히 체험해볼 수 있었다. 다만 회원가입이나 로그인 폼 처럼 단순한 폼을 다루는 게 아니라서 도중에 많이 헤맸다. 그럼에도 react-hook-form 을 공부하겠다는 취지에 걸맞은 프로젝트였다고 생각한다.
프로젝트를 하는 중간에도 틈틈히 리팩토링을 진행하며 개발을 하긴 했지만 아직 설계가 미흡한 컴포넌트들이 남아있다. 이런 컴포넌트들의 설계를 다시 하거나 추가적으로 렌더링 최적화를 할 수 있는 곳은 없는지 더 살펴봐야 한다. 다만... 지금 내 앞으로 예약된 Next JS 공부도 급급해서 리팩토링을 언제 할진 미지수다. 🥹
그간 데브코스에서 진행하는 개인 과제 및 팀 프로젝트 과제는 종종 해왔지만 이번에 필요에 의해 단독으로 토이 프로젝트를 진행한 것은 처음이다. 팀 프로젝트도 모두가 합심하여 프로젝트를 버리는 경우도 있는데 개인 프로젝트는 나 한명 뿐이라 설계가 바로 잡히기 전까지는 프로젝트를 도중에 관둬야 하는 생각도 많이 들었다. 박찬욱 감독님이 작품을 버리고 싶을 수 있겠지만 가난하게라도 끝까지 키워야 한다고 했는데, 나도 이번 토이 프로젝트를 부유하진 않더라도 끝까지 해본 것에 의의를 두고 싶다.
그리고 곧 공부할 Next JS 로는 일기 프로젝트를 만들 예정이다. 이번 토이 프로젝트에서 얻은 교훈으로 더 설계에 집중해서 많은 것을 얻어갈 수 있었으면 좋겠다.