[최종 프로젝트] 연도, 월 selectbox 컴포넌트 만들기

내가 담당하게 된 탄소계산기 부분에는 연도와 달을 선택할 수 있는 셀렉박스가 존재한다.
디자이너분과 소통을 통해 알게 되었는데, 기존 우리가 알던 년도 따로 달 따로 존재한다기보단 버튼을 누르면 연도와 달의 리스트가 같이 나오고 연도와 달 모두 선택했을 시에 리스트가 닫힌다고 한다.

다른 페이지에서도 사용이 될 예정이라 처음부터 컴포넌트화 시켜서 작업을 하기로 했다.
1. 컴포넌트 사용
const [thisYear, setThisYear] = useState<number | null>(currentYear);
const [thisMonth, setThisMonth] = useState<number | null>(currentMonth);
const handleYearChange = (year: number) => {
setThisYear(year);
};
const handleMonthChange = (month: number) => {
setThisMonth(month);
};
<YearMonthPicker
thisYear={thisYear} // 현재 시점 년도
thisMonth={thisMonth} // 현재 시점 달
onChangeYear={handleYearChange} // 연도 변경 핸들러 전달
onChangeMonth={handleMonthChange} // 월 변경 핸들러 전달
onCloseDropdown={handleCloseDropdown} // 드롭다운 닫힘 시 호출될 함수
disabled={false} // selectbox list 제공 유무
/>
컴포넌트에는 props로 여러 값들을 전달하게 된다.
위의 내용들은 props로 내려주는 코드를 일부 빼와서 작성을 한 내용이다. (원래 useEffect안에서 호출 시 변경되는 로직들이 있음)
2. 컴포넌트 설정
const YearMonthPicker: React.FC<YearSelectProps> = ({
thisYear,
thisMonth,
onChangeYear,
onChangeMonth,
onCloseDropdown,
disabled = false
}) => {
const [selectedYear, setSelectedYear] = useState<number | null>(thisYear);
const [selectedMonth, setSelectedMonth] = useState<number | null>(thisMonth);
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
...
props로 받은 내용들 중 default로 현재 년도와 달을 주고 싶어 설정을 해두었다.
그리고 드롭다운 리스트가 열리고 닫힐 state도 함께 설정했다.
2-1 컴포넌트에서 핸들러 설정
const toggleDropdown = () => {
if (!disabled) {
setIsDropdownOpen((prev) => !prev);
}
};
const handleYearClick = (year: number) => {
if (!disabled) {
setSelectedYear(year);
onChangeYear(year); // 연도 변경 핸들러 호출
onCloseDropdown(); // 드롭다운 닫힘 시 호출
}
};
const handleMonthClick = (month: number) => {
if (!disabled) {
setSelectedMonth(month);
onChangeMonth(month); // 월 변경 핸들러 호출
onCloseDropdown(); // 드롭다운 닫힘 시 호출
}
if (selectedYear) {
setIsDropdownOpen(false); // 연도와 월이 모두 선택되었을 때 닫힘
}
};
직접 토글 버튼들을 수정할 수 있는 핸들러들을 작업해줬다.
클릭 시, 연도, 월 변경 및 드롭다운 닫힘과 같은 기능에 대한 코드들이다.
그리고 생각보다 복잡했던 로직이었던 드롭다운 리스트를 구현한 방법을 적어보려 한다.
<button onClick={toggleDropdown}>
{selectedYear && selectedMonth
? `${selectedYear}년 ${String(selectedMonth).padStart(2, "0")}월`
: `${selectedYear}년 ${String(selectedMonth).padStart(2, "0")}월`}
</button>
위에 state로 관리하던 년도와 달을 적어줬다.
그리고 월이 한 자릿수인 경우를 대비에 padStart 메서드를 통해 앞에 "0"을 추가해 줬다.
연도 선택 드롭박스
{isDropdownOpen && (
<div className="flex flex-row gap-4">
<div className="year-list">
<p>연도 선택</p>
{Array.from({ length: currentYear - 2020 + 1 }, (_, i) => (
<div
key={2020 + i}
onClick={() => handleYearClick(2020 + i)}
className={`dropdown-item ${
selectedYear === 2020 + i ? "selected" : ""
}`}
>
{2020 + i}년
</div>
))}
</div>
...
월 설정 로직
현재 연도(currentYear)부터 2020년도까지의 연도를 동적으로 생성하기 위해 `Array.from({ length: currentYear - 2020 + 1 }, (_, i) =>...)`를 사용
`{ length: currentYear - 2020 + 1 }`
length 속성만을 가진 객체로, 배열의 길이를 정의함.
`currentYear`가 2024라면 length는 `2024 - 2020 + 1 = 5`가 됨
`(_, i) =>...`
배열의 현재 값으로, 여기서는 사용하지 않기 때문에 `_`로 적음
2020부터 currentYear까지의 모든 연도를 포함하는 배열을 생성
조금 더 예제를 통해 정리하자면 아래와 같다.
const currentYear = 2024;
const years = Array.from({ length: currentYear - 2020 + 1 }, (_, i) => 2020 + i);
console.log(years); // [2020, 2021, 2022, 2023, 2024]
`onClick={() => handleYearClick(2020 + i)}`
연도 아이템을 클릭했을 때 호출되는 이벤트 핸들러이다. 2022년을 클릭하면 handleYearClick(2022)가 호출이 되도록 구현.