Develop/React Native

React Native 캘린더 사용하기 (react-native-calendars)

찹쌀토마토 2022. 11. 15. 18:09

 현재 졸업 프로젝트로 비건 서비스 앱을 제작 중에 있는데, 그 중 내가 맡은 부분은 다른 식단관리 앱과 같이 캘린더에 매일 먹은 음식의 비건식 여부를 기록할 수 있는 기능이다. 이를 구현하기 위해 앱에서 캘린더를 구현해야 했다. 찾아보던 중 react-native-calendars 라는 라이브러리를 발견했다. iOS와 Android 둘 다 지원한다. 이 라이브러리를 이용해 기본적인 캘린더를 구현하고자 한다.

react-native-calendars GitHub

 

 

 먼저 생성한 React Native 프로젝트 폴더에 라이브러리를 설치한다.

npm install --save react-native-calendars

 

디폴트 캘린더 구현

 GitHub readme에 적힌 코드를 프로젝트 내의 App.js에 복사한다.

import React, { Component } from 'react'
import {Calendar} from 'react-native-calendars';

class App extends Component {
  render() {
     return (
       {console.log('selected day', day)}}
        // Handler which gets executed on day long press. Default = undefined
        onDayLongPress={(day) => {console.log('selected day', day)}}
        // Month format in calendar title. Formatting values: <http://arshaw.com/xdate/#Formatting>
        monthFormat={'yyyy MM'}
        // Handler which gets executed when visible month changes in calendar. Default = undefined
        onMonthChange={(month) => {console.log('month changed', month)}}
        // Hide month navigation arrows. Default = false
        hideArrows={true}
        // Replace default arrows with custom ones (direction can be 'left' or 'right')
        renderArrow={(direction) => ()}
        // Do not show days of other months in month page. Default = false
        hideExtraDays={true}
        // If hideArrows=false and hideExtraDays=false do not switch month when tapping on greyed out
        // day from another month that is visible in calendar page. Default = false
        disableMonthChange={true}
        // If firstDay=1 week starts from Monday. Note that dayNames and dayNamesShort should still start from Sunday.
        firstDay={1}
        // Hide day names. Default = false
        hideDayNames={true}
        // Show week numbers to the left. Default = false
        showWeekNumbers={true}
        // Handler which gets executed when press arrow icon left. It receive a callback can go back month
        onPressArrowLeft={substractMonth => substractMonth()}
        // Handler which gets executed when press arrow icon right. It receive a callback can go next month
        onPressArrowRight={addMonth => addMonth()}
        // Disable left arrow. Default = false
        disableArrowLeft={true}
        // Disable right arrow. Default = false
        disableArrowRight={true}
        // Disable all touch events for disabled days. can be override with disableTouchEvent in markedDates
        disableAllTouchEventsForDisabledDays={true}
        /** Replace default month and year title with custom one. the function receive a date as parameter. */
        renderHeader={(date) => {/*Return JSX*/}}
      />
     )
   }
 }
export default App;

 react-native run-android 로 실행하면 다음과 같이 캘린더 화면이 뜨는 것을 알 수 있다. 하지만 제일 왼쪽 열에 관련 없는 숫자들도 보이고, 우리가 흔히 사용하는 캘린더의 모양과도 다르다. 다행히 이 라이브러리는 다양한 커스터마이징을 지원한다.

 

 

 

캘린더 커스터마이징

1. 현재 위치한 월 표시

 우선 달력이므로 현재 위치한 날짜가 몇 월인지 알아야 한다. 이는 캘린더 태그 안의 마지막 옵션인 renderHeader={(date) => {/*Return JSX*/}}를 주석 처리해주면 2022 11 이라는 현재 위치한 날짜의 월을 알 수 있다.

 

2. 요일 표시

 또한 달력에는 요일이 필요하다. README의 Calender 소제목 위에 LocaleConfig 에 대해 다룬 파트가 있는데 이 안에 dayNames와 dayNamesShort를 이용해 각 날짜가 무슨 요일인지 상단에 표시해 줄 수 있다. 예시로 적혀있는 요일은 프랑스어로 추정된다…

import {LocaleConfig} from 'react-native-calendars';

LocaleConfig.locales['fr'] = {
  monthNames: ['Janvier','Février','Mars','Avril','Mai','Juin','Juillet','Août','Septembre','Octobre','Novembre','Décembre'],
  monthNamesShort: ['Janv.','Févr.','Mars','Avril','Mai','Juin','Juil.','Août','Sept.','Oct.','Nov.','Déc.'],
  dayNames: ['일요일','월요일', '화요일','수요일','목요일','금요일','토요일'],
  dayNamesShort: ['일', '월','화','수','목','금','토'],
  today: 'Aujourd\\'hui'
};
LocaleConfig.defaultLocale = 'fr';

위의 부분을 App.js의 3번 줄에 붙여넣은 후 캘린더 태그의 hideDayNames={true} 를 false로 변경해주면 요일별 이름을 볼 수 있다.

 

3. 가장 왼쪽 열 숫자 제거

 캘린더 태그 안의 속성들을 보다 가장 왼쪽 열의 회색 숫자들이 weekNumber, 그 해의 몇번째 주인지를 나태낸다는 것을 알았다. showWeekNumbers={true} 를 false로 바꿔준다.

 

 

현재까지의 App.js 코드

import React, { Component } from 'react'
import {Calendar} from 'react-native-calendars';
import {LocaleConfig} from 'react-native-calendars';

LocaleConfig.locales['fr'] = {
  monthNames: ['Janvier','Février','Mars','Avril','Mai','Juin','Juillet','Août','Septembre','Octobre','Novembre','Décembre'],
  monthNamesShort: ['Janv.','Févr.','Mars','Avril','Mai','Juin','Juil.','Août','Sept.','Oct.','Nov.','Déc.'],
  dayNames: ['일요일','월요일', '화요일','수요일','목요일','금요일','토요일'],
  dayNamesShort: ['일', '월','화','수','목','금','토'],
  today: 'Aujourd\\'hui'
};
LocaleConfig.defaultLocale = 'fr';

class App extends Component {
  render() {
     return (
         {console.log('selected day', day)}}
        // Handler which gets executed on day long press. Default = undefined
        onDayLongPress={(day) => {console.log('selected day', day)}}
        // Month format in calendar title. Formatting values: <http://arshaw.com/xdate/#Formatting>
        monthFormat={'yyyy MM'}
        // Handler which gets executed when visible month changes in calendar. Default = undefined
        onMonthChange={(month) => {console.log('month changed', month)}}
        // Hide month navigation arrows. Default = false
        hideArrows={true}
        // Replace default arrows with custom ones (direction can be 'left' or 'right')
        renderArrow={(direction) => ()}
        // Do not show days of other months in month page. Default = false
        hideExtraDays={true}
        // If hideArrows=false and hideExtraDays=false do not switch month when tapping on greyed out
        // day from another month that is visible in calendar page. Default = false
        disableMonthChange={true}
        // If firstDay=1 week starts from Monday. Note that dayNames and dayNamesShort should still start from Sunday.
        firstDay={1}
        // Hide day names. Default = false
        hideDayNames={false}
        // Show week numbers to the left. Default = false
        showWeekNumbers={false}
        // Handler which gets executed when press arrow icon left. It receive a callback can go back month
        onPressArrowLeft={substractMonth => substractMonth()}
        // Handler which gets executed when press arrow icon right. It receive a callback can go next month
        onPressArrowRight={addMonth => addMonth()}
        // Disable left arrow. Default = false
        disableArrowLeft={true}
        // Disable right arrow. Default = false
        disableArrowRight={true}
        // Disable all touch events for disabled days. can be override with disableTouchEvent in markedDates
        disableAllTouchEventsForDisabledDays={true}
        /** Replace default month and year title with custom one. the function receive a date as parameter. */
        //renderHeader={(date) => {/*Return JSX*/}}
        />
     )
   }
 }

export default App;

다시 react-native run-android 로 실행하면 이렇게 우리가 아는 모양에 가까워진 캘린더의 모습을 볼 수 있다.

 

 

 

월 이동 화살표 추가하기

 내가 구현하려고 하는 기능은 식단 기록이기에 단순히 오늘 날짜와 요일을 보여주는 것 뿐만 아니라, 그 전과 후의 기록을 볼 수 있는 기능도 필요하다. 그러기 위해선 월의 이동이 필수적이다. 코드를 살펴보던 중 arrow 관련된 속성들이 보여 이 설정값을 변경하면 가능하다는 것을 알게 되었다. 한 번 알게되면 간단한 방법이지만 구현하기 위해 많은 구글링과 시간이 걸렸기에 기록하게 되었다.

 

 우선 위 App.js 코드에서 arrow가 들어간 세 속성의 값을 바꿔준다.

.
	// Hide month navigation arrows. Default = false
  	hideArrows={false}
.
.
.
	// Disable left arrow. Default = false
  	disableArrowLeft={false}
  	// Disable right arrow. Default = false
 	disableArrowRight={false}

 

 이렇게만 수정하고 다시 실행하면 Arrow라는 변수를 찾을 수 없다는 에러가 뜬다. 에러가 난 코드를 보니 35번 줄의 renderArrow={(direction) => (<Arrow/>)} 라는 속성에서 오류가 났다. 바로 윗줄의 주석에서 Replace default arrows with custom ones (direction can be 'left' or 'right') 라는 설명으로 화살표를 내가 커스텀 하라고 되어있는데, React Native 가 처음인 나로선 어떻게 해야할지 막막했다.

 

 우선 renderArrow 부분을 다음과 같이 변경한다.

// Replace default arrows with custom ones (direction can be 'left' or 'right')
renderArrow={(direction) => direction === "left" ? (
              <Icon name="caretleft" size={20} color="#81CC7E" />
                ) : (
              <Icon name="caretright" size={20} color="#81CC7E" />
                )
            }

 renderArrow 라는 속성에 화살표 함수가 들어가는데 이때 이 때 direction의 값이 left (왼쪽 화살표 - 전 달로 이동) 이면 내가 : 앞에 명시한 아이콘으로 왼쪽 화살표를 렌더링 하고, 마찬가지로 right 면 : 뒤에 명시한 아이콘으로 오른쪽 화살표를 렌더링 해 화면에 나타낸다. 이를 조건부 연산자 ?를 이용해 설정했다.

명시할 아이콘은 다양한데 나는 그 중 react-native-vector-icons 를 선택했다.

 

 

react-native-vector-icons

npm 으로 프로젝트 폴더에 설치한다.

npm install react-native-vector-icons

https://oblador.github.io/react-native-vector-icons/ 에 들어가 원하는 icon을 선택한다. 나는 이 중 AntDesign 의 caretright(▶), caretleft(◀)를 선택했다.

 

react-native-vector-icons directory

 

oblador.github.io

 

 사용할 코드 상단에서 import 한다.

import Icon from 'react-native-vector-icons/AntDesign';
// import Icon from 'react-native-vector-icons/사용할 아이콘의 상위폴더 이름';

 

 사용할 부분에 Icon 태그를 넣고 여러 속성을 적용해 사용한다. 다시 위의 renderArrow 코드로 돌아가면, 왼쪽 화살표는 “caretleft”라는 이름을 가진, 크기가 20이고, 색상이 #81CC7E인 아이콘으로, 오른쪽 화살표는 “caretright”라는 이름을 가진, 크기가 20이고, 색상이 #81CC7E인 아이콘으로 렌더링 되는 코드임을 알 수 있다.

renderArrow={(direction) => direction === "left" ? (
              <Icon name="caretleft" size={20} color="#81CC7E" />
                ) : (
              <Icon name="caretright" size={20} color="#81CC7E" />
                )
            }

 

최종 App.js

import React, { Component } from 'react'
import {Calendar} from 'react-native-calendars';
import {LocaleConfig} from 'react-native-calendars';
import Icon from 'react-native-vector-icons/AntDesign';

LocaleConfig.locales['fr'] = {
  monthNames: ['Janvier','Février','Mars','Avril','Mai','Juin','Juillet','Août','Septembre','Octobre','Novembre','Décembre'],
  monthNamesShort: ['Janv.','Févr.','Mars','Avril','Mai','Juin','Juil.','Août','Sept.','Oct.','Nov.','Déc.'],
  dayNames: ['일요일','월요일', '화요일','수요일','목요일','금요일','토요일'],
  dayNamesShort: ['일', '월','화','수','목','금','토'],
  today: 'Aujourd\\'hui'
};
LocaleConfig.defaultLocale = 'fr';

class App extends Component {
  render() {
     return (
         {console.log('selected day', day)}}
        // Handler which gets executed on day long press. Default = undefined
        onDayLongPress={(day) => {console.log('selected day', day)}}
        // Month format in calendar title. Formatting values: <http://arshaw.com/xdate/#Formatting>
        monthFormat={'yyyy MM'}
        // Handler which gets executed when visible month changes in calendar. Default = undefined
        onMonthChange={(month) => {console.log('month changed', month)}}
        // Hide month navigation arrows. Default = false
        hideArrows={false}
        // Replace default arrows with custom ones (direction can be 'left' or 'right')
        renderArrow={(direction) => direction === "left" ? (
          
            ) : (
          
            )
        }
        // Do not show days of other months in month page. Default = false
        hideExtraDays={true}
        // If hideArrows=false and hideExtraDays=false do not switch month when tapping on greyed out
        // day from another month that is visible in calendar page. Default = false
        disableMonthChange={true}
        // If firstDay=1 week starts from Monday. Note that dayNames and dayNamesShort should still start from Sunday.
        firstDay={1}
        // Hide day names. Default = false
        hideDayNames={false}
        // Show week numbers to the left. Default = false
        showWeekNumbers={false}
        // Handler which gets executed when press arrow icon left. It receive a callback can go back month
        onPressArrowLeft={substractMonth => substractMonth()}
        // Handler which gets executed when press arrow icon right. It receive a callback can go next month
        onPressArrowRight={addMonth => addMonth()}
        // Disable left arrow. Default = false
        disableArrowLeft={false}
        // Disable right arrow. Default = false
        disableArrowRight={false}
        // Disable all touch events for disabled days. can be override with disableTouchEvent in markedDates
        disableAllTouchEventsForDisabledDays={true}
        /** Replace default month and year title with custom one. the function receive a date as parameter. */
        //renderHeader={(date) => {/*Return JSX*/}}
        />
     )
   }
 }

export default App;

다시 react-native run-android 로 실행하면 이렇게 우리가 아는 모양에 가까워진 캘린더의 모습을 볼 수 있다.