useCalendar hook 으로 Calendeok 리팩토링 해보기

D uuu·2024년 4월 18일
1

project

목록 보기
8/10

토스 Slash 22- Effective Component 지속 가능한 성장과 컴포넌트 강의를 보고 useCalendar 만들어보기

토스 Slash 영상을 보면서 내 프로젝트에도 적용해볼 수 있지 않을까 하는 부분이 여러 있었는데 이번에는 useCalendar 훅을 만들어서 한번 적용해보고자 한다.
토이 프로젝트로 Calendeok 이라는 달력을 만들었는데 라이브러리처럼 만드는 것을 목표로 했기 때문에 어떻게 하면 누구나 쉽게 가져다 쓸 수 있을까? 하는 고민을 많이 했었다.

마침 토스 영상에서 useCalendar 로 훅으로 만들어 calendar 를 구현한 샘플 코드를 보고 아 ! 이렇게 만들면 CSS 를 신경쓰지 않고 달력을 구현할 수 있겠구나 하는 생각이 들었고, 기존 코드를 리팩토링 해보기로 했다.

⭐️핵심

영상에도 나왔듯 핵심은 달력을 구성하는 데이터는 변하지 않지만 달력의 UI 는 언제든지 변경 될 수 있다 는 점이다.
달력을 구성하는데 필요한 데이터를 useCalendar hooks 에 위임해서 데이터와 UI 를 구분하고 오로지 데이터에만 집중해서 모듈화한다.
바로 이런 패턴을 Headless 라고 하는데, 한가지 문제에만 집중하기 때문에 더 많은 곳에서 사용할 수 있고 변경으로부터 격리시킬 수 있다.

기존에 라이브러리를 사용하면서 불편했던 커스텀 문제를 이를 통해 해결할 수 있겠구나 싶었다.
UI 라이브러리를 사용하면서 괜찮은 디자인을 찾아보기가 어렵고, 커스텀을 하려고 해도 막상 내가 원하는대로 커스텀 하는것도 상당히 어려웠는데,
useCalendar 를 사용해서 달력 데이터만 편하게 가져다 쓰고 스타일링은 각자 원하는대로 하면 좋을 것 같다는 생각이 들었다.

구현 과정 및 완성 코드 보기

우선 useCalendar 로 구현한 달력 완성본부터 보자면 아래와 같다.
약간의 CSS 를 추가해서 보기 좋게 만들었다.


토스 예시 코드에서는 useCalendar 로 headers, body, view 를 가져와 화면에 그렸는데
나는 조금 더 세분화했다.

prevController, nextController 👉 이전 월, 다음 월로 이동할 수 있는 함수

curMonth, curYear 👉 선택한 연도와 월

weeks 👉 요일을 나타내는 값

body 👉 달력의 날짜

토스 버전
const { headers, body, view } = useCalendar();
내가 만든 버전
const { prevController, nextController, curMonth, curYear, weeks, body } = useCalendar();

사용법

아래와 같이 useCalendar 로 값을 불러와서 사용하면 된다.

import dayjs from 'dayjs';
import useCalendar from './hooks/useCalendar';
import { useState } from 'react';

function App() {
    const { prevController, nextController, curMonth, curYear, weeks, body } = useCalendar();
    const [selected, setSelected] = useState<Date>(new Date());
    const selectedDate = dayjs(selected).format('YYYY-MM-DD');

    const handleDate = (date: Date) => {
        setSelected(date);
    };

    return (
        <>
            <div className="container">
                <div className="date">{selectedDate}</div>
                <div className="header">
                    <div>
                        {curYear}년 {curMonth + 1}월
                    </div>
                </div>

                <div className="controller">
                    <button onClick={prevController}>이전 달</button>
                    <button onClick={nextController}>다음 달</button>
                </div>
                <table className="table">
                    <thead>
                        <tr>
                            {weeks.ko.map((week, index) => (
                                <th key={index}>{week}</th>
                            ))}
                        </tr>
                    </thead>
                    <tbody className="cell">
                        {body.map((rows, index) => (
                            <tr key={index}>
                                {rows.map((row) => (
                                    <td key={row.value} className={`dateCell ${selected === row.date && 'isSelected'}`}>
                                        <button onClick={() => handleDate(row.date)}>{row.date.getDate()}</button>
                                        {dayjs().isSame(row.date, 'day') && <div>Today</div>}
                                    </td>
                                ))}
                            </tr>
                        ))}
                    </tbody>
                </table>
            </div>
        </>
    );
}

export default App;

후기

이전 코드는 달력 데이터와 UI 가 함께 작성되어 있어서 스타일 변경이 자유롭지 못했다.

예를들어 아래와 같은 요구사항이 들어오면 기존 코드에서는 달력 전체를 건드려야 했다.
그러나 useCalendar 로 데이터와 UI 를 구분해 작성하니 controller 위치만 조정하니 쉽게 구현이 가능했다.

연도와 월을 가운데로 옮겨주시고, 버튼을 양쪽에 두고 싶어요.
그리고 버튼은 손가락 이모티콘으로 바꿔주세요

기존변경 요청

이번에 토스 영상을 보고 데이터와 UI 를 구분해서 코드를 작성하는 방법에 대해 알게 되었고, 마침 토이 프로젝트로 달력을 만들어 놓은게 있어서 쉽게 젹용해볼 수 있었다.

평소에는 input 이나 button, modal 과 같은 것들을 hook 으로 만드는걸 당연하게 생각해왔는데, 달력도 마찬가지로 변하지 않는 기본 동작을 훅으로 만들 수 있다는 생각을 못했었다. 이번 경험을 통해 데이터와 UI를 분리하고, 훅을 사용하여 변하는 값과 변하지 않는 값의 구분을 명확히 하는 데에 대한 아이디어를 얻을 수 있었다.

profile
배우고 느낀 걸 기록하는 공간

0개의 댓글

관련 채용 정보