#1 UI 프로토 타입
위와 같이 총 3개의 화면을 하나의 네비게이션으로 묶어 화면을 작성해보도록 하겠습니다.
#2 payment screens
import React from 'react';
import { View } from 'react-native';
import styles from '../../common/styles/styled.screen';
import CarpoolInformationBlock from './components/CarpoolInformationBlock';
const CarpoolInformationScreen = () => {
return(
<View style={ styles.container }>
<CarpoolInformationBlock />
</View>
);
};
export default CarpoolInformationScreen;
import { useNavigation, useRoute } from '@react-navigation/native';
import Ionicons from 'react-native-vector-icons/Ionicons';
import React from 'react';
import { Text, View } from 'react-native';
import FullButton from '../../../common/FullButton';
import styles from '../styles/styled.carpool.block';
const CarpoolInformationBlock = () => {
const navigation = useNavigation();
const route = useRoute();
const { carpool } = route.params;
const toBilling = () => {
navigation.navigate("PaymentScreen", {
carpool
});
};
return (
<View style={ styles.block }>
<View style={ styles.content_block }>
<View style={ styles.location }>
<Text style={ styles.font }>
{ carpool.start_location.description }
</Text>
</View>
<View style={ styles.location }>
<Ionicons name="ios-arrow-down-sharp"/>
</View>
<View style={ styles.location }>
<Text style={ styles.font }>
{ carpool.dest_location.description }
</Text>
</View>
<View>
<Text>
{ carpool.start_time }
</Text>
</View>
</View>
<View style={ styles.button }>
<FullButton text="결제하기"
onPress={ toBilling }
/>
</View>
</View>
)
};
export default CarpoolInformationBlock;
import { StyleSheet } from "react-native";
import palette from "../../../utils/palette";
const styles = StyleSheet.create({
block: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center'
},
content_block: {
flex: 0.7,
margin: 10,
justifyContent: 'center',
alignItems: 'center'
},
location: {
flex: 0.1
},
button: {
flex: 0.3,
marginBottom: 10
},
font: {
fontWeight: 'bold',
shadowOpacity: 0
}
});
export default styles;
CarpoolInformationScreen은 예약하기 전 카풀의 출발지와 목적지, 요금 등을 확인하는 스크린입니다. 해당 스크린에서 카풀 이용을 위한 정보를 확인한 뒤 결제 페이지로 넘어가는 네비게이션 기능을 수행합니다.
import React from 'react';
import { View } from 'react-native';
import styles from '../../common/styles/styled.screen';
import PaymentForm from './components/PaymentForm';
const PaymentScreen = () => {
return(
<View style={ styles.container }>
<PaymentForm />
</View>
);
};
export default PaymentScreen;
import { useNavigation, useRoute } from '@react-navigation/native';
import React, { useEffect } from 'react';
import { Text, View } from 'react-native';
import { useSelector } from 'react-redux';
import FullButton from '../../../common/FullButton';
import { paymentHook } from '../../../modules/payment/payment.hooks';
import styles from '../styles/styled.payment.form';
import Toast from 'react-native-toast-message';
import Loading from '../../../common/Loading';
const PaymentForm = () => {
const navigation = useNavigation();
const route = useRoute();
const { carpool } = route.params;
const [billing, { loading }] = paymentHook.useBilling();
const toSuccessScreen = () => {
form.payment_name = carpool.dest_location.description;
form.user_nickname = user.nickname;
form.ride_info_id = carpool.ride_info_id;
const input = { ...form };
billing({
variables: {
input
}
}).then(response => {
if(response.data) {
navigation.navigate("SuccessPaymentScreen");
}
}).catch(error => {
Toast.show({
type: 'info',
text1: error.toString()
.split(":")[1],
position: 'bottom'
});
return;
});
};
const {
form,
user
} = useSelector(({
user,
payment
}) => ({
form: payment.billingInput,
user: user.user
}));
return (
<View style={ styles.block }>
{
loading ?
<Loading /> :
<View style={ styles.block }>
<View style={ styles.content_block }>
<View style={ styles.row }>
<View style={ styles.name }>
<Text style={ styles.boldFont }>
결제 수단
</Text>
</View>
<View style={ styles.label }>
<Text style={ styles.font }>
카드
</Text>
</View>
</View>
<View style={ styles.row }>
<View style={ styles.name }>
<Text style={ styles.boldFont }>
비용
</Text>
</View>
<View style={ styles.label }>
<Text>
3000
</Text>
</View>
</View>
<View style={ styles.row }>
<View style={ styles.name }>
<Text style={ styles.boldFont }>
결제명
</Text>
</View>
<View style={ styles.label }>
<Text>
{ carpool.dest_location.description }
</Text>
</View>
</View>
<View style={ styles.row }>
<View style={ styles.name }>
<Text style={ styles.boldFont }>
결제자 닉네임
</Text>
</View>
<View style={ styles.label }>
<Text>
{ user.nickname }
</Text>
</View>
</View>
</View>
<View style={ styles.button }>
<FullButton text="결제하기"
onPress={ toSuccessScreen }
/>
</View>
</View>
}
</View>
)
};
export default PaymentForm;
import { StyleSheet } from "react-native";
import palette from "../../../utils/palette";
const styles = StyleSheet.create({
block: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center'
},
row: {
flex: 0.15,
flexDirection: 'column',
marginTop: 4,
marginBottom: 4
},
name: {
flex: 1,
flexDirection: 'row'
},
label: {
flex: 1,
flexDirection: 'row'
},
boldFont: {
fontWeight: 'bold',
shadowOpacity: 0
}
});
export default styles;
Payment Screen은 결제를 진행하는 스크린입니다. billing Mutation을 이용하여 payment-service와 통신을 진행한 후 결제를 진행합니다.
import React from 'react';
import { View } from 'react-native';
import styles from '../../common/styles/styled.screen';
import SuccessPaymentForm from './components/SuccessPaymentForm';
const SuccessPaymentScreen = () => {
return (
<View style={ styles.container }>
<SuccessPaymentForm />
</View>
);
};
export default SuccessPaymentScreen;
import { CommonActions, useNavigation } from '@react-navigation/native';
import React from 'react';
import { Text, View } from 'react-native';
import FullButton from '../../../common/FullButton';
import styles from '../../../common/styles/styled.form';
const SuccessPaymentForm = () => {
const navigation = useNavigation();
const toMainScreen = e => {
navigation.dispatch(CommonActions.reset({
routes: [{ name: 'bottom-nav' }]
}));
};
return(
<View style={ styles.block }>
<View style={ styles.box }>
<Text>
성공적으로 예약이 완료되었습니다!
</Text>
<FullButton onPress={ toMainScreen }
text="메인 페이지로 가기"
/>
</View>
</View>
);
};
export default SuccessPaymentForm;
최종 결제가 완료된 후 결제가 성공했음을 알리는 스크린입니다.
import React from 'react';
import { createStackNavigator } from '@react-navigation/stack';
import HomeScreen from '../screens/home/HomeScreen';
import CarpoolDetailScreen from '../screens/home/CarpoolDetailScreen';
import DriverDetailScreen from '../screens/home/DriverDetailScreen';
import CarpoolInformationScreen from '../screens/payment/CarpoolInformationScreen';
import PaymentScreen from '../screens/payment/PaymentScreen';
import SuccessPaymentScreen from '../screens/payment/SuccessPaymentScreen';
const Stack = createStackNavigator();
const HomeNavigator = () => {
return (
<Stack.Navigator>
...
<Stack.Screen name="CarpoolInformationScreen"
component={ CarpoolInformationScreen }
/>
<Stack.Screen name="PaymentScreen"
component={ PaymentScreen }
/>
<Stack.Screen name="SuccessPaymentScreen"
component={ SuccessPaymentScreen }
/>
</Stack.Navigator>
);
};
export default HomeNavigator;
홈 네비게이터에 결제 스크린들을 등록하도록 하겠습니다.
스크린이 완성되었으니 apollo 모듈을 작성하여 payment-service와의 연결을 진행하도록 하겠습니다.
import { gql } from '@apollo/client';
export const GET_PAYMENT = gql`
query GetPayment($payment_id: String!) {
getPayment(payment_id: $payment_id) {
payment_id,
payment_type,
cost,
payment_name,
user_nickname,
user_id,
ride_info_id,
status
}
}
`;
export const GET_PAYMENTS = gql`
query GetPayments {
getPayments {
payment_id,
payment_type,
cost,
payment_name,
user_nickname,
user_id,
ride_info_id,
status
}
}
`;
payment-service에서 작성했던 결제 데이터들을 가져오는 쿼리입니다.
import { gql } from "@apollo/client";
export const BILLING = gql`
mutation Billing($input: BillingInput!) {
billing(input: $input)
}
`;
export const CANCEL_BILLING = gql`
mutation CancelBilling($input: CancelInput!) {
cancelBilling(input: $input)
}
`;
export const RE_BILLING = gql`
mutation ReBilling($input: ReBillingInput!) {
reBilling(input: $input)
}
`;
결제, 결제 취소, 재결제를 위한 뮤테이션입니다.
import { useLazyQuery, useMutation, useQuery } from "@apollo/client"
import { BILLING, CANCEL_BILLING, RE_BILLING } from "./mutations"
import { GET_PAYMENT, GET_PAYMENTS } from "./queries";
export const paymentHook = {
useBilling: () => {
return useMutation(BILLING);
},
useCancelBilling: () => {
return useMutation(CANCEL_BILLING);
},
useReBilling: () => {
return useMutation(RE_BILLING);
},
useGetPayment: payment_id => {
return useLazyQuery(GET_PAYMENT, {
variables: {
payment_id
}
});
},
useGetPayments: () => {
return useQuery(GET_PAYMENTS);
}
};
아폴로 모듈을 작성했으니 테스트를 진행해보도록 하겠습니다
{
"_id": {
"$oid": "61dd055b4a3b9e5e990b46fe"
},
"ride_info_id": "IC-eg4gj5uwi-f6scvct9r75",
"rider_id": "cd9f524e-2609-4acc-95f6-abde4df6b479",
"passengers": [
"06ad51a9-1d0c-4b4a-87a4-aa672407d763"
],
"start_time": "2022. 1. 11. 오후 11:14:33",
"start_location": {
"description": "서울특별시 용산구 이태원1동",
"latitude": "37.5400456",
"longitude": "126.9921017"
},
"dest_location": {
"description": "서울특별시 성동구 옥수동",
"latitude": "37.54359350000001",
"longitude": "127.0134664"
},
"current_location": {
"latitude": null,
"longitude": null
},
"status": "PENDING",
"cost": 9000,
"car": {
"car_name": "소나타",
"car_number": "29허1002",
"car_size": "중형"
},
"__v": 0
}
테스트 스크린과 데이터 전부 잘 나오는 모습을 볼 수 있습니다. 다음 글에서는 마이페이지를 작성하고, 미결제 부분과 결제 취소를 처리하도록 하겠습니다.