React-Native/[프로젝트] 택시 운행관리 기록장
[React-native CLI] Home화면 기능 구현하기
개발조각
2024. 3. 23. 22:16
728x90
반응형
Home화면에서 구현해야 될 기능을 크게 3가지로 구분할 수 있습니다.
- 현재 달이면 다음 달 이동 못하게 하기
- 월간 운행 정보 데이터 구하기
- 연간 영업 금액 구하기
구현 화면
기존 코드
// react, react-native
import React, {useEffect, useState} from 'react';
import {ScrollView} from 'react-native';
// library
import dayjs from 'dayjs';
// assets, utils, realm
import {useRecoilState} from 'recoil';
import {recordState} from '../recoil/atoms.ts';
import {readAllRecord} from '../realm/recordRealmFunctions.ts';
// component
import YearBusinessAmount from '../components/home/YearBusinessAmount.tsx';
import MonthCalendar from '../components/home/MonthCalendar.tsx';
import MonthBusinessAmount from '../components/home/MonthBusinessAmount.tsx';
import MonthRecordInfo from '../components/home/MonthRecordInfo.tsx';
import CreateButton from '../components/home/CreateButton.tsx';
// style
import {Home as Style} from '../styles/home.styles.ts';
const Home = () => {
const currentMonth = dayjs().format('YYYY-MM'); // 현재 달: 년-월
const [selectMonth, setSelectMonth] = useState(currentMonth);
const [recordData, setRecordData] = useRecoilState(recordState);
useEffect(() => {
const data = readAllRecord();
setRecordData(data.map(record => ({...record})));
}, [setRecordData]);
return (
<Style.container>
<ScrollView>
{/* 연간 영업 금액 */}
<YearBusinessAmount />
{/* 월별 운행 기록 */}
<Style.bottomContainer>
{/* 월별 달력 */}
<MonthCalendar
currentMonth={currentMonth}
selectMonth={selectMonth}
setSelectMonth={setSelectMonth}
/>
{/* 이번달 영업 금액 */}
<MonthBusinessAmount monthTotalData={monthTotalData} />
{/* 이번달 운행 정보 */}
<MonthRecordInfo monthTotalData={monthTotalData} />
</Style.bottomContainer>
</ScrollView>
{/* create 버튼 */}
<CreateButton />
</Style.container>
);
};
export default Home;
현재 달이면 다음달 이동 못하게 하기
이 기능은 월별 달력인 MonthCalendar에서 구현하겠습니다.
{/* 월별 달력 */}
<MonthCalendar
currentMonth={currentMonth}
selectMonth={selectMonth}
setSelectMonth={setSelectMonth}
/>
구현해야 될 기능
- 현재 달이면 다음 달 이동 못하게 하기
- 현재 달이면 다음 달로 넘기기 svg색상을 회색으로 변경하기
1번은 TouchableOpacity에 disabled 속성이 있어 현재달일 때
2번은 기존에 SvgXml에 설정해 주었습니다.
구현 코드
components/home/monthCalendar.tsx
// react, react-native
import React, {Dispatch, SetStateAction} from 'react';
import {View} from 'react-native';
import {SvgXml} from 'react-native-svg';
// library
import dayjs from 'dayjs';
// assets
import {svg} from '../../assets/svg';
// style
import {MonthCalendar as Style} from '../../styles/home.styles';
import Theme from '../../styles/Theme';
interface PropsType {
currentMonth: string; // 현재 달
selectMonth: string;
setSelectMonth: Dispatch<SetStateAction<string>>;
}
// 다음달 확인 못하게 막기
const MonthCalendar = ({
currentMonth,
selectMonth,
setSelectMonth,
}: PropsType) => {
// 이전 달력으로 이동
const onPrevPress = () => {
const nextMonth = dayjs(selectMonth).subtract(1, 'month').format('YYYY-MM');
setSelectMonth(nextMonth);
};
// 다음 달력으로 이동
const onNextPress = () => {
const nextMonth = dayjs(selectMonth).add(1, 'month').format('YYYY-MM');
setSelectMonth(nextMonth);
};
// 현재 달로 이동
const onCurrentPress = () => {
setSelectMonth(currentMonth);
};
return (
<Style.container>
<Style.centerWrap>
{/* 이전달 이동 */}
<Style.iconButton onPress={onPrevPress}>
<View>
<SvgXml xml={svg.prev} />
</View>
</Style.iconButton>
<Style.text>{dayjs(selectMonth).format('YYYY년 MM월')}</Style.text>
{/* 다음달 이동 */}
<Style.iconButton
onPress={onNextPress}
disabled={currentMonth === selectMonth}>
<View>
<SvgXml
xml={svg.next}
fill={
currentMonth === selectMonth
? Theme.colors.grey
: Theme.colors.black
}
/>
</View>
</Style.iconButton>
</Style.centerWrap>
{/* 현재달 이동 */}
<Style.iconButton position={true} onPress={onCurrentPress}>
<View>
<SvgXml xml={svg.turn} />
</View>
</Style.iconButton>
</Style.container>
);
};
export default MonthCalendar;
주요 코드
<Style.iconButton
onPress={onNextPress}
disabled={currentMonth === selectMonth}>
<View>
<SvgXml
xml={svg.next}
fill={
currentMonth === selectMonth
? Theme.colors.grey
: Theme.colors.black
}
/>
</View>
</Style.iconButton>
현재달과 선택한 달이 같으면 현재달임으로 다음 달로 이동 못하게 해 주었습니다.
월간 운행 정보 데이터 구하기
월간 운행 정보 데이터는 MonthBusinessAmount, MonthRecordInfo에서 사용하기 때문에 Home.tsx에서 구현했습니다.
<<달마다 합산>>
operatingAmount : 총합 // 영업 금액
card : 총합 // 카드현금 : 총합 // 현금
lpgInjectionVolume : 총합 // LPG 주입량
lpgUnitPrice : 단가 총합 / 해당달의 개수 -> 반올림 // LPG 평균 단가
mileage : 총합 // 주행거리
businessDistance : 총합 // 영업거리 toll : 더하기 // 통행료
lpgChargeAmount : 주입량 총합 * LPG 평균 단가 // LPG 충전 금액
fuelEfficiency : 주행거리 총합 / LPG 주입량 총합 -> 반올림 // 연비
pgUsage : 주행거리 총합 / (주행거리 총합 / LPG 주입량 총합) -> 반올림 // LPG 사용량
이정보를 토대로 월간 운행 정보 데이터를 구해주었습니다.
구현 순서
- 선택한 달 데이터 필터(2024년 3월이면 2024년 3월 데이터만 뽑기)
- 1차로 총합만 구해도 되는 데이터 구하기
- 2차로 1차에서 구한 총합으로 나머지 운행 정보 구하기
const currentMonth = dayjs().format('YYYY-MM'); // 현재 달: 년-월
const [selectMonth, setSelectMonth] = useState(currentMonth);
const [recordData, setRecordData] = useRecoilState(recordState);
useEffect(() => {
const data = readAllRecord();
setRecordData(data.map(record => ({...record})));
}, [setRecordData]);
이전에 recoil을 이용하여 realm데이터를 recoil에 넣어주었기 때문에 recoil을 이용해서 구현하겠습니다.
1. 선택한 달 데이터 필터
// 선택한 달 데이터 필터
const selectMonthData = recordData.filter(data => {
return dayjs(data.date).format('YYYY-MM') === selectMonth;
});
filter을 이용하여 선택한 달과 같은 데이터를 필터 했습니다.
2. 1차로 총합만 구해도 되는 데이터 구하기
const monthTotalData = selectMonthData.reduce(
(acc, curr) => {
acc.card += curr.card; // 카드
acc.cash += curr.cash; // 현금
acc.lpgInjectionVolume += curr.lpgInjectionVolume; // LPG 주입량
acc.lpgUnitPrice += curr.lpgUnitPrice; // LPG 단가
acc.mileage += curr.mileage; // 주행거리
acc.businessDistance += curr.businessDistance; // 영업거리
acc.toll += curr.toll; // 통행료
acc.operatingAmount += curr.operatingAmount; // 영업 금액
return acc;
},
{
date: selectMonth, // 날짜
card: 0, // 카드
cash: 0, // 현금
lpgInjectionVolume: 0, // LPG 주입량
lpgUnitPrice: 0, // LPG 단가
mileage: 0, // 주행거리
businessDistance: 0, // 영업거리
toll: 0, // 통행료
operatingAmount: 0, // 영업금액
lpgChargeAmount: 0, // LPG 충전 금액
fuelEfficiency: 0, // 연비
lpgUsage: 0, // LPG 사용량
},
);
reducer를 이용해서 각 데이터의 총합을 구해주었습니다.
3. 2차로 1차에서 구한 총합으로 나머지 운행 정보 구하기
// LPG 평균 단가
monthTotalData.lpgUnitPrice = selectMonthData.length
? Math.round(monthTotalData.lpgUnitPrice / selectMonthData.length)
: 0;
// LPG 충전 금액
monthTotalData.lpgChargeAmount =
monthTotalData.lpgInjectionVolume * monthTotalData.lpgUnitPrice;
// 연비
monthTotalData.fuelEfficiency = monthTotalData.lpgInjectionVolume
? Math.round(monthTotalData.mileage / monthTotalData.lpgInjectionVolume)
: 0;
// LPG 사용량
monthTotalData.lpgUsage =
monthTotalData.mileage / monthTotalData.lpgInjectionVolume
? Math.round(
monthTotalData.mileage /
(monthTotalData.mileage / monthTotalData.lpgInjectionVolume),
)
: 0;
나머지 구해야 되는 값들은 집접대입해서 구해주었습니다.
나누기 한 값이 소수이면 안되기 때문에 반올림을 해주었고, 나눈 값이 0이면 인피니트가 나오기 때문에 나눈 값이 0이면 0으로 반환해 주었습니다.
전체 코드
screens/Home.tsx
// react, react-native
import React, {useEffect, useState} from 'react';
import {ScrollView} from 'react-native';
// library
import dayjs from 'dayjs';
// assets, utils, realm
import {useRecoilState} from 'recoil';
import {recordState} from '../recoil/atoms.ts';
import {readAllRecord} from '../realm/recordRealmFunctions.ts';
// component
import YearBusinessAmount from '../components/home/YearBusinessAmount.tsx';
import MonthCalendar from '../components/home/MonthCalendar.tsx';
import MonthBusinessAmount from '../components/home/MonthBusinessAmount.tsx';
import MonthRecordInfo from '../components/home/MonthRecordInfo.tsx';
import CreateButton from '../components/home/CreateButton.tsx';
// style
import {Home as Style} from '../styles/home.styles.ts';
const Home = () => {
const currentMonth = dayjs().format('YYYY-MM'); // 현재 달: 년-월
const [selectMonth, setSelectMonth] = useState(currentMonth);
const [recordData, setRecordData] = useRecoilState(recordState);
useEffect(() => {
const data = readAllRecord();
setRecordData(data.map(record => ({...record})));
}, [setRecordData]);
// 선택한 달 데이터 필터
const selectMonthData = recordData.filter(data => {
return dayjs(data.date).format('YYYY-MM') === selectMonth;
});
const monthTotalData = selectMonthData.reduce(
(acc, curr) => {
acc.card += curr.card; // 카드
acc.cash += curr.cash; // 현금
acc.lpgInjectionVolume += curr.lpgInjectionVolume; // LPG 주입량
acc.lpgUnitPrice += curr.lpgUnitPrice; // LPG 단가
acc.mileage += curr.mileage; // 주행거리
acc.businessDistance += curr.businessDistance; // 영업거리
acc.toll += curr.toll; // 통행료
acc.operatingAmount += curr.operatingAmount; // 영업 금액
return acc;
},
{
date: selectMonth, // 날짜
card: 0, // 카드
cash: 0, // 현금
lpgInjectionVolume: 0, // LPG 주입량
lpgUnitPrice: 0, // LPG 단가
mileage: 0, // 주행거리
businessDistance: 0, // 영업거리
toll: 0, // 통행료
operatingAmount: 0, // 영업금액
lpgChargeAmount: 0, // LPG 충전 금액
fuelEfficiency: 0, // 연비
lpgUsage: 0, // LPG 사용량
},
);
// LPG 평균 단가
monthTotalData.lpgUnitPrice = selectMonthData.length
? Math.round(monthTotalData.lpgUnitPrice / selectMonthData.length)
: 0;
// LPG 충전 금액
monthTotalData.lpgChargeAmount =
monthTotalData.lpgInjectionVolume * monthTotalData.lpgUnitPrice;
// 연비
monthTotalData.fuelEfficiency = monthTotalData.lpgInjectionVolume
? Math.round(monthTotalData.mileage / monthTotalData.lpgInjectionVolume)
: 0;
// LPG 사용량
monthTotalData.lpgUsage =
monthTotalData.mileage / monthTotalData.lpgInjectionVolume
? Math.round(
monthTotalData.mileage /
(monthTotalData.mileage / monthTotalData.lpgInjectionVolume),
)
: 0;
return (
<Style.container>
<ScrollView>
{/* 연간 영업 금액 */}
<YearBusinessAmount />
{/* 월별 운행 기록 */}
<Style.bottomContainer>
{/* 월별 달력 */}
<MonthCalendar
currentMonth={currentMonth}
selectMonth={selectMonth}
setSelectMonth={setSelectMonth}
/>
{/* 이번달 영업 금액 */}
<MonthBusinessAmount monthTotalData={monthTotalData} />
{/* 이번달 운행 정보 */}
<MonthRecordInfo monthTotalData={monthTotalData} />
</Style.bottomContainer>
</ScrollView>
{/* create 버튼 */}
<CreateButton />
</Style.container>
);
};
export default Home;
연간 영업 금액 구하기
월간 운행정보와 구현하는 방식이 비슷합니다.
YearBusinessAmount.tsx에 구현해 주었습니다.
구현 순서
- 현재 년도의 데이터 가져오기
- 현재 년도의 연간 영업 금액 구하기
1. 현재 년도의 데이터 가져오기
// 현재 년도의 데이터 가져오기
const selectYearData = recordData.filter(data => {
return dayjs(data.date).format('YYYY') === currentYear;
});
2. 현재 년도의 연간 영업 금액 구하기
// 현재 년도의 연간 영업 금액 구하기
const yearBusinessAmount = selectYearData.reduce((acc, curr) => {
return acc + curr.operatingAmount;
}, 0);
전체 코드
components/home/YearBusinessAmount.tsx
// react, react-native
import React from 'react';
// library
import {useRecoilValue} from 'recoil';
import dayjs from 'dayjs';
// recoil, utils
import {recordState} from '../../recoil/atoms';
import {numberCommas} from '../../utils/calculate';
// style
import {YearBusinessAmount as Style} from '../../styles/home.styles';
const MonthBusinessAmount = () => {
const currentYear = dayjs().format('YYYY');
const recordData = useRecoilValue(recordState);
// 현재 년도의 데이터 가져오기
const selectYearData = recordData.filter(data => {
return dayjs(data.date).format('YYYY') === currentYear;
});
// 현재 년도의 연간 영업 금액 구하기
const yearBusinessAmount = selectYearData.reduce((acc, curr) => {
return acc + curr.operatingAmount;
}, 0);
return (
<Style.container>
<Style.title>연간 영업 금액</Style.title>
<Style.valueText>{numberCommas(yearBusinessAmount)}원</Style.valueText>
</Style.container>
);
};
export default MonthBusinessAmount;
728x90
반응형