오늘은 <처음 배우는 리액트 네이티브> 8장 내비게이션에 대해 알아보겠다 😁
Navigation
리액트 네이티브에서는 네비게이션 기능을 지원하지 않아서 외부 라이브러리인 리액트 네비게이션 라이브러리를 이용해야 한다.
1. 리액트 네비게이션
리액트 네비게이션에서 지원하는 네비게이션의 종류는 스택(stack) 네비게이션, 탭(tab) 네비게이션, 드로어(drawer) 네비게이션 세 종류이다. 이 포스트에서는 리액트 네비게이션5 버전을 기준으로 진행할 것이다.
1) 네비게이션의 종류
1️⃣ NavigationContainer
컴포넌트
네비게이션의 계층구조와 상태를 관리하는 컨테이너 역할을 하며, 모든 네비게이션 구성 요소를 감싼 최상위 컴포넌트이다.
2️⃣ Navigator
컴포넌트
화면을 관리하는 중간 관리자 역할로 네비게이션을 구성하며, 여러 개의 Screen
컴포넌트를 자식 컴포넌트로 가진다.
3️⃣ Screen
컴포넌트Screen
컴포넌트는 화면으로 사용되는 컴포넌트로 name
과 component
속성을 지정해야 한다.
name
은 화면 이름으로 사용되고component
에는 화면으로 사용될 컴포넌트를 전달한다.- 화면으로 사용되는 컴포넌트에는 항상
navigation
과route
가prop
으로 전달된다.
2) 설정 우선 순위
리액트 네비게이션에서 설정할 수 있는 다양한 속성을 수정하는 방법은 다음 세 가지 방법이 있다.
1️⃣ Navigator
컴포넌트의 속성을 수정하는 방법
2️⃣ Screen
컴포넌트의 속성을 수정하는 방법
3️⃣ 화면으로 사용되는 컴포넌트의 props
로 전달되는 navigation
을 이용해서 수정하는 방법
2️⃣번과 3️⃣번 방법은 해당 화면에만 적용되지만, 1️⃣번 방법은 자식 컴포넌트로 존재하는 모든 컴포넌트에 적용된다.
설정 우선 순위는
1️⃣ Navigator
컴포넌트의 속성을 수정하는 방법
2️⃣ Screen
컴포넌트의 속성을 수정하는 방법
3️⃣ 화면으로 사용되는 컴포넌트의 props
로 전달되는 navigation
을 이용해서 수정하는 방법
순으로 높아진다. 즉 props
로 전달되는 navigation
을 이용하는 방법의 우선순위가 가장 높다.
3) 라이브러리 설치
리액트 네비게이션 라이브러리 설치
npm install --save @react-navigation/native
expo install react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context @react-native-community/masked-view
2. 스택 네비게이션
1) 화면 구성
- 스택 네비게이션에 필요한 라이브러리 설치하기
npm install @react-navigation/stack
스택 네비게이션은 가장 많이 사용되는 네비게이션으로, 현재 화면 위에 다른 화면을 쌓으면서 화면을 이동한다. 예를 들면 채팅 애플리케이션에서 채팅방에 입장하는 상황이나 여러 목록 중에서 특정 항목의 상세 화면으로 이동할 때 많이 사용된다.
스택 네비게이션은 화면 위에 새로운 화면을 쌓으면서(push) 이동하기 때문에 이전 화면을 계속 유지한다. 가장 위에 있는 화며을 들어내면(pop) 이전 화면으로 돌아갈 수 있다.
createStackNavigator
함수로 스택 네비게이션 생성하기
src/navigation/Stack.js
import React from 'react';
import { createStackNavigator } from '@react-navigation/stack';
import Home from '../screens/Home';
import List from '../screens/List';
import Item from '../screens/Item';
const Stack = createStackNavigator();
const StackNavigation = () => {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="List" component={List} />
<Stack.Screen name="Item" component={Item} />
</Stack.Navigator>
);
};
export default StackNavigation;
생성된 스택 네비게이션에는 화면을 구성하는 Screen
컴포넌트와 Screen
컴포넌트를 관리하는 Navigator
컴포넌트가 있다.
✅ name
에는 화면의 이름을 작성하는데, 이때 Screen
컴포넌트의 name
은 반드시 서로 다른 값을 가져야 한다.
2) 화면 이동
Screen
컴포넌트의 component
로 지정된 컴포넌트는 화면으로 이용되고, navigation
이 props
로 전달된다.navigation
의 navigate
함수는 원하는 화면으로 이동하는 데 사용되는 함수이다.
이번에는 Home 화면에서 props
로 전달되는 navigation
을 이용해서 버튼을 클릭하면 List 화면으로 이동하도록 만들어보겠다. navigation
에 있는 navigate
함수를 이용해서 원하는 화면의 이름을 전달하면 해당 화면으로 이동한다.
✅ 이때 전달되는 화면의 이름은 Screen
컴포넌트의 name
값 중 하나를 입력해야 한다.
src/screens/Home.js
...
const Home = ({ navigation }) => (
return (
<Container>
<StyledText>Home</StyledText>
<Button
title="go to the list screen"
onPress={() => navigation.navigate('List')}
/>
</Container>
);
};
...
만약 이동하는 화면이 어떤 화면의 상세 화면이라면, 상세 화면은 어떤 내용을 렌더링해야 하는지 전달받아야 한다. navigate
함수를 이용할 때 두 번째 파라미터에 객체를 전달해서 이동하는 화면에 필요한 정보를 함께 전달한다.
navigate
함수를 이용하여 List 화면에서 목록을 클릭하면 해당 항목의 정보와 함께 Item 화면으로 이동하도록 해보겠다. Item 화면으로 이동할 때 항목의 id
와 name
을 함께 전달한다. 전달된 내용은 컴포넌트의 props
로 전달되는 route
의 params
를 통해 확인할 수 있다.
src/screens/List.js
const List = ({ navigation }) => {
const _onPress = item => {
navigation.navigate('Item', {id: item._id, name: item.name });
};
...
);
...
이번에는 Item 화면에서 전달되는 params
를 이용하여 화면에 항목의 id
와 name
을 출력해보겠다.
src/screens/Item.js
const Item = ({ route }) => {
return (
<Container>
<StyledText>Item</StyledText>
<StyledText>ID: {route.params.id}</StyledText>
<StyledText>Name: {route.params.name}</StyledText>
</Container>
);
};
...
3. 화면 배경색 수정
src/screens/Home.js
...
const Container = styled.View`
background-color: #ffffff;
align-items: center;
`;
...
cardStyle
을 이용해서 스택 네비게이션의 화면 배경색을 지정한다. 화면의 배경색은 일반적으로 동일하게 사용하므로, 화면마다 설정하기보다 Navigator
컴포넌트의 screenOptions
에 설정해서 전체에 적용하도록 한다.
src/navigations/Stack.js
const StackNavigation = () => {
return (
<Stack.Navigator
initialRouteName="Home"
screenOptions={{ cardStyle: {backgroundColor: '#ffffff'} }}
>
...
</Stack.Navigator>
);
4. 헤더 수정하기
스택 네비게이션의 헤더(header)는 뒤로 가기 버튼이나 타이틀(title)을 통해 현재 화면을 알려주는 역할을 한다.
타이틀 수정하기
1️⃣ name
의 속성 값 변경하기
헤더의 타이틀은 Screen
컴포넌트의 name
속성을 기본값으로 사용한다. name
을 원하는 값으로 수정해서 헤더의 타이틀을 수정한다. Item 화면을 나타내는 Screen
컴포넌트의 name
속성을 Detail로 변경했다.
src/navigations/Stack.js
const StackNavigation = () => {
return (
<Stack.Navigator
initialRouteName="Home"
screenOptions={{ cardStyle: {backgroundColor: '#ffffff'} }}
>
<Stack.Screen name="Detail" component={Item} />
</Stack.Navigator>
);
};
name
속성을 Detail로 변경함에 따라 원래 Item 화면으로 이동할 때 navigate
함수에 전달하는 첫 번째 파라미터 값도 변경되어야 한다. 이렇게 모든 name
속성을 이용한 곳을 수정해야 한다는 단점이 있다.
2️⃣ headerTitle
속성 이용하기Screen
컴포넌트의 options
를 이용해서 개별 화면 설정을 수정할 수 있다. headerTitle
속성을 이용해서 List 화면의 타이틀을 List Screen으로 변경했다.
src/navigations/Stack.js
const StackNavigation = () => {
return (
<Stack.Navigator
initialRouteName="Home"
screenOptions={{ cardStyle: {backgroundColor: '#ffffff'} }}
>
<Stack.Screen
name="List"
component={List}
options={{ headerTitle: 'List Screen' }}
/>
</Stack.Navigator>
);
};
모든 화면에서 같은 타이틀이 나타나도록 수정하는 방법은 Navigator
컴포넌트의 screen Options
속성에 headerTitle
을 지정하는 방법이 있다.
스타일 수정하기
헤더의 배경색 등을 수정하는 headStyle
과 헤더의 타이틀 컴포넌트의 스타일을 수정하는 headerTitleStyle
속성을 이용해서 헤더의 스타일을 수정할 수 있다.
headerStyle
을 이용해서 헤더의 스타일을 변경하고, headerTitleStyle
속성을 이용해서 타이틀의 스타일을 변경해보겠다. 그리고 안드로이드와 iOS에서 모두 동일하게 타이틀 정렬을 하기 위해 headerTitleAlign
속성을 이용한다. 이때 headerTitleAlign
속성의 값은 left
와 center
둘 중에 하나를 선택할 수 있다.
src/navigations/Stack.js
const StackNavigation = () => {
return (
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
cardStyle: {backgroundColor: '#ffffff'},
headerStyle: {
height: 110,
backgroundColor: '#95a5a6',
borderBottomWidth: 5,
borderBottomColor: '#34495e',
},
headerTitleStyle: {color: '#ffffff', fontSize: 24},
headerTitleAlign: 'center',
}}
>
...
</Stack.Navigator>
);
};
타이틀 컴포넌트 변경
타이틀에 문자열이 아닌 다른 것을 렌더링 하는 방법은 headerTitle
속성에 컴포넌트를 반환하는 함수를 지정하면 된다. headerTitle
에 함수가 설정되면 해당 함수의 파라미터로 style
과 tintColor
등이 포함된 객체가 전달된다.style
은 headerTitleStyle
에 설정된 값이고, tintColor
는 headerTintColor
에 지정된 값이 전달된다.
다음 코드는 vector-icons에서 제공하는 컴포넌트를 이용해서 리액트 로고가 렌더링 되도록 작성한 것이다.
src/navigations/Stack.js
...
import { MaterialCommunityIcons } from '@expo/vector-icons';
...
const StackNavigation = () => {
return (
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
cardStyle: {backgroundColor: '#ffffff'},
headerStyle: {
height: 110,
backgroundColor: '#95a5a6',
borderBottomWidth: 5,
borderBottomColor: '#34495e',
},
headerTitleStyle: {color: '#ffffff', fontSize: 24},
headerTitleAlign: 'center',
headerTitle: ({ style }) => (
<MaterialCommunityIcons name="react" style={style}/>
),
}}
>
...
</Stack.Navigator>
);
};
버튼 수정하기
헤더 왼쪽에 뒤로가기 버튼을 만들어보겠다. 안드로이드에서 뒤로가기 버튼은 버튼의 타이틀을 보여주지 않지만 iOS에서는 이전 화면의 타이틀을 버튼의 타이틀로 보여준다. headerBackTitleVisible
을 이용하면 두 플랫폼의 버튼 타이틀 렌더링 여부를 동일하게 설정할 수 있다. 이전 화면의 이름이 아닌 다른 값을 이용하고 싶은 경우 headerBackTitle
을 이용한다.
버튼 타이틀의 스타일은 headerBackTitleStyle
을 이용해서 글자의 색, 글자 크기 등 다양한 스타일을 지정할 수 있다.headerTintColor
에 지정된 색은 버튼뿐만 아니라 헤더의 타이틀에도 적용된다.
이때 우선순위는 headerBackTitleStyle
와 headerTitleStyle
이 headerTintColor
보다 높다.
src/navigations/Stack.js
...
import { MaterialCommunityIcons } from '@expo/vector-icons';
...
const StackNavigation = () => {
return (
<Stack.Navigator
...
>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen
name="List"
component={List}
options={{
headerTitle: 'List Screen',
headerBackTitleVisible: true,
headerBackTitle: 'Prev',
headerTitleStyle: {fontSize: 24},
headerTintColor: '#e74c3c',
}}
/>
</Stack.Navigator>
);
};
3. 탭 네비게이션
탭 네비게이션은 화면 위나 아래에 위치하며, 탭 버튼을 누르면 버튼과 연결된 화면으로 이동한다. 주로 채팅 어플리케이션에서 쉽게 확인할 수 있다.
1. 라이브러리 설치
npm install @react-navigation/bottom-tabs
2. 화면 구성
3개의 버튼과 해당 버튼에 연결된 화면으로 구성된 탭 네비게이션을 만드는 코드는 다음과 같다.
현재 화면을 확일할 수 있는 텍스트가 나타나는 간단한 컴포넌트 3개를 만들었다.
src/screens/TabScreens.js
import React from 'react';
import styled from 'styled-components/native';
const Container = style.View`
flex: 1;
justify-content: center;
align-items: center;
`;
const StyledText = styled.Text`
font-size: 30px;
`;
export const Mail = () => {
return (
<Container>
<StyledText>Mail</StyledText>
</Container>
);
};
export const Meet = () => {
return (
<Container>
<StyledText>Meet</StyledText>
</Container>
);
};
export const Settings = () => {
return (
<Container>
<StyledText>Settings</StyledText>
</Container>
);
};
위에서 만든 컴포넌트를 이용해서 탭 네비게이션을 만들어보겠다. crateBottomTabNavigatior
함수를 이용해 탭 네비게이션을 생성한다. 탭 네비게이션에도 스택 네비게이션과 동일하게 Navigator
컴포넌트, Screen
컴포넌트가 있다. 다음 코드를 작성하고 App 컴포넌트에서 탭 네비게이션을 사용하면 하단에 3개의 버튼이 놓인 탭 바가 있고, 탭의 버튼을 클릭할 때마다 화면이 변경되는 것을 확인할 수 있다.
첫번째로 렌더링되는 화면을 변경하고 싶은 경우 initialRouteName
속성을 이용한다.
src/screens/Tab.js
import React from 'react';
import { createBottomTabNavigator } from '...';
import { Mail, Meet, Settings } from '../screens/TabScreens';
const Tab = createBottomTabNavigator();
const TabNavigation = () => {
return (
<Tab.Navigator initialRouteName="Settings">
<Tab.Screen name="Mail" component={Mail} />
<Tab.Screen name="Meet" component={Meet} />
<Tab.Screen name="Settings" component={Settings} />
</Tab.Navigator>
);
};
export default TabNavigation;
3. 탭 바 수정하기
버튼 아이콘 설정하기
탭 네비게이션의 기본 설정은 탭 버튼 아이콘이 지정되어 있지 않다. tabBarIcon
을 이용해서 탭 버튼에 아이콘을 렌더링할 수 있다. tabBarIcon
에 컴포넌트를 반환하는 함수를 지정하면 버튼의 아이콘이 들어갈 자리에 해당 컴포넌트를 레더링한다. tabBarIcon
에 설정된 함수에는 color
, size
, focused
값을 포함한 객체가 파라미터로 전달된다.
src/navigatoins/Tab.js
...
import { MaterialCommunityIcons } from '@expo/vector-icons';
const TabIcon = ({ name, size, color }) => {
return <MaterialCommunityIcons name={name} size={size} color={color} />;
};
const Tab = createBottomTabNavigator();
const TabNavigation = () => {
return (
<Tab.Navigator initialRouteName="Settings">
<Tab.Screen
name="Mail"
component={Mail}
options={{
tabBarIcon: props => TabIcon({...props, name: 'email' })
/>
<Tab.Screen
name="Meet"
component={Meet}
options={{
tabBarIcon: props => TabIcon({...props, name: 'video' })
/>
<Tab.Screen
name="Settings"
component={Settings}
options={{
tabBarIcon: props => TabIcon({...props, name: 'settings' })
/>
</Tab.Navigator>
);
};
만약 Screen
컴포넌트마다 탭 버튼 아이콘을 지정하지 않고 한곳에서 모든 버튼의 아이콘을 관리하고 싶은 경우 Navigaotr
컴포넌트의 screenOptions
속성을 사용해서 관리할 수 있다.screenOptions
에 객체를 반환하는 함수를 설정하고 함수로 전달되는 route
를 이용한다.
버튼 아이콘 아래에 렌더링되는 버튼의 라벨(label)은 Screen
컴포넌트의 name
값을 기본값으로 사용한다. 탭 버튼의 라벨은 tabBarLabel
을 이용해서 변경한다.
src/navigatoins/Tab.js
...
import { MaterialCommunityIcons } from '@expo/vector-icons';
const TabIcon = ({ name, size, color }) => {
return <MaterialCommunityIcons name={name} size={size} color={color} />;
};
const Tab = createBottomTabNavigator();
const TabNavigation = () => {
return (
<Tab.Navigator
initialRouteName="Settings"
screenOptions={({ route }) => ({
tabBarIcon: props => {
let name='';
if (route.name === 'Mail') name = 'email';
else if (route.name === 'Meet') name = 'video';
else name = 'settings';
return TabIcon({ ...props, name });
},
})}
>
<Tab.Screen
name="Mail"
component={Mail}
options={{
tabBarLabel: 'Inbox',
tabBarIcon: props => TabIcon({...props, name: 'email' })
/>
...
</Tab.Navigator>
);
};
스타일 수정하기
탭 네비게이션의 탭 바 배경색은 흰색이 기본값이다.
먼저 화면의 배경색을 다음과 같이 변경한다.
src/screens/TabScreen.js
...
const Container = styled.View`
flex: 1,
justify-content: center,
align-items: center,
background-color: #54b7f9;
`;
const StyledText = styled.Text`
font-size: 30px;
color: #ffffff;
`;
...
탭 바의 스타일은 tabBarOptions
속성에 style
값으로 스타일 객체를 설정해서 변경한다. 탭 버튼의 아이콘은 선택되어 활성화된 상태의 색과 선택되지 않아 비활성 상태의 색을 각각 activeTintColor
와 inactiveTintColor
로 설정할 수있다. 만약 라벨이 렌더링되도록 설정되었다면 라벨의 색도 버튼의 활성화 상태에 따라 나타난다.barTabIcon
에 설정한 함수의 파라미터 focused
는 버튼의 선택된 상태를 나타내는 값이다. 아래 코드에서는 focused
의 값에 따라 버튼이 활성화되었을 때는 내부가 채워진 이미지가 렌더링되고, 비활성화 상태에서는 내부가 비 아이콘이 렌더링된다.
src/navigatoins/Tab.js
...
const TabNavigation = () => {
return (
<Tab.Navigator
initialRouteName="Settings"
tabBarOptions={{
...
activeTintColor: '#ffffff',
inactiveTintColor: '#cfcfcf',
}}
>
<Tab.Screen
name="Mail"
component={Mail}
options={{
tabBarLabel: 'Inbox',
tabBarIcon: props =>
TabIcon({
...props,
name: props.focused ? 'email' : 'email-outline',
}),
}}
/>
...
</Tab.Navigator>
);
};
'Develop > React Native' 카테고리의 다른 글
[React Naitve] 리액트 네이티브 앱에서 Firebase 시작하기 (인증, 데이터베이스, 스토리지) (0) | 2022.01.19 |
---|---|
[React Native] 리액트 네이티브로 💬채팅앱 만들기 (feat. firebase) (0) | 2022.01.19 |
[React Native] 리액트 네이티브에서 전역 상태 관리하는 방법 - Context API, useContext (0) | 2021.11.14 |
[React Native] 리액트 네이티브 Hooks (useState, useEffect, useRef, useMemo) (0) | 2021.11.14 |
[React Native] 리액트 네이티브로 ✅TODO List 만들기 (0) | 2021.11.14 |