💡 주제 : 폼 생성 및 입력 서비스
🗓 기간 : 03.16 ~ 03.19
🔨기술 스택 : React, Typescript, styled-components, Antd
💻 담당 : 개발환경 구축 및 폼 생성 페이지 구현
👤 참여 인원 : 3
프로젝트 상세 설명
사용자가 원하는 형식으로 폼을 생성하고 데이터를 입력받을 수 있는 폼 서비스로, 폼 생성 시에는 제목 및 필드 목록을 작성하게 되고 필드 목록은 추가/삭제 및 순서 변경이 가능하다. 각 필드 목록은 타입(텍스트, 전화번호, 주소, 드롭다운, 첨부파일, 이용약관) 및 필수 입력 여부를 선택할 수 있고 라벨 및 플레이스 홀더, 컨텐츠를 입력할 수 있다. 폼 열기를 통해 미리 생성된 폼을 확인할 수 있으며 저장할 경우 생성된 폼 목록에서 확인할 수 있다. 또한 생성된 폼은 사용자들이 작성할 수 있으며 사용자들이 작성한 내용을 확인할 수도 있다.
이번 과제는 과제가 2개로 첫번째 과제는 구현해야 하는 기능이 많고, 2번째 과제는 이전에 성공적으로 구현한 팀이 없어서 두 과제를 모두 진행하기가 어려워 보였다. 그러나 첫 번째 과제 역할 분담 중에 2번 과제를 하고 싶은 팀원분들이 계셔서 두 과제 모두 구현해보는 쪽으로 도전해보게되었다. 결과적으로 3명이서 1번 과제를 맡게 되었고, 나는 폼 생성 페이지를 맡았다. 폼 생성 페이지는 구글폼 만들기 처럼 폼을 만드는 페이지로 여러가지 컴포넌트 요소가 필요해보였다.
우선 기술 스택을 정했고 가장 익숙한 react, styled-components, context API
를 사용하기로 했다. 여기에다 마지막 과제라서 Typescript
를 사용해보고 싶다는 팀원의 의견에 찬성하여 Typescript도 처음으로 사용해보기로 했다. 또한 빠른 시간 내에 구현하기 위해 디자인 라이브러리를 사용해보기로 했고 팀원분께서 Ant design(Antd)을 사용해본 경험이 있으셨는데 사용하기 편했다는 의견을 주셔서 이번 기회에 Antd
를 사용해보게 되었다.
Typescript를 도입하기로 결정한 것은 이미 기본적인 개발 환경 구축이 끝난 상태였어서 docs를 찾아보며 Typescript에 필요한 설정들을 추가해주었다.(완벽하게 설정한 것인지 모르겠다..)
이번 개발 환경 구축은 내가 맡아서 진행해보았는데 설정 파일을 작성하는 부분들은 아직 완전히 이해하지 못한 상태로 작성한 부분들이 있는 것 같아 나중에 각 옵션들의 용도나 의미를 정리해봐야 할 것 같다.
Typescript 설정 외에도 eslint, prettier, husky 등의 설정을 맡아서 진행하였고, Typescript 도입에 따라 변경되어야 하는 사항들을 수정해주었다.
팀원들과 회의를 진행하면서 기본적인 구조는 만들어놓고 시작하면 팀원들이 개발하기 수월할 것 같아 styled-components theme provider나 react-router-dom 정도를 미리 적용해서 PR을 올렸다.
이렇게 기본적인 설정 및 코드 작성을 마치고 본격적으로 내가 맡은 기능을 구현해보기 시작했다.
폼 생성 페이지에서 구현해야 하는 기능은 다음과 같다.
기능과 ui를 분석하면서 필드에 필요한 컴포넌트들을 분리하여 구현해야겠다는 생각이 들었고, 최대한 컴포넌트를 분리해서 구현했다.
폼 생성 페이지의 필드 영역은 다양한 input 요소들로 구성되어있고, 폼 type 선택에 따라 동적으로 변화해야 하기 때문에 필드 영역도 최대한 컴포넌트를 분리하여 구현하려고 했다. 필드 영역인 <Field/>
컴포넌트를 만들고 내부에 <TextEditor>,<FieldTools>
컴포넌트를 넣어주었으며 <FieldTools>
컴포넌트 내부에는 <ChedkBox/>,<SelectType/>,<OptionList/>
컴포넌트들을 넣어주었다. 이 구조는 추후에 Context API 적용 및 드래그 기능을 구현하면서 <Field/>
컴포넌트 내부에 필드 데이터와 관련된 컴포넌트들을 감싸는<FieldDatas/>
컴포넌트를 두어 이 내부에 기존 <TextEditor>,<FieldTools>
를 넣어주는 구조로 변경되었다.
폼 생성 페이지 내부에서 공유할 Context를 생성해 주었다. Context는 필드 하나를 관리하는 FieldContext
와 필드 목록 및 제목, 폼 id를 관리하는 FormDataContext
를 만들었으며 이전 프로젝트에서 redux 사용시 action, reducer, type
등을 나누어서 관리했던 것이 편리했던 기억이 있어 이와 비슷한 구조로 Context를 구성하였다. 이렇게 생성한 Context 들은 Field내의 항목들의 값이 변경되었을 때 FieldContext
의 state를 수정하고 FieldContext가 수정된 것을 감지하여 FormDataContext
의 state를 수정하도록 만들었다. 또한 생성된 폼 목록을 띄우고 관리하기 위해 FormListContext
도 구현하였다.
필드 항목에는 다양한 기능들이 있지만 그 중에서 처음 도전해보았던 텍스트 에디터와 드래그를 통한 순서이동에 대해 구현 방법을 정리하였다.
텍스트 에디터
텍스트 에디터는 위지윅 에디터를 사용하여 구현하라는 안내가 있어 관련해서 검색을 해보았고 Quill
이라는 에디터를 사용하였다. 우선 지원하는 기능들이나 UI가 기업의 참고 이미지의 에디터와 유사하여 좋았고, 사용해본 유저들이 많아 선택하게 되었다. 사용 시에는 필요한 기능들을 배열로 나열하여 전달해주기만 하면 되어서 사용이 간편했다.
드래그를 통한 순서 이동
javascript 개발 시에 draggable 라이브러리를 사용해 본 경험이 있으나 직접 드래그 이벤트를 구현해본 것은 처음이었다. 요구사항이 복잡하지 않았고, 라이브러리를 사용함으로써 기존 구현해놓은 컴포넌트들의 구조가 변경될 가능성이 있어 직접 구현하게 되었으며 drag
를 시작한 element와 dragOver
이벤트가 발생된 element의 index
를 저장하여 dragEnd
이벤트가 발생되었을 때 해당 index들을 통해 drag한 element를 over된 element의 index로 변경하여 배열을 업데이트 해주었다.
최종 구현된 폼 생성 화면은 다음과 같다.
Typescript를 이번에 처음 사용해보았는데, 오류 내용과 해결방법에 대해 정리한 내용이 notion 페이지에 가득 차지할 정도로 수많은 오류들을 맞닥뜨리면서 별다른 생각없이 사용해오던 변수, 파라미터들의 type을 알게되었고 얼마나 type에 대한 개념이 부족했는지 느끼게 되었다. 하지만 다양한 오류를 만날 때마다 검색을 거듭했고, 오류들을 하나씩 해결해나가면서 type에 대한 이해도 향상되었다. 마침내 개발 중반 이후 부터는 오류가 나더라도 Type을 바로바로 수정할 수 있게 되었고, 다른 팀원분들에게도 오류의 원인과 해결방법에 대해 설명할 수 있게 되었다. 처음에는 Typescript를 사용한 것을 후회하기도 했지만 사용해보니 Code에 대한 이해도도 올라가고 잘못된 값에 대한 방지가 바로 바로 가능한 것이 장점으로 느껴졌다. 특히 Context를 생성하면서 TypeScript 오류를 많이 보았었는데 해결 과정을 통해서 Context를 생성하기 위해서 어떤 타입의 값들이 필요한지, 전달해주어야 하는지 알게되었으며 TypeScript를 사용하지 않았더라면 이해하지 못했을 부분이기 떄문에 이 점에서 많은 도움이 되었으며 개인 프로젝트를 진행하더라도 꼭 다시 사용하겠다고 다짐했다.
컴포넌트가 리렌더링 되면서 하위 컴포넌트들이 불필요하게 리렌더링 되는 부분을 최적화하였다. 이전에 나였다면 어디서 리렌더링이 불필요하게 발생하는지도 파악하지 못했을 것이며 어떤 방식으로 처리해야하는지 검색이 필요했을텐데 이번에는 "이 부분에서 불필요하게 리렌더링이 일어날 것 같은데?" 라고 생각되는 부분에 log를 남겨 확인해보면 실제로 불필요한 리렌더링이 발생하고 있는 것을 확인할 수 있었고, 이를 해결하기 위해 useCallback, useMemo, memo
등을 다양하게 사용하면서 리렌더링을 최적화하였습니다.
react-quill 설치 시 npm ERR! code ERESOLVE 오류
npm install 뒤에 '--save --legacy-peer-deps` 를 추가해주면 된다.
WYSIWYG(위지윅) 이란
What You See Is What You Get 의 약자로 '보이는 그대로 얻는다' 라는 의미, 즉 문서 편집 과정 도중 포맷 된 형태의 텍스트가 그대로 출력되는 것을 말한다.
Context API를 Redux처럼 action,reducer를 나누어 구현할 수 있다는 것을 알게되었다.
Typescript를 통해 평소에 사용하던 훅의 매개변수 타입을 알 수 있었고, 상태 관리를 위해 actions, reducers를 구현하면서 type을 신경쓰다보니 구조 이해에 많은 도움이 되었다.
다양한 Typescript 오류 해결 방법을 알게되었다. 이에 대해서는 별도로 정리를 해보려고 한다.