2주만에 다시 RN을 하려니 가물가물하네요.. ㅠ^ㅠ
새 마음 새 뜻으로 프로젝트 생성부터 해 보겠습니다..
> npx create-expo-app todo-calendar
명령어로 expo 프로젝트를 생성해주었습니다.
날짜는 라이브러리 없이 new Date
로 관리가 가능합니다.
현재 날짜, 현재 시간 등을 받아오는 작업등을 할 수 있습니다.
어플에서 사용하는 작업중에서는 단순한 기능보다는, 날짜에서 하루를 빼고, 며칠을 더하고 비교하는 등의 정교한 작업을 필요로합니다.
정교한 작업을 위해서는 날짜를 쉽게 다루게 해주는 라이브러리를 사용하는 것이 좋습니다.
날씨 라이브러리
- moment.js
- day.js
신규개발을 중단하고 번들사이즈가 큰 moment.js 보다는 day.js 사용을 지향합니다. 사용법은 유사합니다.
yarn
을 통해 day.js를 설치해주었습니다.
import dayjs from "dayjs";
import isBetween from "dayjs/plugin/isBetween";
import isSameOrBefore from "dayjs/plugin/isSameOrBefore";
import isSameOrAfter from "dayjs/plugin/isSameOrAfter";
dayjs.extend(isBetween);
dayjs.extend(isSameOrBefore);
dayjs.extend(isSameOrAfter);
export const runPracticeDayjs = () => {
const hour = new Date().getHours();
console.log('hour', hour);
const now = dayjs("2022-11-04 16:01:30");
console.log("===== Practice Dayjs =====");
console.log(
"1. set minute - hh",
dayjs(now).set("minute", 5).format("YYYY.MM.DD hh:mm:ss a A")
);
console.log(
"2. set minute - HH",
dayjs(now).set("minute", 5).format("YYYY.MM.DD HH:mm:ss")
);
console.log(
"3. set hour",
dayjs(now).set("hour", 10).format("YYYY.MM.DD HH:mm:ss")
);
console.log("4. get year", dayjs(now).get("year"));
console.log("5. get month", dayjs(now).get("month")); // 0~11(1월~12월)
console.log("6. get date", dayjs(now).get("date"));
console.log("7. get day", dayjs(now).get("day")); // 0:일 ~ 6:토
console.log("8. get second", dayjs(now).get("second"));
console.log(
"9. add hour",
dayjs(now).add(3, "hour").format("YYYY.MM.DD HH:mm:ss")
);
console.log(
"10. subtract hour",
dayjs(now).subtract(3, "hour").format("YYYY.MM.DD HH:mm:ss")
);
console.log("11. startOf", dayjs(now).startOf("month").format("YYYY.MM.DD"));
console.log("12. endOf", dayjs(now).endOf("month").format("YYYY.MM.DD"));
const aDate = dayjs("2022-10-29 15:00:20");
const bDate = dayjs("2022-10-29 16:00:00");
console.log("13. isSame month", dayjs(aDate).isSame(bDate, "month"));
console.log("14. isSame hour", dayjs(aDate).isSame(bDate, "hour"));
console.log("15. isBefore", dayjs(aDate).isBefore(bDate));
console.log("16. isBefore date", dayjs(aDate).isBefore(bDate, "date"));
console.log("17. isAfter a,b", dayjs(aDate).isAfter(bDate));
console.log("18. isAfter b,a", dayjs(bDate).isAfter(aDate));
console.log("19. isSameOrBefore", dayjs(aDate).isSameOrBefore(bDate, "date"));
console.log("20. isSameOrAfter", dayjs(aDate).isSameOrAfter(bDate, "date"));
console.log(
"21. isBetween",
dayjs("2022-10-29 15:30:00").isBetween(aDate, bDate)
);
console.log(
"22. isBetween date",
dayjs("2022-10-29 15:30:00").isBetween(aDate, bDate, "date")
);
console.log("23. diff minute a,b", dayjs(aDate).diff(bDate, "minute"));
console.log("24. diff minute b,a", dayjs(bDate).diff(aDate, "minute"));
};
src
폴더를 생성하고 practice-dayjs.js
파일을 생성하여 예제코드를 살펴보았습니다.
날짜 관련은 다른 곳에서도 다루기 떄문에 자세하게 살펴보지는 않았습니다.
format
YYYY
: 년 (4자리)
MM
: 월 (2자리)
DD
: 일 (2자리)
HH
: 시간 (24시간제)
hh
: 시간 (12시간제)
mm
: 분
ss
: 초
a
: 오전
A
: 오후
month
: 0~11 (1월~12월)
day
: 0~6 (일~토)
subtract
: 날짜 빼기
startOf
: 맨 처음 날짜
endOf
: 맨 마지막 날짜
isSame
: 날짜가 같은지 비교
isBefore
: 해당 날짜보다 이전인지 비교
isAfter
: 해당 날짜보다 이후인지 비교
isSameBefore
: 해당 날짜보다 같거나 이전인지 비교
isSameAfter
: 해당 날짜보다 같거나 이후인지 비교
isBetween
: 해당 날짜 사이에 있는지 비교
diff
: 두 날짜의 차이
단위가 없는 경우
: ms 까지 비교
isBetween, isSameOrBefore, isSameORAfter 함수는 import
해주고 day.js에 extend
를 해주어야 사용 가능합니다.
윈도우에 보이는 캘린더입니다.
캘린더를 하나의 배열로 보자면 총 35가지의 원소가 담긴 배열이 필요합니다.
7개의 컬럼이 있고, 5개의 열이 있습니다.
App.js에 columns
,now
를 추가하여, 현재 시간을 기준으로 캘린더에 담길 컬럼을 가져와줍니다.
useEffect
랜더링 직후에 실행되는 함수에서 콘솔로 찍어주었습니다.
빼먹은 day.js와 useEffect도 import
해주었습니다.
위에 로그들은 예제코드의 로그들이고 가장 아래의 columns
을 확인해줍니다.
columns
의 내용을 정렬해주기 위해 https://jsonformatter.org/ 페이지에 복붙해주었습니다.
정렬된 내용을 살펴보니 10월 1일부터 11월 4일까지의 값을 가지고 있습니다.
cloumns
값을 가져와 주는 getCalendarColumns 함수에 대해 살펴보겠습니다.
export const getCalendarColumns = (now) => {
const start = dayjs(now).startOf("month"); // 10월 1일
const end = dayjs(now).endOf("month"); // 10월 31일
const endDate = dayjs(end).get("date"); //31
const columns = [];
for (let i = 0; i < endDate; i += 1) {
const date = dayjs(start).add(i, "day");
columns.push(date);
}
const filledColumns = fillEmptyColumns(columns, start, end);
return filledColumns;
};
now
현재 시간을 토대로, now가 포함된 month을 기준으로 첫 번째 날짜 start
를 가져왔고, 마지막 날짜인 end
를 가져왔습니다.
가져온 날짜를 배열에 넣기 위해 길이를 구해줍니다. 날짜가 1부터 시작하므로 마지막날짜가 길이가됩니다.
end
의 get("date") 를 통해 31 을 가져옵니다.
columns
라는 배열을 생성했고, 반복문을 통해 담아줍니다. endDate
만큼 반복합니다.
dayjs 에서 start
날짜를 기준으로 i
가 오는만큼 날짜를 더해서 columns
에 담아줍니다.
10월 1일이 일요일이기때문에 채워줄 필요는 없습니다.
하지만 다른 달에 했을때는 다르기 때문에 코드는 작성해주겠습니다.
// 1. 첫날 이전 공백 채우기
const startDay = dayjs(start).get("day"); //0
for (let i = 1; i <= startDay; i += 1) {
const date = dayjs(start).subtract(i, "day");
filledColumns.unshift(date);
}
start
시작일의 요일에따라 반복문이 몇번돌지 정해줍니다.
일요일인 경우에는 day 가 0 이기때문에 0만큼 채워줍니다.
수요일인 경우에는 day 가 3 이기 떄문에 3만큼 채워줍니다.
start
를 기준으로 i
만큼 뺀 날짜를
unshift
함수를 이용해서 차례대로 앞에 넣어줍니다.
// 2. 마지막날 이후 공백 채우기
const endDay = dayjs(end).get("day");
/**
0 -> 6
1 -> 5
2 -> 4
endDay + ? = 6
*/
for (let i = 1; i <= 6 - endDay; i += 1) {
const date = dayjs(end).add(i, "day");
filledColumns.push(date);
}
end
마지막 날의 요일을 기준으로 반복문에 몇 번 돌지 정해줍니다.
push
함수를 사용해서 마지막에 차례대로 넣어줍니다.
이렇게 해서 날짜 데이터를 columns
배열에 담아주는 getCalendarColumns 함수를 살펴보았습니다.
만들어진 columns 를 가지고 달력을 그려줍니다.
안쓰는 부분은 지워주었습니다.
FlatList
를 선언하고, date
에는 columns, renderItem
을 추가해줍니다.
renderItem
은 가독성있게 return문 위 쪽에 작성해주었습니다.
prop 안의 item으로 데이터의 컬럼에 하나하나 접근해줍니다.
우선 Text 로 만들어주었고, 안에 들어갈 item 은 date
이기 때문에 재명명 해주었습니다.
dayjs
로 감싸주면 dayjs
의 함수를 사용할 수 있습니다.
dayjs
를 통해 date를 가져와줍니다.
35개의 원소가 담긴 배열인 columns가 랜더링되었습니다.
제 경우에는 잘리지 않았지만, 다른 환경에서는 잘릴 수 있으니
SafeAreaView 로 수정해서 안전한 영역에서 표시되도록 수정해주었습니다.
달력은 한 줄에 7개의 요일이 들어갑니다.
numColumns
: 한 줄에 몇개의 columns이 그려질지 결정해주는 속성
한 줄에 7개씩 렌더링된 모습입니다.
스타일링을 주기위해 View
를 사용해주었고, Text
들 사이에 공백을 추가해주었습니다.
달력 같아진 모습!! 상단바 영역을 침범하는 건 불편하지만 추후에 전체적인 스타일링 하면서 조정해줄테니 일단 넘어갑니다.. ㅎ
중앙에 오도록 스타일링을 한 번 더 추가해주었습니다.
공백은 이제 필요 없으니 지워주었습니다.
date 의 day 에 따라 색상을 다르게 해줍니다.
dayjs
로 date를 감싸주고, get('day')
로 요일 정보를 받아옵니다.
day 가 0(일)
일때와 6(토)
일 떄 예외처리를 해줍니다.
삼항연산자를 사용하여 구현해주었습니다.
색상이 들어가니 점점 형태가 나오네요.
마찬가지로 Text 에 Opcity를 설정해주고,
dayjs
를 활용해 date가 now의 month 와 같은지 비교하여 isCurrentMonth 값을 전해줍니다.
작성일 10월 24일입니다. 다음 달인 11월은 Opacity 처리 되어 불투명해졌습니다.
날짜가 랜더링된 부분 위에 들어갈 것이기 때문에 ListHeaderComponent로 만들어줄 수 있습니다.
요일을 그리는 부분이 날짜를 그린 부분과 유사하여 요일에도 똑같이 적용이 가능합니다.
renderItem 부분을 Column컴포넌트로 따로 빼주기 전에!
0
부터 6
까지의 param을 받아 요일을 반환하는 getDayText 함수는 src
폴더의 utill.js
에 선언해주었습니다.
case문을 통해서 리턴해주어도 되고, 배열을 생성하고 day가 곧 index이므로 그대로 리턴해주어도 됩니다.
그리고 color
부분도 마찬가지로 0
부터 6
까지의 param을 받아 색상을 반환하는 getDayColor 함수도 src
폴더의 utill.js
에 선언해주었습니다.
다시 App.js 로 돌아와서 renderItem 부분을 Column 컴포넌트로 빼주겠습니다.
text, color, opacity 를 받아서 return 해주는 Column 컴포넌트를 만들어주었습니다.
현재 날짜와, 요일을 출력해줄 헤더부분인 ListHeaderComponent를 추가해주었고, 요일부분만 우선 작성하였습니다.
flexDirection: "row"
로 스타일을 주어 요일이 가로로 배치되도록 했습니다.
renderItem 부분도 기존 코드에서 Column 컴포넌트를 재사용하도록 수정하였습니다.
최종 return 부분에도 ListHeaderComponent를 추가해주었습니다.
요일이 위에 잘 있겠지만,,,,,,,,,, 미뤄뒀던 상단바 영역 구분을 해 주어야 할 것 같습니다.
react-native-iphone-x-helper
라이브러리를 받아줍니다.
> yarn add react-native-iphone-x-helper
getStatusBarHeight
, getBottomSpace
를 import 해주고, statusBarHeight, bottomSpace에 해당 함수를 이용해 상단바 영역과 하단 영역의 높이를 넣어주었습니다.
statusBarHeight에는 (true)
를 주어 안전영역을 포함한 높이로 넣어주었습니다.
App.js 가장 하단에 있는 Stylesheet에 paddingTop : statusBarHeigght
로 지정해주었습니다.
요일도 잘 보이는 모습입니다!
currentDateText로 현재 now 날짜를 받아와 포맷해주고, 스타일링을 해주었습니다.
Margin
은 유용하게 많이 사용될테니 src
폴더에 `Margin.js
를 추가하여 따로 작성해주었습니다.
날짜 크기가 너무 작아서 Text 스타일링을 해 주었고, 위 아래로 Margin을 추가하였습니다.
https://icons.expo.fyi/Index
항상 애용하던 사이트에서 아이콘을 가져와줍니다.
SimpleLineIcons 의 arrow-left
와 arrow-right
를 선택해주었습니다.
당장 사용해야할 버튼도 2개이기 때문에 ArrowButton 컴포넌트로 따로 만들어주었습니다.
터치가 가능한 버튼이어야하기 때문에 TouchableOpacity로 감싸주고, 스타일링 해주었습니다.
ListHeaderComponent에 적용해줍니다.
버튼 스타일에 Padding을 넣어주었기 때문에 Margin을 제거해주었습니다.
UI만 우선 구성하고, 함수부분은 아직 비워두었습니다.
UI가 모두 구성된 모습입니다.
https://velog.io/@bi-sz/투두리스트프로젝트[2]
기능구현은 다음 게시글에서 이어서 진행하겠습니다.