ABOUT ME

-

  • [ React Native ] React Native Big Calendar 공휴일 커스텀
    Application/React Native 2026. 3. 14. 17:54
    반응형

    https://yumedev.tistory.com/103

     

    [ Next ] 공휴일 Open Api 만들기

    만드는 프로젝트에 캘린더가 들어가면 항상 공공데이터포털에서 api를 호출해서 사용해야하기 때문에이전 라이브러리는 직접 공공데이터포털 api를 호출해서 뿌려주기때문에 결국에는 직접 공

    yumedev.tistory.com

    이전 공휴일 open api를 만들고 나서 앱에서 해당 api를 호출하도록 했다.

    redis를 사용했지만 redis도 무료 제한이 있기때문에 나중에는 해당 부분이 문제가 생기면 

    따로 local에 저장해서 날짜 업데이트 하는 시기에 맞춰서만 api를 호출하도록 수정해야할 수도..

     

    우선 공휴일 데이터를 불러왔으니 이제 캘린더에 공휴일 표시를 해줘야한다.


    React Native Big Calendar

    https://yumedev.tistory.com/101

     

    [ React Native ] React Native Calendar 커스텀

    React Native에서 캘린더를 구현할려고하는데처음에는 React Native Calendar를 사용했는데 캘린더의 일정 표시를 하루는 되지만 범위가 안되어서 다른 라이브러리를 사용. React Native Big Calendar는 일정 ran

    yumedev.tistory.com


    이전 라이브러리에서 날짜 커스텀이 안되서 직접 건드려서 날짜의 style을 바꾼 것처럼

    이번에도 직접 라이브러리를 바꿔서 사용할려고 한다.

     

    우선 공휴일 데이터를 가져왔으니 이걸 캘린더에 props로 전달해줘야한다.

    그래서 캘린더 props 타입에 holidayEvent 타입을 추가

    export interface CalendarContainerProps<T extends ICalendarEventBase> {
    	/.../
        holidayEvent?: Record<string, string>;
    }

     

    이후 props로 전달 받은 것을 받아올 수 있도록

    renderEvent =. a.renderEvent가 붙은 곳에 전부 붙혀주었다.

    ( 일단 renderEvent는 할일 렌더링을 해주는 부분이니까 그냥 사용하는 부분에는 다 공휴일 데이터가 필요할 수도.. 하면서 다 붙혀주었다. )

    renderEvent = _a.renderEvent, holidayEvent = _a.holidayEvent

     

    이후 공휴일 데이터는 결국 db에서 데이터를 가져오는 것이므로 await로 가져와야하다보니

    캘린더에서도 데이터가 들어오고 나서 렌더링 하는게 아닌이상 데이터가 가져오면 다시 재렌더링 시켜줘야한다.

    그래서 기존 로직에서 holidayEvent를 감지할 수 있도록해야한다.

     

    index.js에 보면 calendarContainer 부분이 있는데

    여기서 월 모드만 쓰기때문에

    return 부분에 마지막 createElement 부분에 holidayeEvent: holidayEvent를 추가해주었다.

    function _CalendarContainer(_a) {
    	/.../
    	if (mode === 'month') {
        	/.../
    	return (React__default["default"].createElement(React__default["default"].Fragment, null,
                React__default["default"].createElement(HeaderComp_1, tslib.__assign({}, headerProps_1)),
                React__default["default"].createElement(.... renderEvent: renderEvent, holidayEvent: holidayEvent,...)

     


    Calendar Component

    공휴일 데이터는 날짜가 바뀌면 이전 저장된 년도를 비교해서 

    다르면 데이터를 가져오고 같으면 그대로 유지하도록 처리

       useEffect(() => {
            if(props.date.year() !== currentYear) {
                setCurrentYear(props.date.year())
            }
    
        }, [monthKey]);
    
        const [holidays, setHolidays] = useState<Record<string, string>>({});
    
        useEffect(() => {
            const fetchHolidays = async () => {
                const data = await appStore.getYearHolidays(currentYear);
                setHolidays(data || {});
            };
    
            fetchHolidays();
        }, [currentYear]);

     

    이후 store에서도 캐싱된 데이터가 존재하면 존재하는 데이터로 없다면 api를 호출해서 데이터를 가져오도록 설정

    interface AppState {
    	/.../
        // 휴일 정보 캐싱
        holidayMap: Record<string, string>
        getYearHolidays: (year: number) => Promise<Record<string,string>>
        loadHolidays: (year: number) => Promise<void>
    }
    
    export const useAppStore = create<AppState>((set,get) => ({
    	/.../
        holidayMap: {},
        getYearHolidays: async (year) => {
            const { holidayMap } = get();
    
            if (holidayMap[year] && Object.keys(holidayMap[year]).length > 0) {
                return holidayMap[year];
            }
    
            const holidays = await getHolidays(year)
            set((state) => ({
                holidayMap: {
                    ...state.holidayMap,
                    [year]: holidays
                }
            }))
    
            return holidays;
        },
        loadHolidays: async (year) => {
            const { getYearHolidays } = get();
            getYearHolidays(year)
        },
    }))

     

    이후 props를 전달

    <Calendar<CalendarEvent>
    	/.../
       	holidayEvent={holidays}
    	/.../
    />

     

     


    Test

    이후 이전에 수정한 renderDateCell함수에 holidayEvent를 찍어서 데이터가 정상적으로 들어오는지 확인

    var renderDateCell = function (date, index) {
    	/.../
    	console.log(holidayEvent)
    }

     

    이후 시간은 좀 걸리지만 데이터가 나오는 것을 확인 할 수 있다.

    현재 처음 캘린더가 로딩되고 이후 공휴일 데이터가 들어오면서 재렌더링 해서 속도가 좀 느려서 이부분을

    좀 더 데이터 처리 방식 아니면 캘린더 렌더링 방식을 변경해서 추가 작업을 해야할 것 같다.

     

    이후 이제 날짜와 holidayEvent와 비교해서 존재하면 날짜를 빨간색으로 바꾸고 토요일을 그냥 파란색으로 나오도록 처리

    var renderDateCell = function (date, index) {
            if (date && renderCustomDateForMonth) {
                return renderCustomDateForMonth(date.toDate());
            }
    
            // 1. 공통 변수 설정
            var isToday = (date === null || date === void 0 ? void 0 : date.format(SIMPLE_DATE_FORMAT)) === now.format(SIMPLE_DATE_FORMAT);
            var isCurrentMonth = (date === null || date === void 0 ? void 0 : date.month()) === targetDate.month();
            var day = date === null || date === void 0 ? void 0 : date.day();
    
            var dateStr = date ? date.format('YYYYMMDD') : null;
            var holidayName = (dateStr && holidayEvent) ? holidayEvent[dateStr] : null;
    
            // 2. 색상 결정 로직
            var getTextColor = function () {
                if (isToday) return theme.palette.primary.main;
    
                if (day === 0 || holidayName) { // 일요일
                    return isCurrentMonth ? '#FF3B30' : 'rgba(255,59,48,0.2)'; // 이번 달 아니면 30% 투명도 (HEX 뒤에 4D 추가)
                }
                if (day === 6) { // 토요일
                    return isCurrentMonth ? '#007AFF' : 'rgba(0,122,255,0.2)'; // 이번 달 아니면 30% 투명도
                }
    
                // 평일
                return isCurrentMonth ? theme.palette.gray['800'] : theme.palette.gray['500'];
            };
    
            var todayContainerStyle = isToday ? {
                backgroundColor: theme.palette.primary.main,
                color: theme.palette.gray['800'],
                width: 18,
                height: 18,
                padding: 2,
                borderRadius: 100,
            } : {
                padding: 2,
            };
            
            return (
                React__namespace.createElement(reactNative.View, {
                        style: {
                            flexDirection: 'row',
                            alignItems: 'center',
                            justifyContent: 'start',
                            paddingVertical: 2,
                            width: '100%',     
                            paddingHorizontal: 2
                        }
                    },
                    // 날짜 숫자
                    React__namespace.createElement(reactNative.Text, {
                        style: [
                            { textAlign: 'center' },
                            theme.typography.sm,
                            { color: getTextColor() },
                            todayContainerStyle,
                            tslib.__assign({}, getCalendarCellTextStyle(date === null || date === void 0 ? void 0 : date.toDate(), index)),
                        ]
                    }, date && date.format('D')),
    
                    // 공휴일 명칭
                    holidayName && React__namespace.createElement(reactNative.Text, {
                        style: {
                            fontSize: 8,
                            fontWeight: 'bold',
                            color: isCurrentMonth ? '#FF3B30' : 'rgba(255,59,48,0.2)',
                            marginLeft: 2,
                            flexShrink: 1, 
                        },
                        numberOfLines: 1,
                        ellipsizeMode: 'clip'
                    }, holidayName)
                )
            );
        };

     

    그럼 이렇게 공휴일 데이터가 정상적으로 들어가서 화면에 나오는 것을 확인할 수 있다.

    728x90
    반응형