끄적이는 개발노트

React Native - Navigation 본문

React Native

React Native - Navigation

크런키스틱 2025. 1. 23. 21:05
728x90

어플에서 화면 이동을 구현하려면 Navigation을 활용해야 한다.

이 때, 어플은 router 방식이 아닌 stack 형식으로 화면이 쌓이는 형태이다.

 

1. 설치

npm install @react-navigation/native react-native-screens react-native-safe-area-context @react-navigation/native-stack @react-navigation/elements
#or
yarn add @react-navigation/native react-native-screens react-native-safe-area-context @react-navigation/native-stack @react-navigation/elements

 

 

2. 설정

react-native-screens 패키지는 MainActivity.kt or MainActivity.java 파일을 수정해야 한다.

파일 경로는 android/app/src/main/java/<package name> 이다.

// MainActivity.kt

// ...
import android.os.Bundle;		// <= import 추가

class MainActivity: ReactActivity() {
  // ...
  override fun onCreate(savedInstanceState: Bundle?) {		// <= 해당 내용 추가
    super.onCreate(null)
  }
  // ...
}

 

 

3. 코드

개인적으로 Navigation을 App.tsx에 바로 선언하기보다는 별도의 파일로 관리하는 것을 선호한다.

추후, 어플 로딩 중에 먼저 처리해야 할 로직들 (ex. 어플 버전 체크, 데이터 불러오기 등)이 대부분 요구되고, 로그인과 같이 페이지에 접근하는 조건이 발생하는 경우 로그인 여부에 따라 stack을 나눌 필요가 있기 때문이다.

그러다보면 자연스럽게 코드가 길어지기 때문에 따로 Navigation.tsx 파일을 생성하여 관리하는 것을 추천한다.

// App.tsx

import './global.css';
import {SafeAreaProvider} from 'react-native-safe-area-context';
import Navigation from 'navigation';

const App = () => {
  return (
    <SafeAreaProvider>
      <Navigation />
    </SafeAreaProvider>
  );
};

export default App;
// Navigation.tsx

import DetailScreen from '@/screen/detail';
import HomeScreen from '@/screen/home';
import {StackParamList} from '@/types/stack';
import {NavigationContainer} from '@react-navigation/native';
import {createNativeStackNavigator} from '@react-navigation/native-stack';
import {Text} from 'react-native';

const Stack = createNativeStackNavigator<StackParamList>();

const RootStack = () => {
  return (
    <Stack.Navigator
      initialRouteName="Home"
      screenOptions={{
        headerShown: false,
        headerBackVisible: false,
        animation: 'flip',
      }}>
      <Stack.Screen
        name="Home"
        component={HomeScreen}
        options={{
          headerShown: true,
          headerShadowVisible: false,
          headerTintColor: '#000000',
          headerTitleStyle: {
            fontFamily: 'GmarketSansMedium',
          },
          headerTitleAlign: 'center',
          headerTitle: '홈',
        }}
      />
      <Stack.Screen
        name="Detail"
        component={DetailScreen}
        options={({navigation}) => ({
          headerShown: true,
          headerShadowVisible: false,
          headerTintColor: '#000000',
          headerTitleStyle: {
            fontFamily: 'GmarketSansMedium',
          },
          headerTitleAlign: 'center',
          headerTitle: '상세정보',
          headerLeft: () => (
            <Text onPress={() => navigation.goBack()}>뒤로가기</Text>
          ),
        })}
      />
    </Stack.Navigator>
  );
};

const Navigation = () => {
  return (
    <NavigationContainer>
      <RootStack />
    </NavigationContainer>
  );
};

export default Navigation;

 

매우 간단하게 navigation에 홈 화면과 상세정보 화면을 추가했다.

위에 보이는 options들은 아래 공식문서에서 확인하면 본인이 원하는대로 다양하게 활용하여 구성할 수 있다.

https://reactnavigation.org/docs/screen-options/

 

Options for screens | React Navigation

Each screen can configure various aspects about how it gets presented in the navigator that renders it by specifying certain options, for example, the header title in stack navigator, tab bar icon in bottom tab navigator etc. Different navigators support d

reactnavigation.org

 

// home.tsx

import CustomText from '@/components/text';
import {HomeProps} from '@/types/stack';
import {Button, View} from 'react-native';
import {SafeAreaView} from 'react-native-safe-area-context';

const HomeScreen = ({navigation}: HomeProps) => {
  return (
    <SafeAreaView className="flex-1">
      <View className="w-full h-full flex justify-center items-center bg-white">
        <CustomText className="text-success text-xl" children="홈 화면" />
        <Button
          title="상세정보"
          onPress={() => navigation.navigate('Detail')}
        />
      </View>
    </SafeAreaView>
  );
};

export default HomeScreen;
// detail.tsx

import CustomText from '@/components/text';
import {DetailProps} from '@/types/stack';
import {View} from 'react-native';
import {SafeAreaView} from 'react-native-safe-area-context';

const DetailScreen = ({navigation}: DetailProps) => {
  return (
    <SafeAreaView className="flex-1">
      <View className="w-full h-full flex justify-center items-center bg-white">
        <CustomText className="text-success text-xl" children="상세정보 화면" />
      </View>
    </SafeAreaView>
  );
};

export default DetailScreen;

 

 

4. Typescript

타입스크립트를 사용하는 경우, stack에 대한 타입 선언과 각 화면에서 페이지 이동, params 등을 사용하기 위한 navigation과 route props의 타입을 지정할 필요가 있다.

이를 위해, 본인은 src 폴더 안에 types 폴더를 생성하고 stack.ts라는 파일을 만들었다.

// stack.ts

import {NativeStackScreenProps} from '@react-navigation/native-stack';

export type StackParamList = {
  Home: undefined;
  Detail: undefined;
};

export type HomeProps = NativeStackScreenProps<StackParamList, 'Home'>;
export type DetailProps = NativeStackScreenProps<StackParamList, 'Detail'>;

 

위와 같이 타입 선언은 간단하다.

Navigation에서 선언해준 screen name을 key로 지정하고, 각 화면에서 별도로 받는 params가 없다면 undefined로 마무리해주면 된다. 만약 params가 필요하다면

Detail: {
	id: string;
}

와 같이 넘겨주고자하는 값과 타입을 선언해주면 된다.

이 때, 유의할 점은 params에 setState와 같은 function을 넘기려하면 기능은 작동하더라도 직렬화가 불가능한 기능은 어떤 에러를 발생할지 모르니 사용을 자제하라는 warning이 발생하니 사용하지 말아야 한다.

 

 

5. 화면 이동

  • navigate("route"): 지정한 화면으로 이동
  • replace("route"): 현재 화면을 지정한 화면으로 대체 (이전 화면 stack에서 제거)
  • push("route"): 지정한 화면을 추가
  • pop(): 이전 화면으로 돌아감 (현재 화면 stack에서 제거)
  • popToTop(): 맨 처음 화면으로 돌아감
  • goBack(): 이전 화면으로 돌아감

 

6. 실행

이제 코드를 실행해보면 아래와 같이 화면 간에 이동이 잘 이루어지는 것을 확인할 수 있다.

728x90