재사용 컴포넌트를 만들 때 고려사항
아래 내용들은 어떤 레퍼런스가 아닌, 공부하면서 이런 식으로 메뉴얼을 구성하여 컴포넌트를 구성할 때 좀 더 주의를 기울였으면 좋겠다고 생각한 것들을 적은 것이니(뇌피셜), 필요 없다면 넘어가셔도 좋은 내용입니다.
- 각 컴포넌트가 하나의 목적만 이룰 수 있도록, 컴포넌트가 책임질 바운더리를 구성 (함수를 작성하는 것과 동일)
- 컴포넌트를 재사용(동적으로 사용)하기 위하여 재사용할 범위를 prop으로 구성
- title를 동적으로 구성할 거면 title을 prop으로 받고, color를 동적으로 구성할 거면 color를 prop으로 받는 방식
- 스타일을 바꾸기 위하여 꼭 해당 style을 prop으로 받아야 할 필요는 없음 → error 프롭이 true일 경우, 빨간색을 나타내도록 할 수도 있다. 이 경우, style 자체가 error라는 속성에 종속되므로 style을 완벽하게 재사용하지 않는다면, 해당 style 속성을 프롭으로 받을 필요는 없다.
- 상속 대신 합성 모델 사용하기.
- 합성 모델의 대표적인 prop인 children prop을 사용할 수 있다
- children 프롭은 범용적인 박스 역할을 하는 layout prop이나, 어떠한 엘리먼트들이 들어오는지 정확하게 알 수 없는 경우 사용할 수 있다.
- 또한, children을 가공하기 위한 특별한 역할이 필요할 때, children프롭을 사용할 수 있다.
- JSX.Element를 받을 때에는 children, 데이터를 받을 때에는 prop을 이용하면 될 듯?
- 객체 지향 프로그래밍의 개념인 구현과 API를 분리하는 방법은 컴포넌트에서 동일하게 적용된다.
- 예를 들어, 드래그 하여 파일을 추가할 수 있는 컴포넌트를 만든다고 가정
- 구현 영역: 드래그 이벤트 감지 등 사이드 이펙트 처리, 드래그 상태 정의
- API 영역: 드래그가 발생했을 때 처리해야 할 이벤트(재사용하기 위해 프롭으로 전달)
- 드래그 로직이 길어지거나, 재사용 가능성이 있다면 hooks를 이용하여 분리하기.
- 리액트 컴포넌트는 함수로 구성되어 있어 쉽게 재구성이 가능하기 때문에, 처음부터 완벽한 컴포넌트를 만들기 위한 부담감을 버리자.
- 페이지부터 만들고, 잘 동작하고 재사용할 만한 것들을 컴포넌트로 만들어보자.
- 이 프롭이 필요하다 싶으면 나중에 추가해보자
- 이 과정이 익숙해지면 컴포넌트를 만드는 과정이 점점 빨라질 것이다.
오늘 배운 것들
- Avatar 및 AvatarGroup 컴포넌트
- Slider 컴포넌트
- Progress 컴포넌트
- Skeleton 컴포넌트
- Input 컴포넌트
- Select 컴포넌트
Avatar 및 AvatarGroup 컴포넌트
- 사용자가 등록한 사진을 보여주는 Avatar 프롭이다.
- 재사용성을 높이기 위하여
alt, src, size, shape, placeholder
프롭을, lazy loading을 위한 lazy, placeholder
프롭을 넣어주었다.
- 이 placeholder는 이미지가 로딩중일 때 보여주는 placeholder 목적으로 구현했으나, 만약 사용자가 등록한 프로필 사진이 없을 때 이 placeholder를 보여주고, 외부 wrapper에서 로딩 중일 때 placeholder 컴포넌트를 자체적으로 보여줘도 될 듯 하다.
Slider 컴포넌트
- 볼륨 조절처럼 마우스를 클릭하여 옮길 경우 핸들이 이동하는 컴포넌트이다.
- 재사용성을 높이기 위해 step, min, max와 움직이면서 실행할 onChange 콜백 함수를 프롭으로 받도록 하였다.
- 사용자 경험이 향상되도록 mousedown은 sliderRef 컴포넌트에 걸어주었고, mousemove와 mouseup 이벤트는 document 전체에 이벤트 리스너를 감지하도록 하였다.
- 근데 최적화가 안되있는지, 빠르게 왔다 갔다 할 경우 원활하게 움직이지 않았고, 마우스를 손에서 땠음에도 불구하고 핸들이 따라 움직이는 현상이 있었다. 조금 더 고민해봐야겠다.
Progress 컴포넌트
- 만드는 방법은 Slider와 비슷하다. Container와 rail, track으로 이루어져있다. 이 컴포넌트들은 서로 종속적이므로 하나의 컴포넌트로 구성해도 무방하다. 스타일을 입히기 위하여 각각 styled component로 구성하였다.
- 애니메이션을 입히고, transition을 이용하여 width가 변할 때 부드럽게 이동하도록 하였다. width를 0으로 잡고, value prop을 이용하여 퍼센트를 동적으로 구현하도록 하였다.
Skeleton 컴포넌트
- 스켈레톤은 요즘 유튜브 로딩시나, velog에서 글을 불러오는 중, 빈 화면이나 스피너 대신 이 자리에 컨텐츠가 존재한다고 알려주는 컴포넌트이다.
- 가장 기초가 되는
Base
컴포넌트에 애니메이션과 색상, border-radius의 값을 정의하고, 두 가지 shape로 구성하여(Box, Circle) Base를 상속하는 방식을 사용하였다.
- 현재는 index.tsx에서 Skeleton을 묶어서 Box, Circle컴포넌트를 export하는데, 차라리 동적 태그로 구성하면 좀 더 깔끔하게 구성할 수 있을것 같다는 생각을 했다.
- paragraph 스켈레토는 Box 컴포넌트를 이용하여 map으로 구성하고 있다. 유튜브나 velog처럼 하나의 줄에서 여러 조각으로 나누어서 표현하는 방식도 조금 더 생각하면 구현할 수 있을 것 같다.
- input 컴포넌트는 일반적으로 재사용성을 높이기 위해 Wrapper와 함께 사용한다. 또한, Input 컴포넌트의 width를 100%로 지정해놓고 wrapper의 너비를 조정하는 방법으로 너비를 조정한다.
- input의 재사용성을 높이기 위하여 (disabled, required, readOnly, inValid) 등의 프롭들을 받도록하였고, input 자체는 로직을 처리할만한 것이 없기 때문에 별도로 구현 영역은 구현하지 않았다.
- 현재 onChange를 프롭으로 받도록 안했는데, 실제로 컴포넌트를 사용하기 위해서 이를 프롭으로 받도록 하는 것이 좋겠다.
Select 컴포넌트
- Input 컴포넌트처럼 Select 역시 재사용성을 고려하기 위하여 block, inline-block을 결정하는 wrapper를 만들고, Select 컴포넌트 자체는 width를 100%로 고정하여 구성한다.
- option을 데이터로 입력 받고, option 컴포넌트를 내부에서 처리하도록 하였다. Avatar와 AvatarGroup과 달리 데이터를 프롭으로 받아서 처리하는 이유는 다음과 같다고 생각한다.
- data로 처리하는 것이 더 적합(AvatarGroup과 달리 View적 요소가 없고 오직 데이터만 존재)
- Select 자체가 어떤 컴포넌트를 감싸기 위한 wrapper가 아님
- 흥미로웠던 것은, placeholder를 프롭으로 입력받아 아무것도 선택하지 않았을 때에는, placeholder가 보이게하고, placeholder 옵션에 hidden 속성을 주어 어떤 것을 하나라도 선택하면 select에서 보이지 않게 하는 것이었다.
- 또한, placeholder의 값이 selected 값으로 선택되지 않도록 잘 걸러주는 작업을 해야 한다. select 값이 변할 때 핸들러 onChange를 프롭으로 받아서 선택된 데이터를 받을 수 있다.
느낀 점
- Vue가 끝나고 리액트를 시작하니, 이전에 리액트는 해봤지!하고 만만하게 접근했는데, Vue가 체감상 훨씬 쉬운 것 같다… Vue는 프레임워크고 리액트는 라이브러리라 그런지 너무나 자유도가 높아서 오히려 더 체계 잡는 것이 어려운 것 같다.
- 그래도 처음에는 막막했는데, 계속 실습을 진행하고, 생각의 흐름을 정리고 기록해나가면서 어느정도 틀이 잡히기 시작한 것 같다. 그래도 커스텀 hooks 같은 부분은 좀 더 연습이 필요할 듯 하다.
- 기록은 언제나 도움이 된다. 미래의 나를 위해서 조금 더 고생하는 습관을 들이자!
출처