-
React DatePicker 라이브러리를 사용해 날짜 선택하기Today I Learned 2022. 11. 24. 23:42
운동 모집 게시글을 작성하는 폼의 프론트엔드 영역 구현을 진행했다.
날짜 입력란을 클릭하면 달력이 나오고, 달력의 날짜를 선택하면 날짜가 선택된다.
시간 입력창은 시작 시간과 분, 종료 시간과 분을 드롭다운에서 선택할 수 있다.
게시물을 구성하는 게시글 객체와 게임 객체가 생성되기 위해 필요한 데이터를 사용자가 입력해 작성하기 버튼을 누르면, 서버에 POST 요청을 보내는 API까지 구현한 상태이다.
게시글 작성하기 기능을 구성하는 프론트엔드 레이어 구조는 다음과 같다.
- UI: PostForm, SelectTime
- Page: PostFormPage
- Store: PostFormStore
- Infrastructure: PostApiService
게시물 작성 폼을 만들면서 가장 신경썼던 부분은 날짜와 시간에 관련된 부분을 어떻게 백엔드에 전달할 것인가를 결정하는 것이었다. 게시물 목록이나 상세 내용 조회만을 해올 때까지는 운동 날짜에 대한 정보를 갖는 값 객체 ExerciseDate는 value라는 이름의 필드로 날짜와 시간을 하나의 문자열로 관리하고 있었다.
// GameDate.java @Embeddable public class GameDate { @Column(name = "date") private String value; // constructors // getter // equals, hashCode, toString }
가장 단순한 형태의 입력을 받는 것으로 기능 구현을 시작하고 있기는 했지만, 문자열로 날짜와 시간을 한 번에 직접 입력받게 한다면 정상적이지 않은 내용을 입력할 경우가 있을 수 있고, 이를 예외처리로 방지하기 위해서는 너무 많은 경우를 생각해야 할 것 같았다. 따라서 입력 폼에서부터 정해진 양식대로 데이터를 전달하게 할 수 있을까를 고민했고, 날짜와 시간을 나눠서 관리하는 쪽으로 모델을 수정하기로 결정했다.
그 전에 프론트엔드에서 먼저 날짜와 시간을 나눠서 받아올 수 있도록 동료들이 그간 많이 사용했던 DatePicker 라이브러리와 <select>, <option> 태그를 조합해 드랍다운을 만들어 사용해보기로 했다.
DatePicker 라이브러리
날짜를 정해진 값으로 부여하기 위해 DatePicker 라이브러리를 가져와 프로젝트 의존성에 추가했다.
npm i react-datepicker
// PostForm.jsx import 'react-datepicker/dist/react-datepicker.css'; import DatePicker from 'react-datepicker'; export default function PostForm({ data, changeGameDate, }) { const handleChangeGameDate = (date) => { changeGameDate(date); }; return ( <div> <label htmlFor="input-game-date"> 날짜를 선택하세요: </label> <DatePicker id="input-game-date" selected={data.gameDate} onChange={(date) => handleChangeGameDate(date)} dateFormat="yyyy년 MM월 dd일" /> </div> ); }
UI 테스트 코드를 작성하는 과정에서는 달력에서 날짜를 선택했을 때, 선택한 날짜를 상태로 갱신하는 changeGameDate를 호출하는지 테스트를 시도해보았다. onChange prop을 받고, 날짜를 선택했을 때 입력창에 출력되는 날짜 값의 dateFormat을 직접 지정해줄 수 있었기 때문에 입력창의 값을 변경하는 fireEvent.change 메서드를 이용해 테스트를 시도해보았다.
context('각 입력 폼에 내용을 입력하면', () => { it('입력되는 내용을 상태로 저장하는 핸들러 함수 호출', () => { fireEvent.change(screen.getByLabelText('날짜를 선택하세요:'), { target: { value: '2022년 11월 25일' }, }); expect(changeGameDate).toBeCalled(); }); });
변경된 날짜의 값을 가져와 저장하는 것을 확인할 수 있었다.
시간 선택하기
시간과 분을 1~12, 0~59 사이에서만 선택할 수 있도록 select와 option 태그를 조합해 드롭다운 작성을 시도했다. 처음에는 절대값을 이용해 option 여러 개를 늘어놓아 보았는데, 다음과 같이 option이 줄줄이 늘어지는 형태가 나왔다.
// SelectTime.jsx <div> <label htmlFor={id}> {type} {' '} {time} </label> <select id={id} onChange={onChange} > <option defaultValue disabled hidden > 선택 </option> <option>01</option> <option>02</option> <option>03</option> <option>04</option> <option>05</option> <option>06</option> // ... </select> </div>
반복을 줄이기 위해 리팩터링을 진행했다.
예를 들어 시간은 01부터 12까지의 숫자가 순차적으로 나타나는 식이기 때문에 map을 이용해 출력할 수 있을 것으로 보였다. 길이가 12인 배열을 생성하고 반복을 돌면서 index를 이용해 숫자값을 주는 식으로 코드를 수정했다.
export default function SelectTime({ id, onChange, type, time, }) { return ( <div> <label htmlFor={id}> {type} {' '} {time} </label> <select id={id} onChange={onChange} > <option defaultValue disabled hidden > 선택 </option> {Array(12).fill(0).map((_, index) => { const value = index + 1 < 10 ? `0${index + 1}` : (index + 1).toString(); return (( <option key={value}> {value} </option> )); }) )} </select> </div> ); }
리팩터링 이후에도 UI 컴포넌트가 의도한 대로 출력되는 것을 확인할 수 있었다. 다만 분을 선택하는 경우에는 01부터 59까지 숫자가 한번에 나타나는 과정에서 사용성에 불편함이 느껴졌기 때문에 이후 CSS 작업과 리팩터링 작업을 진행할 때 드롭다운 개수를 조정하거나, 다른 방식으로 분을 입력하게 해야 할 필요성이 느껴졌다.
- 작업 링크
https://github.com/hsjkdss228/smash-frontend/pull/30
'Today I Learned' 카테고리의 다른 글
에러 핸들링 작업 과정에서 마주친 리팩터링 신호 (0) 2022.11.26 테스트 코드가 아무리 많아도 진짜 필요한 것을 테스트하지 않는다면? (게시글 작성하기) (0) 2022.11.25 통과하면 안 되는 테스트인데 자꾸 통과하는 이유는... (await waitFor) (0) 2022.11.24 이제야 조금은 앱 같다 (0) 2022.11.22 미신청자/신청자/참가자/작성자 별 컴포넌트 구분하기 (0) 2022.11.20