폼에서 생년월일에 대한 정보를 받아야 한다. 폼은 react-hook-form을 이용해 구현하였고 폼 중에 생년월일 입력 필드를 클릭하면 달력에서 연월일을 지정할 수 있도록 만들 것이다. 달력을 직접 구현하기에는 시간이 오래 걸릴 것 같아 react-datepicker를 사용하기로 했다.
React-datepicker란 ?
커스터마이징이 가능한 달력을 구현할 수 있는 React 라이브러리이다.
자세한 설명은 공식문서에 잘 나와있다.
공식문서 : https://reactdatepicker.com/
date-fns 설치하기
date-fns는 로케일 설정 및 날짜 포맷팅을 위해 사용되는 라이브러리이다.
react-datepicker는 기본적으로 영어로 달력의 내용을 표시해주기 때문에 이를 한글로 설정하기 위해 date-fns를 설치하였다.
공식문서 : https://date-fns.org/
타입스크립트를 함께 사용하였고 이전 버전에서는 @types/react-datepicker를 설치해줘야 했는데 최신 버전에서는 설치하지 않아도 된다.
react-datepicker 사용하기
import DatePicker from 'react-datepicker'
import {useState} from 'react'
import 'react-datepicker/dist/react-datepicker.css'; //datepicker 기본 스타일파일
export default function Calendar() {
const [selectedDate, setSelectedDate] = useState<Date | null>(null);
return (
<DatePicker
dateFormat='yyyy.MM.dd' // 날짜 포맷
selected={selectedDate} //selectedDate에 저장된 날짜 값을 표시
onChange={(date) => setSelectedDate(date)} //날짜 값 변경
/>
}
위와 같이 가장 기본적인 설정만 한 달력을 만들 수 있다.
react-datepicker 달력 커스터마이징 하기
조금 더 멋있는 달력을 만들기 위해 커스터마이징을 해보자.
가장 먼저 영어로 된 달력을 한글로 바꾸기 위해 다음과 설정한다.
import {ko} from 'date-fns/locale'
처음에 설치한 date-fns를 통해 한글로 바꿔주었다.
const handleDateChnage = (Date | null ) => {
setDate(date)
onChange(date)
} //변경된 date값 저장 함수
<DatePicker
locale={ko} //date-fns/locale 적용
dateFormat="yyyy-MM-dd" //데이터 포맷팅 설정
shouldCloseOnSelect //날짜를 선택하고 나면 자동으로 달력 닫기
maxDate={new Date()} //현재 날짜를 기준으로 이후의 날짜 선택 x
placeholderText="YYYY-MM-DD" //placeholder
selected={date} //날짜 선택 값
onChange={handleDateChange} //date값 변경
renderCustomHeader={({
date,
changeYear,
changeMonth,
decreaseMonth,
increaseMonth,
prevMonthButtonDisabled, //이전달 이동이 불가하면 disabled
nextMonthButtonDisabled, //다음달 이동이 불가하면 disabled
} //rednderCustomHeader는 달력 상단 부분의 header 커스텀
/>
위와 같이 DatePicker에 설정을 추가해주었다.
달력에서 선택한 날짜가 onChage를 통해 date에 값이 저장되어 렌더링된다.
나는 사용자의 편의성을 위해서 년도와 월을 옵션을 통해 고를 수 있게 수정할 것이다.
import { useState } from 'react';
import DatePicker from 'react-datepicker';
import 'react-datepicker/dist/react-datepicker.css';
import { ko } from 'date-fns/locale';
import * as S from './style';
export default function Calendar() {
const [date, setDate] = useState<Date | null>(selectedDate);
const handleDateChange = (date: Date | null) => {
setDate(date);
onChange(date);
};
return (
<S.CalendarContainer>
<DatePicker
locale={ko}
dateFormat="yyyy-MM-dd"
shouldCloseOnSelect
maxDate={new Date()}
placeholderText="YYYY-MM-DD"
selected={date}
onChange={handleDateChange}
renderCustomHeader={({
date,
changeYear,
changeMonth,
decreaseMonth,
increaseMonth,
prevMonthButtonDisabled,
nextMonthButtonDisabled,
}) => (
<div>
// month 감소 버튼
<button onClick={decreaseMonth} disabled={prevMonthButtonDisabled}>
<
</button>
<span>
//year값 출력
<select
value={date.getFullYear()}
onChange={({ target: { value } }) =>
changeYear(parseInt(value))
}
>
//year 설정(현재 년도로 부터 100개 1940~2024)
{Array.from(
{ length: new Date().getFullYear() - 1940 + 1 },
(_, i) => {
const year = 1940 + i;
return (
<option key={year} value={year}>
{year}년
</option>
);
}
)}
</select>
//month값 출력
<select
value={date.getMonth()}
onChange={({ target: { value } }) =>
changeMonth(parseInt(value))
}
>
//month 설정(1~12월)
{Array.from({ length: 12 }, (_, i) => (
<option key={i} value={i}>
{i + 1}월
</option>
))}
</select>
</span>
// month 증가 버튼
<button onClick={increaseMonth} disabled={nextMonthButtonDisabled}>
>
</button>
</div>
)}
/>
</S.CalendarContainer>
);
}
생년월일을 받기 위해 현재 년도로 부터 100개, 즉 1940년~2024년까지 고를 수 있게 옵션 값들을 넣어주었고 1~12월까지 12개의 달의 옵션을 만들었다.
< 와 > 버튼을 통해 month의 증가와 감소를 바꿀 수 있으며 renderCustomHeader의 changeYear, changeMonth,의 두 옵션으로 화살표를 눌러서 일일이 바꿀수 있게 하는 것 뿐만 아니라 년도와 달을 클릭하여 드롭다운을 통해 직접 선택할 수 있게 만들었다.
위의 GIF는 import 'react-datepicker/dist/react-datepicker.css'; 를 통해 불러온 기본 스타일이 적용되어 있는 상태이기 때문에
CSS를 통해 디자인을 바꿔보자
CSS 적용하기
CSS를 적용할 때 시간이 많이 걸렸던 것 같다. datepicker 라이브러리의 클래스 이름값을 가져와서 일일이 수정해줘야 하기 때문에
내가 선택한 방법은 개발자 도구에서 변경할 부분의 요소들을 하나씩 찍어보는 방법이였다.
이런식으로 className값을 하나씩 찾아보고 수정하였다.
import { styled } from 'styled-components';
export const CalendarContainer = styled.div`
width: 122px;
position: relative;
.react-datepicker__header {
border-radius: 20px 20px 0 0;
background-color: #000000;
}
.react-datepicker {
background-color: #ffffff;
border-color: #000000;
border-radius: 20px;
}
.react-datepicker__header .react-datepicker__current-month {
color: #ffffff;
}
.react-datepicker__header .react-datepicker__day-name {
color: #ffffff;
}
.react-datepicker__day--weekend {
color: #ff0000;
}
.react-datepicker__day--disabled.react-datepicker__day--weekend {
color: #ccc;
}
.react-datepicker__input-container input {
width: 20.125rem;
height: 2.125rem;
border: none;
border-bottom: 1px solid #ededed;
padding: 0.5rem;
font-size: 0.75rem;
line-height: 1.125rem;
color: #000000;
&:focus {
outline: none;
border: 1px solid #000000;
}
}
.react-datepicker__triangle {
display: none;
}
.react-datepicker__month-dropdown-container,
.react-datepicker__year-dropdown-container {
select {
background-color: #ffffff;
border: 1px solid #ddd;
border-radius: 8px;
padding: 4px;
font-size: 1rem;
color: #333;
cursor: pointer;
appearance: none;
}
select::-ms-expand {
display: none;
}
}
`;
나는 나의 취향에 맞게 위와 같이 CSS를 수정하였다.
완성
최종 완성본이며 선택한 날짜가 필드에 잘 출력되고 있다.
react-datepickd 라이브러리의 class 이름들이 명확하기 때문에 각자 취향에 맞게 수정을 하면 될 것이다.
'React' 카테고리의 다른 글
Transient Props를 사용해 styled-component 에러 해결하기 (0) | 2024.09.28 |
---|---|
React + MSW(Mock Service Worker)로 백엔드 API 없이 개발하기 (0) | 2024.09.24 |
[React] 제어 컴포넌트와 비제어 컴포넌트 (2) | 2024.09.11 |
CRA없이 Webpack(웹팩) + Babel(바벨) 기본 설정하기 (0) | 2024.07.29 |
recoil을 통한 전역상태관리 (0) | 2023.11.29 |