[리액트 네이티브] 스택(stack) 내비게이션, 탭(tab) 내비게이션
my code archive
article thumbnail
반응형

일반적으로 모바일 애플리케이션은 하나의 화면이 아닌, 다양한 화면이 상황에 맞게 전환되면서 나타남.

=> 내비게이션(navigation)은 모바일 애플리케이션에서 가장 중요한 기능 중 하나임!!

=> 리액트 네이티브에서는 내비게이션을 지원하지 않는다. 외부 라이브러리를 이용해야 한다.

 

🤍리액트 내비게이션

  • 리액트 네이티브 애플리케이션의 내비게이션을 쉽고 간단하게 관리할 수 있도록 도와줌
  • 지원하는 내비게이션 종류 : 스택(stack) 내비게이션, 탭(tab) 내비게이션, 드로어(drawer) 내비게이션
  • 구조 : NavigationContainer, Navigator, Screen 컴포넌트
  • Screen 컴포넌트 : 화면으로 사용되는 컴포넌트로, name과 component 속성을 지정해야함. name은 화면 이름, component에는 화면으로 사용될 컴포넌트를 전달함.(화면으로 사용되는 컴포넌트에는 항상 navigaion과 route가 props로 전달됨.)
  • Navigator 컴포넌트 : 화면을 관리하는 중간 관리자 역할. 여러 개의 Screen 컴포넌트를 자식 컴포넌트로 갖고 있음.
  • NavigationContainer : 내비게이션의 계층 구조, 상태를 관리하는 컨테이너 역할. 모든 구성 요소를 감싼 최상위 컴포넌트.

🤍리액트 내비게이션 실습

프로젝트 생성

expo init react-native-navigaion	//프로젝트 생성
npm install styled-components	//스타일드 컴포넌트 라이브러리 설치

리액트 내비게이션 라이브러리 설치

//라이브러리 설치
npm install --save @react-navigaion/native

//종속성 설치
expo install react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context @react-native-community/masked-view

전체 디렉토리 구조

1. 스택 내비게이션(Stack Navigaion)

  • 가장 많이 사용되는 내비게이션
  • 현재 화면 위에 다른 화면을 쌓으며 화면을 이동하는 것이 특징임. ex) 채팅방 입장, 여러 목록 중 특정 항목의 상세 화면으로 이동
npm install @react-navigation/stack	//스택 라이브러리 설치

첫 화면 Home 컴포넌트, 다음 화면인 List 화면으로 이동하기 위한 버튼으로 구성되어 있음.

 const Home = () => {
     return(
        <Container>
            <StyledText>Home</StyledText>
             <Button title="go to the list screen" />
         </Container>
     );
 };

목록 화면으로 사용할 List 컴포넌트

const items = [
    {_id: 1, name: 'React Native'},
    {_id: 2, name: 'React Navigation'},
    {_id: 3, name: 'JavaScript'},
];

const List = () => {
    const _onPress = item => {};

    return(
        <Container>
            <StyledText>List</StyledText>
            {items.map(item=>(
                <Button
                    key={item._id}
                    title={item.name}
                    onPress={()=>_onPress(item)}
                />
            ))}
        </Container>
    );
};

export default List;

Item 컴포넌트에 임시 데이터를 생성 후,

상세 정보를 보여주는 Stack 컴포넌트 작성

=> Navigator 컴포넌트 안에 Screen 컴포넌트를 자식 컴포넌트로 작성, 앞에서 만든 컴포넌트를 Screen 컴포넌트의 component로 지정함. name에는 화면의 이름을 작성하며, Screen 컴포넌트의 name은 반드시 서로 다른 값을 가져야함!

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;

스택 내비게이션에서 첫 번째 화면으로 나오는 화면을 따로 지정하고 싶을 때에는 initialRouteName 속성을 사용함.

<Stack.Navigator initialRouteName="List">
...
</Stack.Navigator>

📱실행 화면

2-1. 화면 이동

  • Screen 컴포넌트의 component로 지정된 컴포넌트는 화면으로 이용되고, navigation이 props로 전달됨.
  • navigation의 다양한 기능 중 navigate 함수는 원하는 화면으로 이동하는 데에 사용됨.
  • 전달되는 화면 이름은 Screen 컴포넌트의 name 값 중 하나를 입력해야함!
/*
    Home 화면에서 props로 전달되는 navigation을 사용해서 버튼 클릭 시 List 화면으로 이동하도록
*/
const Home = ({navigation}) => {
    return(
        <Container>
            <StyledText>Home</StyledText>
            <Button title="go to the list screen"
            onPress={()=>navigation.navigate('List')}
            />
        </Container>
    );
};

📱실행 화면

2-2. 상세 화면 이동

  • 상세 화면은 어떤 내용을 렌더링해야 하는지 전달받아야함.
  • navigate 함수를 이용할 때 두 번째 파라미터에 객체를 전달해서 이동하는 화면에 필요한 정보를 함께 전달하는 기능이 있음.

Item 화면으로 이동하면서 항목의 id와 name을 전달하도록 작성. 전달된 내용은 컴포넌트의 props로 전달되는 route의 params를 통해 확인 가능함.

const List = ({navigation}) => {
    const _onPress = item => {
        //List 화면에서 목록 클릭 시 해당 항목의 정보와 함께 Item 화면으로 이동
        navigation.navigate('Detail',{id: item._id, name: item.name});
    };

Item 화면에서 전달되는 params를 이용해 화면에 항목의 id와 name 출력하기

📱실행 화면

3. 스타일 수정하기

헤더 스타일으 수정하는 속성은 헤더의 배경색을 수정하는 headerStyle, 헤더의 타이틀 컴포넌트의 스타일을 수정하는 headerTitleStyle이 있다.

<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} />
              ),
        }}
        >

📱실행 화면

  • 헤더의 타이틀을 변경하기 위해 문자열을 지정했던 headerTitle 속성에 컴포넌트를 반환하는 함수 지정 시, 타이틀 컴포넌트를 반환하는 컴포넌트로 변경할 수 있음.
  • headerTitle에 함수가 설정되면 해당 함수의 파라미터로 style, tintColor 등이 포함된 객체가 전달됨. => 그 중 style은 headerTitleStyle에 설정된 값, tintColor는 headerTintColor에 지정된 값이 전달된다.

vector-icons에서 제공하는 컴포넌트를 이용하여 리액트 로고가 랜더링되도록 작성.

📱실행 화면

headerLeft, headerRight에 컴포넌트를 변환함수를 지정하여 헤더 양 끝에 원하는 컴포넌트를 렌더링 해보기

  • useLayoutEffect Hook : useEffect Hook과 사용법이 동일하지만, 컴포넌트 업데이트된 직후 화면이 렌더링되기 전에 실행된다는 차이점이 있음. => 화면 렌더링하기 전에 변경할 부분이 있거나 수치 등을 측정해야 하는 상황에서 많이 사용됨.
  • headerLeft의 onPress는 뒤로 가기 버튼 기능이 전달됨.
  • headerRight 함수의 파라미터는 tintColor만 전달되므로 onPress에 원하는 행동을 정의해야함.
  • popToTop 함수는 현재 쌓여있는 모든 화면을 내보내고 첫 화면으로 돌아가는 기능.
const Item = ({navigation, route}) => {
    useLayoutEffect(()=>{
        navigation.setOptions({
            headerBackTitleVisible: false,
            headerTintColor: '#ffffff',
            headerLeft: ({onPress, tintColor})=>{
                return(
                    <MaterialCommunityIcons
                    name="keyboard-backspace"
                    size={30}
                    color={tintColor}
                    onPress={onPress}
                    />
                );
            },
            headerRight: ({tintColor})=>(
                <MaterialCommunityIcons
                    name="home-variant"
                    size={30}
                    style={{marginRight: 11}}
                    color={tintColor}
                    onPress={()=>navigation.popToTop()}
                    />
            ),
        });
    }, []);

📱실행 화면

4. 탭 내비게이션(Tab Navigation)

탭 내비게이션은 보통 화면 위 or 아래에 위치하며, 탭 버튼을 누르면 연결된 화면으로 이동하는 방식으로 동작함.

npm install @react-navigation/bottom-tabs	//탭 내비게이션 라이브러리 설치

3개의 버튼과 해당 버튼과 해당 버튼에 연결된 화면으로 구성된 탭 내비게이션을 만들 것이며, 먼저 화면으로 사용할 TabScreen 컴포넌트를 작성.

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>
    );
};

위에서 생성된 컴포넌트를 이용해 탭 내비게이션 구성

const Tab = createBottomTabNavigator();

const TabNavigation = ()=>{
	return(
    	<Tab.Navigator>
        	<Tab.Screen name="Mail" component={Mail} />
            <Tab.Screen name="Meet" component={Meet} />
            <Tab.Screen name="Settings" component={Settings} />
        </Tab.Navigator>
    );
 };
 
 export default TabNavigation;

📱실행 화면

5. 버튼 아이콘 설정하기

  • 화면을 구성하는 Screen마다 tabBarIcon에 MaterialCommunityIcons 컴포넌트를 반환하는 함수 지정
  • 반환 컴포넌트의 색과 크기는 TabBarIcon에 지정된 함수의 파라미터로 전달되는 color와 size를 이용해 설정
import {MaterialCommunityIcons} from "@expo/vector-icons";

const TabIcon = ({name, size, color})=>{
    return <MaterialCommunityIcons name={name} size={size} color={color}/>;
};

const TabNavigation = () => {
    return(
        <Tab.Navigator
       ...
        >
        <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 컴포넌트마다 탭 버튼 아이콘을 지정하지 않고 한 곳에서 모든 버튼의 아이콘을 관리하고 싶을 경우 Navigator 컴포넌트의 screenOptions 속성을 사용해서 관리할 수 있다.

//컴포넌트마다 아이콘을 지정하지 않고 한 곳에서 모든 버튼의 아이콘을 관리
// 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.Navigator
        /*
            탭 버튼 순서는 변경하지 않고, 렌더링되는
            첫 번째 화면을 변경하고 싶은 경우 : initialRouteName 속성 이용
        */
        initialRouteName="Settings"
        tabBarOptions={{
            //비활성화된 상태 색 설정
            //activeTintColor: '#ffffff',
            inactiveTintColor: '#0B92E9',
            //탭 바 스타일 변경
            style: {
                backgroundColor: '#54b7f9',
                borderTopColor: '#ffffff',
                borderTopWidth: 2,
            },
            //버튼 아이콘의 아래가 아닌 아이콘 옆에 렌더링
            labelPosition: 'beside-icon',
            //아이콘만 나타나도록
            showLabel: false,
        }}
        >

📱실행 화면

tabBarOptions의 style 속성이 먹히지 않는다ㅠㅠㅠ아직 이유 못찾음...

반응형
profile

my code archive

@얼레벌레 개발자👩‍💻

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

반응형