1. 필요한 라이브러리를 설치합니다.
라이브러리 이름
@react-navigation/native | React Native에서 네비게이션 기능을 제공 |
@react-navigation/bottom-tabs | 하단 탭 네비게이션을 위한 패키지 |
react-native-screens | 네이티브 화면 전환 성능 향상 |
react-native-safe-area-context | 안전 영역(노치, 상태바 등) 처리 |
react-native-vector-icons | 아이콘 사용을 위한 라이브러리 |
react-native-gesture-handler | 제스처(스와이프, 터치 등) 지원 |
react-native-reanimated | 애니메이션 성능 최적화 라이브러리 |
npm install @react-navigation/native @react-navigation/bottom-tabs react-native-screens react-native-safe-area-context react-native-vector-icons react-native-gesture-handler react-native-reanimated
react-native-reanimated는 네이티브 모듈을 활용하여 애니메이션을 최적화하는 라이브러리입니다. 이 라이브러리는 JIT(Just-In-Time) 컴파일러를 사용하지 않는 Hermes 엔진과 함께 작동할 때, Babel 플러그인을 추가적으로 설정해야 정상적으로 동작합니다.
react-native-reanimated 추가 설정
react-native-reanimated는 추가적인 설정이 필요합니다.
babel.config.js 파일을 열고 아래 내용을 추가하세요.
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: ['react-native-reanimated/plugin'],
};
2. react-navigation/bottom-tabs 라이브러리를 이용하여 하단탭 네비게이션을 생성합니다.
import React from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { MaterialCommunityIcons } from '@expo/vector-icons';
const Tab = createBottomTabNavigator();
const TabNavigation = () => {
return (
<Tab.Navigator>
<Tab.screen></Tab.screen>
<Tab.screen></Tab.screen>
<Tab.screen></Tab.screen>
</Tab.Navigator>
)
}
먼저 createBottomTavNavigator함수로 하단탭을 생성하고 이를 Tab 변수에 저장합니다.
Tab.Navigator 로 내비게이터를 설정하고 Tab.screen으로 각각 이동할 화면의 탭(화면에서는 버튼으로 보임)을 만듭니다.
3. screen 페이지를 만듭니다.
import React from 'react';
import { Button, StatusBar, Platform } from 'react-native';
import styled from 'styled-components/native';
import { SafeAreaProvider, SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
const Container = styled(SafeAreaView)`
flex: 1;
align-items: center;
background-color: #ffffff;
`;
const StyledText = styled.Text`
font-size: 30px;
margin-bottom: 10px;
`;
const Home = ({ navigation }) => {
const insets = useSafeAreaInsets();
return (
<SafeAreaProvider>
<StatusBar barStyle="dark-content" backgroundColor="#ffffff" />
<Container style={{ paddingTop: Platform.OS === 'android' ? StatusBar.currentHeight : insets.top }}>
<StyledText>Home</StyledText>
<Button title="Go to the List Screen" onPress={() => navigation.navigate('List')} />
</Container>
</SafeAreaProvider>
);
};
export default Home;
import React from 'react';
import { Button, StatusBar, Platform } from 'react-native';
import styled from 'styled-components/native';
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
// 스타일 정의
const Container = styled(SafeAreaView)`
flex: 1;
align-items: center;
justify-content: center;
background-color: #ffffff;
`;
const StyledText = styled.Text`
font-size: 30px;
margin-bottom: 10px;
`;
// 🚀 Mypage 컴포넌트
const Mypage = ({ navigation }) => {
const insets = useSafeAreaInsets();
return (
<>
<StatusBar barStyle="dark-content" backgroundColor="#ffffff" />
<Container style={{ paddingTop: Platform.OS === 'android' ? StatusBar.currentHeight : insets.top }}>
<StyledText>Mypage</StyledText>
<Button title="Go to Home" onPress={() => navigation.navigate('Home')} />
</Container>
</>
);
};
export default Mypage;
import React from 'react';
import { Button, StatusBar, Platform } from 'react-native';
import styled from 'styled-components/native';
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
const Container = styled(SafeAreaView)`
flex: 1;
align-items: center;
justify-content: center;
background-color: #ffffff;
`;
const StyledText = styled.Text`
font-size: 30px;
margin-bottom: 10px;
`;
// 🚀 Travel 컴포넌트
const Travel = ({ navigation }) => {
const insets = useSafeAreaInsets();
return (
<>
<StatusBar barStyle="dark-content" backgroundColor="#ffffff" />
<Container style={{ paddingTop: Platform.OS === 'android' ? StatusBar.currentHeight : insets.top }}>
<StyledText>Travel</StyledText>
<Button title="Go to Home" onPress={() => navigation.navigate('Home')} />
</Container>
</>
);
};
export default Travel;
4. screens에 각 컴포넌트를 부여하고, 이름, 라벨 정하기
const TabNavigation = () => {
return (
<Tab.Navigator initialRouteName="Home">
<Tab.Screen
name="Home"
component={Home}
/>
<Tab.Screen
name="Travel"
component={Travel}
/>
<Tab.Screen
name="Mypage"
component={Mypage}
/>
</Tab.Navigator>
);
};
여기서 각 컴포넌트에 설정한 name에 따라 intialRouteName 처음 들어갔을 때 눌린 화면을 선택할 수 있습니다.
5. 눌렸는지 여부에 따라 다른 아이콘 뜨게 하기
const TabNavigation = () => {
return (
<Tab.Navigator initialRouteName="Settings">
<Tab.Screen
name="Mail"
component={Mail}
options={{
tabBarLabel: 'Inbox',
tabBarIcon: props => (
<TabIcon
{...props}
name={props.focused ? 'email' : 'email-outline'}
/>
),
}}
/>
<Tab.Screen
name="Meet"
component={Meet}
options={{
tabBarIcon: props => (
<TabIcon
{...props}
name={props.focused ? 'video' : 'video-outline'}
/>
),
}}
/>
<Tab.Screen
name="Settings"
component={Settings}
options={{
tabBarIcon: props => (
<TabIcon
{...props}
name={props.focused ? 'settings' : 'settings-outline'}
/>
),
}}
/>
</Tab.Navigator>
);
};
focused여부에 따라 다른 아이콘을 보여줄 수 있습니다.
6. Navigator 스타일 주기
<Tab.Navigator
initialRouteName="Home"
screenOptions={{
tabBarActiveTintColor: '#000000',
tabBarInactiveTintColor: '#1D1D1F',
tabBarStyle: {
backgroundColor: '#ffffff', // 탭 바 배경색 설정
borderTopColor: '#ffffff', // 탭 바 경계선 색상
borderTopWidth: 1, // 탭 바 경계선 두께
height: 70,
paddingTop: 5,
},
}}
>
Navigator에서는 전체 적인 navigation에 스타일을 줄 수 있습니다.
TabIcon넣기
// tab icon의 크기, 색상, 이름 format 맞추기
const TabIcon = ({ source, size }) => {
return (
<Image
source={source}
style={{ width: size, height: size, resizeMode: 'contain' }}
/>
);
};
아이콘을 일관되게 렌더링할 수 있도록 공통 TabIcon 컴포넌트를 정의하고 이를 사용합니다.
7. 라벨 focused에 따라 바꾸기?
라벨은 focused여부에 따라 바꾸기 어렵습니다. Text를 이용하여 커스터마이즈 하는 방식을 사용하면 focused 여부에 따라 글자색이나 크기, fontWeight등을 설정할 수 있습니다.
import React from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { Image, Text, TouchableOpacity, View } from 'react-native';
import Home from '../screens/Home';
import Travel from '../screens/Travel';
import Mypage from '../screens/Mypage';
import { theme } from "../theme";
const Tab = createBottomTabNavigator();
// tab icon의 크기, 색상, 이름 format 맞추기
const TabIcon = ({ source, size }) => {
return (
<Image
source={source}
style={{ width: size, height: size, resizeMode: 'contain' }}
/>
);
};
const BottomTab = () => {
return (
<Tab.Navigator
initialRouteName="Home"
screenOptions={{
tabBarActiveTintColor: '#000000',
tabBarInactiveTintColor: '#1D1D1F',
tabBarStyle: {
backgroundColor: '#ffffff', // 탭 바 배경색 설정
borderTopColor: '#ffffff', // 탭 바 경계선 색상
borderTopWidth: 1, // 탭 바 경계선 두께
height: 70,
paddingTop: 5,
},
}}
>
<Tab.Screen
name="Home"
component={Home}
options={{
tabBarIcon: ({ focused }) => (
<TabIcon
source={
focused
? require('../assets/icons/bottom-tab-home-filled.png')
: require('../assets/icons/bottom-tab-home.png')
}
size={30}
/>
),
tabBarButton: (props) => (
<TouchableOpacity {...props}>
<View style={{ alignItems: 'center' }}>
<TabIcon
source={
props.accessibilityState.selected
? require('../assets/icons/bottom-tab-home-filled.png')
: require('../assets/icons/bottom-tab-home.png')
}
size={30}
/>
<Text
style={{
fontSize: 12,
color: props.accessibilityState.selected
? '#000000' // 선택된 탭 글자 색
: '#1D1D1F', // 비선택된 탭 글자 색
fontWeight:props.accessibilityState.selected
? 'bold'
: 'medium',
paddingTop: 5,
}}
>
홈
</Text>
</View>
</TouchableOpacity>
),
}}
/>
<Tab.Screen
name="Travel"
component={Travel}
options={{
tabBarIcon: ({ focused }) => (
<TabIcon
source={
focused
? require('../assets/icons/bottom-tab-travel-filled.png')
: require('../assets/icons/bottom-tab-travel.png')
}
size={30}
/>
),
tabBarButton: (props) => (
<TouchableOpacity {...props}>
<View style={{ alignItems: 'center' }}>
<TabIcon
source={
props.accessibilityState.selected
? require('../assets/icons/bottom-tab-travel-filled.png')
: require('../assets/icons/bottom-tab-travel.png')
}
size={30}
/>
<Text
style={{
fontSize: 12,
color: props.accessibilityState.selected
? '#000000'
: '#1D1D1F',
fontWeight:props.accessibilityState.selected
? 'bold'
: 'medium',
paddingTop: 5,
}}
>
여행기록
</Text>
</View>
</TouchableOpacity>
),
}}
/>
<Tab.Screen
name="Mypage"
component={Mypage}
options={{
tabBarIcon: ({ focused }) => (
<TabIcon
source={
focused
? require('../assets/icons/bottom-tab-my-filled.png')
: require('../assets/icons/bottom-tab-my.png')
}
size={30}
/>
),
tabBarButton: (props) => (
<TouchableOpacity {...props}>
<View style={{ alignItems: 'center' }}>
<TabIcon
source={
props.accessibilityState.selected
? require('../assets/icons/bottom-tab-my-filled.png')
: require('../assets/icons/bottom-tab-my.png')
}
size={30}
/>
<Text
style={{
fontSize: 12,
color: props.accessibilityState.selected
? '#000000'
: '#1D1D1F',
fontWeight:props.accessibilityState.selected
? 'bold'
: 'medium',
paddingTop: 5,
}}
>
MY
</Text>
</View>
</TouchableOpacity>
),
}}
/>
</Tab.Navigator>
);
};
export default BottomTab;
'대외활동 > 멋쟁이사자처럼_프론트엔드 12기' 카테고리의 다른 글
navigation not a function 오류 DRACONIST (0) | 2025.03.16 |
---|---|
task :app:checkdebugduplicateclasses failed 오류 ReactNative DRACONIST (0) | 2025.03.07 |
트러블 슈팅. Command failed with exit code 1: gradlew.bat app: DRACONIST (0) | 2025.02.15 |
REACT NATIVE 스터디. 4주차-9장. 채팅 어플리케이션2 메인화면 DRACONIST (1) | 2025.02.06 |
REACT NATIVE 스터디. 4주차-9장. 채팅 어플리케이션 DRACONIST (0) | 2025.02.06 |