React Native 캘린더 사용하기 (react-native-calendars)
현재 졸업 프로젝트로 비건 서비스 앱을 제작 중에 있는데, 그 중 내가 맡은 부분은 다른 식단관리 앱과 같이 캘린더에 매일 먹은 음식의 비건식 여부를 기록할 수 있는 기능이다. 이를 구현하기 위해 앱에서 캘린더를 구현해야 했다. 찾아보던 중 react-native-calendars 라는 라이브러리를 발견했다. iOS와 Android 둘 다 지원한다. 이 라이브러리를 이용해 기본적인 캘린더를 구현하고자 한다.
먼저 생성한 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 로 실행하면 이렇게 우리가 아는 모양에 가까워진 캘린더의 모습을 볼 수 있다.