개발을 하다보면 잘 만들어진 라이브러리를 먼저 찾게 되는 것 같다. 직접 구현하는 것보다 잘 만들어진 라이브러리를 가져다 사용하는 편이 코드도 깔끔하고 생산성도 높아지기 때문에 적극 활용하는 편인데, 가끔은 직접 구현해야할 때가 있다.
이번 프로젝트를 진행하면서 datePicker 라이브러리를 사용하다가 아래 두가지 문제점에 직면했다.
1. 디자인 : 라이브러리 특성 상 커스터마이징의 한계를 느꼈다.
2. 렌더링 이슈 : 아래는 datePicker 로 구현한 모습인데, 날짜 위에 마우스를 올려 놓거나 드래그만 해도 리렌더링 되는 이슈가 있었다. 불필요한 렌더링이라는 생각이 들었고, 성능 문제가 발생할 수도 있다는 생각이 들었다.
첫번째, mouse hover 기능에 따른 리렌더링 발생처럼 보여서 별도로 설정할 수 있는 옵션이 있는지 홈페이지를 샅샅이 살펴봤는데, 별도의 옵션은 없었다.
두번째, Calendar 컴포넌트를 분리해서 React.memo 로 감싸주는 방식으로 최적화를 시도했으나, 여전히 리렌더링이 발생했다.
세번째, 혹시 datePicker 의 옵션에 따라 렌더링이 발생하는건가 싶어서 기본 Calendar 를 생성해서 리렌더링이 발생하는지 실험해봤는데, 기본 Calendar 에서도 리렌더링이 발생했다. 여기까지 해보니 오픈 소스를 살펴봐야겠다는 생각이 들었다.
const Calendar = () => {
const [startDate, setStartDate] = useState(new Date());
return (
<DatePicker selected={startDate} onChange={(date) => setStartDate(date)} />
);
};
네번째, 오픈소스를 살펴보다가 handleDayMouseEnter 함수를 발견했는데, 아마도 이 코드로 인해 달력에 마우스를 올려다 놓으면 리렌더링이 발생하는 것 같았다.
실제 코드에 onDayMouseEnter 함수를 적용시키고 나서 마우스를 올려놓으니 콘솔에 해당 날짜가 계속 찍혔다.
return (
<StyledStartTimePicker
dateFormat="yyyy-MM-dd aa h:mm "
minDate={new Date()}
maxDate={new Date(addTwoWeeks)}
selected={selectedDate}
onChange={changeDateHandler}
locale={ko}
filterDate={isOffDay}
showTimeSelect
minTime={setHours(setMinutes(new Date(), 0), 9)}
maxTime={setHours(setMinutes(new Date(), 0), 21)}
timeCaption="시간"
excludeTimes={booked}
filterTime={filterPassedTime}
onKeyDown={(e) => e.preventDefault()}
dateFormatCalendar="yyyy년 MM월"
onDayMouseEnter={(day) => console.log(day)}
/>
근데 onDayMouseEnter 기능을 사용하지 않아도 계속 렌더링 되는 것이 문제였다. 계속 찾아보다가 github issue 를 발견했는데 이미 누군가 나와 똑같은 이슈를 겪었고 글을 남겨놨다. 비교적 최근 글이었고, 아직 이 이슈에 대해서는 논의 중인 것 같진 않았다.
누군가 나와 똑같은 이슈를 겪었고 github issue 에 의견을 제시한 글을 발견했다.
https://github.com/Hacker0x01/react-datepicker/issues/4383
마지막, 오픈소스를 더 살펴보다가 onMouseEnter 함수를 계속 따라가다 보니 Day 라는 컴포넌트에서 omMouseEnter 이벤트 핸들러에 handleMouseEnter 라는 props 를 전달하여 마우스 이벤트가 발생할때마다 함수가 실행되고 있음을 알게됐다.
확인해보기 위해 해당 부분을 주석처리 해보고 다시 실행을 해보니 달력에 마우스를 올려놓아도 렌더링이 발생하지 않았다!
이 부분이 원인이었던 것이다!
render = () => (
<div
ref={this.dayEl}
className={this.getClassNames(this.props.day)}
onKeyDown={this.handleOnKeyDown}
onClick={this.handleClick}
// onMouseEnter={this.handleMouseEnter}
tabIndex={this.getTabIndex()}
aria-label={this.getAriaLabel()}
role="option"
title={this.getTitle()}
aria-disabled={this.isDisabled()}
aria-current={this.isCurrentDay() ? "date" : undefined}
aria-selected={this.isSelected() || this.isInRange()}
>
{this.renderDayContents()}
{this.getTitle() !== "" && (
<span className="overlay">{this.getTitle()}</span>
)}
</div>
);
여러 시도 끝에 소스코드를 직접 수정하기에는 코드를 이해하고 수정하고 반영하는데 시간과 노력이 많이 들 것으로 예상됐다. 그렇다고 라이브러리에서 이슈에 대해 논의가 있을때까지 기다리는 것도 의미가 없다는 생각이 들었다.
현업에서는 더 다양한 요구 사항이 있을텐데, 이를 충족하기 위해서는 라이브러리에 더는 의존할 수는 없을 것이라는 생각도 들었다.
따라서 라이브러리 특성 상 발생하는 커스터마이징의 어려움과 렌더링 이슈 등으로 인해서 직접 datePicker 와 timePicker 를 구현하기로 했다.
datePicker & timePicker 를 사용하면서 개선되었으면 했던 부분을 함께 develop 하기로 했다.