HTML에서 페이지를 전환할 경우 anchor 태그(<a>)를 이용하여 쉽게 페이지 이동 가능하지만 React를 이용할 때는 조금 복잡하고 또 React Native의 경우는 React보다 조금은 더 복잡한 것 같습니다.
React Native의 경우 기본적으로 페이지 전환은 react navigation 라이브러리를 이용합니다.
그리고 react navigation 라이브러리 안에서 가장 많이 쓰이는 하위 라이브러리는 drawer, bottom-tabs, stack 이 셋이 아닐까 생각합니다.
그리고 이 셋을 모두 포함하는 앱 디자인을 구성해 보도록 하겠습니다.
먼저 Stack Navigation 입니다.
1. Stack Navigation
먼저 npx react-native init testApp으로 통해 testApp이란 이름의 앱을 하나 만듭니다.
그리고 필수 라이브러리를 설치해 줍니다. (아래 i 는 install의 줄임말입니다. 둘 다 사용 가능)
npm i @react-navigation/native (모든 navigation 라이브러리를 사용하기 위한 필수 라이브러리)
npm i react-native-screens react-native-safe-area-context (dependencies 라고 선행(보조/배경)되는 라이브러리)
npm i @react-navigation/native-stack (스택 네비게이션)
(설치한 모든 라이브러리는 package.json 파일의 dependencies항목에서 볼 수 있습니다.)
이제 코딩을 봅시다. 가장 기본적인 App.js 화면을 봅니다.
import React from 'react';
import { View, Text} from 'react-native';
const App = () => {
return (
<View>
<Text>Hello React Navigation!</Text>
</View>
);
};
export default App;
가장 기본적인 화면을 하나 만듭니다.
- 라이브러리 import(@react-navigation/native & @react-navigation/native-stack)
- Frame 함수 및 각 페이지 함수 생성 : 3개 페이지(Page1, Page2, Page3라는 이름의 함수로 만들겠습니다.)
import React from 'react';
import { View, Text} from 'react-native';
import { NavigationContainer } from '@react-navigation/native'; // 네비게이션 컨테이너
import { createNativeStackNavigator } from '@react-navigation/native-stack'; // Stack 네비게이션
const Page1 = () => {
return (
<View style={{flex:1, backgroundColor:"lightgreen"}}>
<Text style={{fontSize:25}}>Page1</Text>
</View>
)
}
const Page2 = () => {
return (
<View style={{flex:1, backgroundColor:"gold"}}>
<Text style={{fontSize:25}}>Page2</Text>
</View>
)
}
const Page3= () => {
return (
<View style={{flex:1, backgroundColor:"steelblue"}}>
<Text style={{fontSize:25}}>Page2</Text>
</View>
)
}
const Stack = createNativeStackNavigator(); // Stack Navigation함수를 Stack변수명으로 저장
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="P1" component={Page1} />
<Stack.Screen name="P2" component={Page2} />
<Stack.Screen name="P3" component={Page3} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default App;
App 함수 위의 Stack 변수 저장은 필수적으로 통용되는 방식이니 잘 기억합시다. (drawer나 bottom-tab도 동일)
화면을 설명하면 맨 위의 흰색 부분은 Header부분으로 모든 페이지에 나타나는 부분입니다.
- Header Design의 경우는 <Stack.Screen />에 각종 Parameter를 입력하여 바꿀 수 있습니다.
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="P1" component={Page1} options={{
title: 'My home', //title
headerTitleAlign:'center', //header 위치
headerStyle:{backgroundColor: 'crimson',}, //header 배경색
headerTintColor: '#fff', //header title 색상
headerTitleStyle: {fontWeight: 'bold',}, //header 굵기
}}/>
<Stack.Screen name="P2" component={Page2} />
<Stack.Screen name="P3" component={Page3} />
</Stack.Navigator>
</NavigationContainer>
);
- Header를 삭제하기 위해서는 <Stack.Navigator /> 에 다음과 같이 추가합니다.
return (
<NavigationContainer>
<Stack.Navigator screenOptions={{headerShown: false}}>
<Stack.Screen name="P1" component={Page1} />
<Stack.Screen name="P2" component={Page2} />
<Stack.Screen name="P3" component={Page3} />
</Stack.Navigator>
</NavigationContainer>
);
Stack.Navigator에 들어가기 때문에 하나의 페이지 함수의 header만 삭제할 수는 없겠습니다. (일괄 적용)
Stack.Navigator안에 모든 페이지 함수에 일괄 적용되는 header style을 넣을 수도 있습니다.(일괄 적용)
return (
<NavigationContainer>
<Stack.Navigator screenOptions={{
title: 'My home', //title
headerTitleAlign:'center', //header 위치
headerStyle:{backgroundColor: 'crimson',}, //header 배경색
headerTintColor: '#fff', //header title 색상
headerTitleStyle: {fontWeight: 'bold',}, //header 굵기
}}>
<Stack.Screen name="P1" component={Page1} />
<Stack.Screen name="P2" component={Page2} />
<Stack.Screen name="P3" component={Page3} />
</Stack.Navigator>
</NavigationContainer>
);
그리고 header에 직접 <View>로 구성된 Custom Component를 넣을 수도 있습니다.
const HeaderSection = () => {
return (
<View style={{height: 40, width:'100%', backgroundColor:'navy', justifyContent:'center'}}>
<Text style={{alignSelf:'center', color:'white'}}>Header</Text>
</View>
)
}
const Stack = createNativeStackNavigator(); // Stack Navigation함수를 Stack변수명으로 저장
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator screenOptions={{
headerTitle: props => <HeaderSection/>,
}}>
<Stack.Screen name="P1" component={Page1} />
<Stack.Screen name="P2" component={Page2} />
<Stack.Screen name="P3" component={Page3} />
</Stack.Navigator>
</NavigationContainer>
);
};
그런데 결과가 조금 이상합니다. 제가 의도한 파란색 배경의 흰색 Header라는 글자가 들어간 <View> 주위로 흰색 바탕이 있습니다. 기본적으로 Header에 들어가있는 margin값이 있다는 것을 알 수 있습니다.
사실 이러한 부분은 공홈에도 잘 나와있지 않고 직접 찾아서 해결해야 합니다. 보니 기본적으로 설정된 높이값과 왼쪽에 기본적인 여백이 있다는 것을 추측해 볼 수 있습니다.
하지만 도저히 이 여백이나 기본 높이값을 수정할 수 있는지 찾다가 포기했습니다. 다만 추가로 headerStyle에 배경값을 넣어 내가 custom으로 만든 View와 배경색을 같게 하는것이 최선입니다...
return (
<NavigationContainer>
<Stack.Navigator screenOptions={{
headerTitle: props => <HeaderSection/>,
headerStyle:{backgroundColor:'navy'} // 배경색만 추가합니다.
}}>
<Stack.Screen name="P1" component={Page1} />
<Stack.Screen name="P2" component={Page2} />
<Stack.Screen name="P3" component={Page3} />
</Stack.Navigator>
</NavigationContainer>
);
이제 페이지 이동에 대해서 알아봅니다.
기본적으로 react native의 경우 모든 사용자의 액션은 onPress 명령을 통해 이루어 집니다. 그리고 onPress에서 알수 있듯이 button등에 적용됩니다. 하지만 button은 design 측면에서 제한이 많아 보통 TouchableOpacity를 이용합시다. 한마디로 누를수 있는 View라고 생각하면 됩니다.
앞서서 위와 같이 P1, P2, P3라는 이름을 가지고 있는 3개 페이지를 만들었는데 맨 앞에 있는 P1만 계속 보입니다. 이제 각각의 페이지에 다른 두 페이지로 이동할 수 있는 버튼과(TouchableOpacity를 이용) 뒤로가기 버튼 세가지를 만들어 봅니다.
import React from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { NavigationContainer } from '@react-navigation/native'; // 네비게이션 컨테이너
import { createNativeStackNavigator } from '@react-navigation/native-stack'; // Stack 네비게이션
const Page1 = ({navigation}) => {
return (
<View style={{flex:1, backgroundColor:"lightgreen"}}>
<Text style={{fontSize:25}}>Page1</Text>
<View style={{flexDirection:'row', justifyContent:'space-evenly'}}>
<TouchableOpacity style={{width:50, height:50, backgroundColor:"aqua"}}
onPress={() => navigation.navigate("P2")}>
<Text>P2</Text>
</TouchableOpacity>
<TouchableOpacity style={{width:50, height:50, backgroundColor:"aqua"}}
onPress={() => navigation.navigate("P3")}>
<Text>P3</Text>
</TouchableOpacity>
<TouchableOpacity style={{width:50, height:50, backgroundColor:"aqua"}}
onPress={() => navigation.goBack()}>
<Text>Back</Text>
</TouchableOpacity>
</View>
</View>
)
}
const Page2 = ({navigation}) => {
return (
<View style={{flex:1, backgroundColor:"gold"}}>
<Text style={{fontSize:25}}>Page2</Text>
<View style={{flexDirection:'row', justifyContent:'space-evenly'}}>
<TouchableOpacity style={{width:50, height:50, backgroundColor:"aqua"}}
onPress={() => navigation.navigate("P1")}>
<Text>P1</Text>
</TouchableOpacity>
<TouchableOpacity style={{width:50, height:50, backgroundColor:"aqua"}}
onPress={() => navigation.navigate("P3")}>
<Text>P3</Text>
</TouchableOpacity>
<TouchableOpacity style={{width:50, height:50, backgroundColor:"aqua"}}
onPress={() => navigation.goBack()}>
<Text>Back</Text>
</TouchableOpacity>
</View>
</View>
)
}
const Page3= ({navigation}) => {
return (
<View style={{flex:1, backgroundColor:"steelblue"}}>
<Text style={{fontSize:25}}>Page3</Text>
<View style={{flexDirection:'row', justifyContent:'space-evenly'}}>
<TouchableOpacity style={{width:50, height:50, backgroundColor:"aqua"}}
onPress={() => navigation.navigate("P1")}>
<Text>P1</Text>
</TouchableOpacity>
<TouchableOpacity style={{width:50, height:50, backgroundColor:"aqua"}}
onPress={() => navigation.navigate("P2")}>
<Text>P2</Text>
</TouchableOpacity>
<TouchableOpacity style={{width:50, height:50, backgroundColor:"aqua"}}
onPress={() => navigation.goBack()}>
<Text>Back</Text>
</TouchableOpacity>
</View>
</View>
)
}
const Stack = createNativeStackNavigator(); // Stack Navigation함수를 Stack변수명으로 저장
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="P1" component={Page1} />
<Stack.Screen name="P2" component={Page2} />
<Stack.Screen name="P3" component={Page3} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default App;
페이지 이동을 위해서는 먼저 각 페이지 함수를 불러올 때 parameter부분에 { navigation }을 추가합니다. 다른 페이지로 이동할 수 있는 함수를 먼저 전달해 줍니다.
그리고 위와 같이 onPress={() => navigation.navigate("이동 페이지 name")}을 추가해 주면 됩니다. 그리고 각 페이지의 세번째 버튼인 뒤로가기는 onPress={() => navigation.goBack()} 을 이용합니다.
(첫번째 페이지의 경우 최초 페이지이기 때문에 navigation.goBack 이 안통하는 것을 알 수 있습니다.)
단순히 페이지 이동 뿐만이 아니라 새로운 페이지(함수)에 값도 넘겨줄 때는 onPress={() => navigation.navigate("페이지 name", { key : value }} 와 같이 오브젝트 형태로 넘겨주고 그 값을 받는 페이지(함수)의 경우 함수 parameter 부분에 추가로 { navigation , route } 로 인자값을 추가하며 이는 route.params.key값 으로 value값을 불러올 수 있습니다.
실제로 page1에서 값들을 page2나 page3으로 넘겨봅시다.
import React from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { NavigationContainer } from '@react-navigation/native'; // 네비게이션 컨테이너
import { createNativeStackNavigator } from '@react-navigation/native-stack'; // Stack 네비게이션
const Page1 = ({navigation}) => {
return (
<View style={{flex:1, backgroundColor:"lightgreen"}}>
<Text style={{fontSize:25}}>Page1</Text>
<View style={{flexDirection:'row', justifyContent:'space-evenly'}}>
<TouchableOpacity style={{width:50, height:50, backgroundColor:"aqua"}}
onPress={() => navigation.navigate("P2", {smile : "😊"})}>
<Text>P2</Text>
</TouchableOpacity>
<TouchableOpacity style={{width:50, height:50, backgroundColor:"aqua"}}
onPress={() => navigation.navigate("P3", {sunGlasses : "😎"})}>
<Text>P3</Text>
</TouchableOpacity>
<TouchableOpacity style={{width:50, height:50, backgroundColor:"aqua"}}
onPress={() => navigation.goBack()}>
<Text>Back</Text>
</TouchableOpacity>
</View>
</View>
)
}
const Page2 = ({navigation, route}) => {
return (
<View style={{flex:1, backgroundColor:"gold"}}>
<Text style={{fontSize:25}}>Page2</Text>
<View style={{flexDirection:'row', justifyContent:'space-evenly'}}>
<TouchableOpacity style={{width:50, height:50, backgroundColor:"aqua"}}
onPress={() => navigation.navigate("P1")}>
<Text>P1</Text>
</TouchableOpacity>
<TouchableOpacity style={{width:50, height:50, backgroundColor:"aqua"}}
onPress={() => navigation.navigate("P3")}>
<Text>P3</Text>
</TouchableOpacity>
<TouchableOpacity style={{width:50, height:50, backgroundColor:"aqua"}}
onPress={() => navigation.goBack()}>
<Text>Back</Text>
</TouchableOpacity>
</View>
<View>
<Text style={{fontSize:50}}>
{route.params.smile}
</Text>
</View>
</View>
)
}
const Page3= ({navigation, route}) => {
return (
<View style={{flex:1, backgroundColor:"steelblue"}}>
<Text style={{fontSize:25}}>Page3</Text>
<View style={{flexDirection:'row', justifyContent:'space-evenly'}}>
<TouchableOpacity style={{width:50, height:50, backgroundColor:"aqua"}}
onPress={() => navigation.navigate("P1")}>
<Text>P1</Text>
</TouchableOpacity>
<TouchableOpacity style={{width:50, height:50, backgroundColor:"aqua"}}
onPress={() => navigation.navigate("P2")}>
<Text>P2</Text>
</TouchableOpacity>
<TouchableOpacity style={{width:50, height:50, backgroundColor:"aqua"}}
onPress={() => navigation.goBack()}>
<Text>Back</Text>
</TouchableOpacity>
</View>
<View>
<Text style={{fontSize:50}}>
{route.params.sunGlasses}
</Text>
</View>
</View>
)
}
const Stack = createNativeStackNavigator(); // Stack Navigation함수를 Stack변수명으로 저장
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="P1" component={Page1} />
<Stack.Screen name="P2" component={Page2} />
<Stack.Screen name="P3" component={Page3} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default App;
각각 페이지에 웃는 이모티콘과 선글라스를 쓴 이모티콘을 넘겨준 예시입니다. 😁
사실 한 포스트에 전부 담으려 했는데 drawer와 bottom-tab은 다음 포스트로...