리액트 네이티브 강좌. 11편 - 스택 네비게이션과 화면 간 데이터 관리
안녕하세요!
리액트 네이티브 강좌 11편입니다.
편의를 위해 전체 강좌 링크도 넣었습니다.
-
리액트 네이티브 강좌. 7편 - 동적인 사용자 인터페이스 구현하기 - Dimensions API와 플랫폼별 코드 작성법
-
리액트 네이티브 강좌. 9편 - Input and Forms with Switch, KeyboardAvoidingView, Form Validation
-
리액트 네이티브 강좌. 10편 - Networking 다루기, 데이터 fetching, loading state, error handling
** 목 차 **
- 리액트 네이티브 강좌. 11편 - 스택 네비게이션과 화면 간 데이터 관리
네비게이션(Navigation)
이번에는 리액트 네이티브의 네비게이션에 대해서 본격적으로 알아보고자 합니다.
모바일 앱에서 네비게이션은 심장과도 같은 존재라고 할 수 있는데요.
사용자가 여러 화면을 넘나들고, 다양한 기능을 이용하고, 앱을 효과적으로 사용할 수 있도록 하는 중요한 장치입니다.
리액트 네이티브에서 네비게이션을 다루는 가장 흔한 방법은 바로 리액트 네비게이션 라이브러리를 사용하는 겁니다.
참고로 엑스포는 자체적으로 라우팅 기능을 제공하지만, 엑스포 프로젝트에서만 사용할 수 있다는 제약이 있는데요.
반면에 리액트 네비게이션은 엑스포 유무와 상관없이 리액트 네이티브 앱에서 모두 사용할 수 있다는 장점이 있습니다!
그래서 우리는 여기서 리액트 네비게이션에 집중해 보겠습니다.
리액트 네비게이션은 스택, 드로어, 탭 네비게이터 등 다양한 네비게이터를 제공합니다.
스택 네비게이터는 새로운 화면이 스택 위에 쌓이는 방식으로 화면 전환을 구현하는 데 유용하고, 드로어 네비게이터는 화면 측면에서 열고 닫을 수 있는 네비게이션 드로어를 표시해 줍니다.
탭 네비게이터는 화면 하단에 위치하여 여러 라우트를 손쉽게 전환할 수 있도록 해 줍니다.
우리는 이 세 가지 네비게이터를 살펴보겠습니다.
예제 템플릿 구성
새로운 엑스포 프로젝트를 만들겠습니다.
npx create-expo-app RN-navigation
프로젝트 폴더가 준비되었다면 리액트 네비게이션을 사용하기 위해 필요한 라이브러리를 설치해야합니다.
리액트 네비게이션 공식 문서에 가면 설치 명령어를 확인할 수 있습니다.
그리고 react-native-screens
와 react-native-safe-area-context
라는 두 가지 라이브러리도 추가로 설치해야 합니다.
npm install @react-navigation/native
npm install react-native-screens react-native-safe-area-context
마지막으로 리액트 네비게이션을 사용하기 위해서는 앱 전체를 감싸는 NavigationContainer
컴포넌트가 필요합니다.
아래 코드를 복사한 후 App.js
파일에 붙여 넣어 주시면 됩니다.
import { NavigationContainer } from '@react-navigation/native';
export default function App() {
return (
<NavigationContainer>
{/* 여기에 네비게이션 관련 코드를 작성할 예정임! */}
</NavigationContainer>
);
}
이로써 코드 준비는 끝났습니다.
스택 네비게이션(Stack Navigation)
이제 리액트 네비게이션을 설치했으니 본격적으로 네비게이션의 기본 중의 기본, 스택 네비게이터를 알아보도록 합시다!
스택 네비게이션은 아주 간단한 원리로 동작하는데요.
마치 카드 덱처럼 새로운 화면이 이전 화면 위에 차곡차곡 쌓이는 방식이죠.
새로운 화면으로 이동하면 카드 덱 위에 새로운 카드가 추가되고, 뒤로 가기를 하면 맨 위 카드가 사라지면서 이전 화면이 나타나는 겁니다.
이 방식은 많은 모바일 앱에서 흔히 볼 수 있는데, 사용자가 여러 단계를 거쳐 원하는 정보를 찾아 들어가고, 다시 원래 화면으로 돌아올 수 있도록 해줍니다.
특히 화면 흐름이 순 linear 형태로 이루어진 경우에 유용합니다.
예를 들어, 상품 목록을 보다가 특정 상품을 눌러 상세 정보를 확인하고, 거기에 있는 링크를 눌러 추가 정보를 보는 경우를 생각해 봅시다.
리액트 네비게이션 라이브러리는 스택 네비게이션을 위해 Stack Navigator
와 Native Stack Navigator
두 가지를 제공합니다.
Stack Navigator
는 자바스크립트 기반으로 동작하며, 사용자 정의가 매우 자유롭다는 장점이 있습니다. 앱에 독특한 네비게이션 경험을 구현하고 싶을 때 적합합니다. 하지만 반대로 성능 면에서는Native Stack Navigator
에 비해 떨어지는 측면이 있습니다.Native Stack Navigator
는 iOS와 안드로이드의 네이티브 네비게이션 기능을 활용하기 때문에 성능이 뛰어나고, 화면 전환이나 제스처가 훨씬 더 자연스럽다는 장점이 있습니다. 물론Stack Navigator
만큼 자유로운 커스터마이징이 어렵다는 단점도 있습니다.
이번에는 고급 커스터마이징보다는 더 나은 성능과 자연스러운 화면 전환에 초점을 맞출 거기 때문에 Native Stack Navigator
를 사용할 예정입니다.
먼저 프로젝트에 Native Stack Navigator
라이브러리를 설치해야겠죠?
리액트 네비게이션 공식 문서의 네비게이터 섹션에서 Native Stack을 찾아 설치 명령어를 복사해 터미널에 붙여 넣고 실행하면 됩니다.
npm install @react-navigation/native-stack
다음으로 App.js
파일 맨 위에 createNativeStackNavigator
함수를 @react-navigation/native-stack
에서 불러온 후, 이 함수를 호출하여 Native Stack Navigator
인스턴스를 생성해 줍니다.
import { createNativeStackNavigator } from '@react-navigation/native-stack';
const Stack = createNativeStackNavigator();
export default function App() {
// ...
}
이제 앱 컴포넌트 안에 네비게이터를 설정해 보겠습니다.
<NavigationContainer>
안에 <Stack.Navigator>
를 사용하고, 그 안에 <Stack.Screen>
을 추가해 줍니다.
<Stack.Screen>
컴포넌트는 name
prop과 component
prop을 필수로 요구합니다.
name
prop에는 화면의 이름을, component
prop에는 화면을 렌더링할 리액트 네이티브 컴포넌트를 지정하면 됩니다.
// ...
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
먼저 HomeScreen
컴포넌트를 만들어 보겠습니다.
프로젝트 루트에 screens
폴더를 새로 만들고, 그 안에 HomeScreen.js
파일을 생성합니다.
그리고 아래처럼 "Home Screen"이라는 텍스트를 렌더링하는 간단한 리액트 네이티브 컴포넌트를 만들어 줍니다.
// screens/HomeScreen.js
import { View, Text, StyleSheet } from 'react-native';
const HomeScreen = () => {
return (
<View style={styles.container}>
<Text style={styles.text}>Home Screen</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
text: {
fontSize: 24,
fontWeight: 'bold',
},
});
export default HomeScreen;
App.js
파일로 돌아와서 HomeScreen
을 import하고, Stack.Screen
의 component
prop에 할당해 줍니다.
// App.js
// ... other imports
import HomeScreen from './screens/HomeScreen';
export default function App() {
// ...
<Stack.Screen name="Home" component={HomeScreen} />
// ...
}
이제 애플리케이션에 첫 번째 화면이 생겼네요!
물론 화면이 하나뿐이면 네비게이션이 무슨 소용이 있겠습니까?
두 번째 화면도 만들어 봅시다.
screens
폴더에 AboutScreen.js
파일을 만들고, HomeScreen.js
파일의 내용을 복사해서 붙여 넣은 후, "Home"을 "About"으로 변경해 줍니다.
// screens/AboutScreen.js
import { View, Text, StyleSheet } from 'react-native';
const AboutScreen = () => {
return (
<View style={styles.container}>
<Text style={styles.text}>About Screen</Text>
</View>
);
};
// ... (스타일은 HomeScreen과 동일)
export default AboutScreen;
다시 App.js
파일로 돌아와서 <Stack.Screen>
코드를 복사한 후, name
prop을 "About"으로 변경하고, component
prop에 AboutScreen
컴포넌트를 할당해 줍니다.
// App.js
// ... other imports
import AboutScreen from './screens/AboutScreen';
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="About" component={AboutScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
기본적인 스택 네비게이터 설정이 완료되었습니다.
이제 터미널에서 npm start
명령어를 사용하여 앱을 실행해 보십시요.
NavigationContainer
에러가 뜨는데요.
다음과 같이 independent prop을 true로 설정하면 됩니다.
내비게이션 컨테이너가 다른 내비게이션 컨테이너와 상태를 공유하지 않도록 해야 할 때 사용하는 prop인데, 일단 여기서는 true로 설정하고 지나갑시다.
<NavigationContainer independent={true}>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="About" component={AboutScreen} />
</Stack.Navigator>
</NavigationContainer>
두 기기 모두 확인해 보면, 기본적으로 HomeScreen
이 렌더링된 것을 볼 수 있을 겁니다.
상단에는 Stack.Screen
의 name
prop 값이 제목으로 표시된 헤더도 보이죠?
이 헤더는 플랫폼별로 구현 방식이 달라서 iOS에서는 가운데에, 안드로이드에서는 왼쪽에 "Home"이라는 텍스트가 표시됩니다.
또한 라이브러리는 기본적으로 화면 상단의 노치 영역을 피해서 콘텐츠를 렌더링해 주기 때문에 안전 영역 뷰 안에 내용이 표시되는 것을 확인할 수 있습니다.
기본적으로 네비게이터 내에서 가장 위에 있는 화면이 초기 화면으로 표시되는데요, <Stack.Navigator>
에 initialRouteName
prop을 설정하여 이 동작을 변경할 수 있습니다.
initialRouteName
prop을 "About"으로 설정하면 앱 실행 시 AboutScreen
이 초기 화면으로 표시됩니다.
// ...
<Stack.Navigator initialRouteName="About">
// ...
파일을 저장하고 서버를 다시 시작하면 AboutScreen
이 초기 화면으로 표시되는 것을 확인할 수 있을 겁니다.
화면 간 이동하기
이제 스택 네비게이터에 Home
과 About
두 화면이 준비되었으니, 이 둘 사이를 어떻게 왔다 갔다 할 수 있는지 알아볼 시간입니다.
화면 간 이동을 다루는 주요 방법은 크게 두 가지가 있습니다.
바로 navigation
prop을 사용하는 방법과 useNavigation
Hook을 사용하는 방법인데요.
각 방법을 자세히 살펴보고 어떤 경우에 어떤 방법을 사용하는 것이 좋을지 알아보도록 합시다.
1. navigation
prop 사용하기
리액트 네비게이션은 애플리케이션의 모든 화면 컴포넌트에 자동으로 navigation
prop을 제공합니다.
navigation
prop은 다양한 네비게이션 동작을 시작하는 데 사용할 수 있는 여러 메서드를 가지고 있는데, 그중에서도 navigate
메서드를 사용하면 다른 화면으로 이동할 수 있습니다.
자, 그럼 HomeScreen
에서 AboutScreen
으로 이동하는 방법을 예제 코드와 함께 살펴볼까요?
먼저 파일 상단에 react-native
에서 Button
컴포넌트를 import 합시다.
그리고 텍스트 요소 아래에 Button
컴포넌트를 추가하고, title
prop에는 "Go to About"을, onPress
prop에는 AboutScreen
으로 이동하는 함수를 지정해 줍니다.
onPress
함수 안에서는 HomeScreen
의 navigation
prop을 비구조화 할당을 사용하여 가져온 후, navigation.navigate()
메서드를 호출하여 About
화면으로 이동하도록 합니다.
navigation.navigate()
메서드의 인자로는 이동하고자 하는 화면의 이름을 전달하면 됩니다.
화면의 이름은 App.js
파일의 Stack.Screen
컴포넌트의 name
prop에 설정한 값과 동일해야 합니다.
// screens/HomeScreen.js
import { View, Text, StyleSheet, Button } from 'react-native';
const HomeScreen = ({ navigation }) => {
return (
<View style={styles.container}>
<Text style={styles.text}>Home Screen</Text>
<Button title="Go to About" onPress={() => navigation.navigate('About')} />
</View>
);
};
// ... (스타일은 이전과 동일)
export default HomeScreen;
App.js
파일에서 initialRouteName
을 Home
으로 변경하고 파일을 저장한 후, 앱을 다시 시작해 보면, iOS와 안드로이드 모두에서 "Go to About" 버튼이 잘 나타날겁니다.
버튼을 누르면 AboutScreen
으로 이동하는 것을 확인할 수 있죠.
상단 왼쪽을 보면 어디서 이동해 왔는지 예전 스크린을 보여주고 있습니다.
2. useNavigation
Hook 사용하기
훅을 선호한다면 리액트 네비게이션에서 제공하는 useNavigation
Hook을 사용할 수도 있습니다.
파일 상단에 useNavigation
Hook을 import하고, 컴포넌트 안에서 호출하여 navigation
객체를 얻어옵니다.
// screens/HomeScreen.js
import { View, Text, StyleSheet, Button } from 'react-native';
import { useNavigation } from '@react-navigation/native';
const HomeScreen = () => {
const navigation = useNavigation(); // navigation 객체 얻기
return (
<View style={styles.container}>
<Text style={styles.text}>Home Screen</Text>
<Button title="Go to About" onPress={() => navigation.navigate('About')} />
</View>
);
};
// ... (나머지 코드는 동일)
navigation
prop을 사용했을 때와 마찬가지로, navigation.navigate('About')
코드를 사용하여 AboutScreen
으로 이동할 수 있습니다.
앱을 다시 시작해보면 동작 방식은 동일하게 유지되는 것을 확인할 수 있을 겁니다.
그럼 언제 뭘 사용해야 할까요?
navigation
prop은 사용하기 쉽고 추가 import가 필요하지 않다는 장점이 있습니다. 특히 화면 컴포넌트 내에서 사용할 때 편리합니다.useNavigation
Hook은 화면 컴포넌트뿐만 아니라 어떤 컴포넌트에서든 사용할 수 있기 때문에 훨씬 유연합니다. 중첩된 컴포넌트를 사용하거나 네비게이션 기능이 필요한 유틸리티 컴포넌트를 만들 때 유용하게 활용할 수 있습니다.
따라서 화면 컴포넌트에서는 navigation
prop을 사용하고, 꼭 필요한 경우에만 useNavigation
Hook을 사용하는 것을 추천합니다.
다시 UI로 돌아와서 HomeScreen
에서 AboutScreen
으로 이동할 때, 리액트 네비게이션이 HomeScreen
을 스택에 유지하면서 그 위에 AboutScreen
을 추가하는 것을 확인할 수 있을 겁니다.
뒤로 가기 버튼을 누르면 스택의 맨 위 화면이 사라지면서 HomeScreen
으로 돌아가는데, 이는 스택 네비게이션의 "Last In, First Out" 원칙 때문입니다.
이러한 스택 방식은 앱 내에서 자연스러운 네비게이션 흐름을 만들어 주어 사용자가 새로운 화면으로 이동하거나 이전 화면으로 손쉽게 돌아갈 수 있도록 해줍니다.
이제 AboutScreen
에도 HomeScreen
으로 이동하는 버튼을 추가할 수 있을 겁니다.
화면 사이에 데이터 주고받기
이전에 화면 간 이동하는 방법을 배웠는데, 이번에는 이동하면서 데이터까지 함께 전달하는 방법을 알아보겠습니다.
바로 코드를 보면서 예제와 함께 알아보는 게 좋겠습니다.
Home
화면에서 About
화면으로 name
이라는 파라미터를 전달하고, About
화면에서 해당 값을 렌더링해 보도록 하겠습니다.
navigation.navigate
메서드 기억하시죠? 화면 이동할 때 사용했던 메서드인데, 사실 이 녀석은 두 번째 인자로 route 파라미터 객체를 받을 수 있습니다.
이 객체에 새로운 화면으로 전달하고 싶은 데이터를 담아 보낼 수 있습니다.
// screens/HomeScreen.js
// ... (이전 코드와 동일)
const HomeScreen = ({ navigation }) => {
return (
<View style={styles.container}>
<Text style={styles.text}>Home Screen</Text>
<Button
title="Go to About"
onPress={() =>
navigation.navigate('About', { name: 'test' }) // name 파라미터 추가!
}
/>
</View>
);
};
// ... (나머지 코드는 동일)
이제 About
화면에서는 route
prop을 사용하여 전달받은 파라미터에 접근할 수 있습니다.
모든 화면 컴포넌트에 route
prop이 제공되니 꼭 기억하시기 바랍니다.
// screens/AboutScreen.js
// ... (이전 코드와 동일)
const AboutScreen = ({ route }) => { // route prop 추가!
const { name } = route.params; // name 파라미터 받기
return (
<View style={styles.container}>
<Text style={styles.text}>About {name}</Text> // name 값 표시
</View>
);
};
// ... (나머지 코드는 동일)
이제 파일을 저장하고 앱을 실행한 후, About
화면으로 이동해 봅시다.
"About test"라는 텍스트가 보이시나요?
이렇게 화면 간에 데이터를 전달할 수 있습니다.
기본 파라미터 값 설정하기
App.js
파일의 About
화면 설정에서 initialParams
prop을 사용하여 기본 파라미터 값을 설정할 수도 있습니다.
// App.js
// ... (이전 코드와 동일)
<Stack.Screen name="About" component={AboutScreen} initialParams={{ name: 'Guest' }} />
// name 파라미터 기본값 설정
// ... (나머지 코드는 동일)
이제 HomeScreen
에서 About
화면으로 이동할 때 name
파라미터를 전달하지 않으면, 기본값으로 설정한 "Guest"가 표시될 겁니다.
현재 화면에서 파라미터 업데이트하기
navigation
prop을 사용하여 현재 화면에서 파라미터를 업데이트할 수도 있습니다.
About
화면에 버튼을 하나 추가하고, 버튼을 누르면 navigation.setParams
메서드를 사용하여 name
파라미터 값을 변경해 보도록 하겠습니다.
// screens/AboutScreen.js
// ... (이전 코드와 동일)
const AboutScreen = ({ route, navigation }) => { // navigation prop 추가!
const { name } = route.params;
return (
<View style={styles.container}>
<Text style={styles.text}>About {name}</Text>
<Button
title="Update the name"
onPress={() => navigation.setParams({ name: '테스트' })} // 이름 업데이트!
/>
</View>
);
};
// ... (나머지 코드는 동일)
앱을 다시 시작하고 About
화면으로 이동한 후, "Update the name" 버튼을 눌러 봅시다.
텍스트가 변경되는 것을 확인할 수 있을 겁니다.
이전 화면으로 데이터 다시 보내기
이전 화면으로 데이터를 다시 보내는 것도 동일한 방식으로 할 수 있습니다.
About
화면에서 HomeScreen
으로 돌아갈 때 데이터를 함께 전달해 보도록 할게요.
// screens/AboutScreen.js
// ... (이전 코드와 동일)
<Button
title="Update the name"
onPress={() => navigation.setParams({ name: "테스트" })}
/>
<Button
title="Go back with data"
onPress={() =>
navigation.navigate("Home", { result: "About에서 간 데이터" })
}
/>
// ... (나머지 코드는 동일)
HomeScreen
에서는 route
prop을 사용하여 전달받은 데이터를 렌더링할 수 있어요.
// screens/HomeScreen.js
// ... (이전 코드와 동일)
const HomeScreen = ({ navigation, route }) => { // route prop 추가!
return (
<View style={styles.container}>
<Text style={styles.text}>Home Screen</Text>
<Text style={styles.text}>{route.params?.result}</Text> // 전달받은 데이터 표시
<Button
title="Go to About"
onPress={() =>
navigation.navigate('About', { name: 'test' })
}
/>
</View>
);
};
// ... (나머지 코드는 동일)
"Go back with data" 버튼을 누르면 "About에서 간 데이터" 텍스트가 HomeScreen
에 표시되는 것을 확인할 수 있을 겁니다.
리액트 네비게이션의 파라미터 처리는 화면 간에 데이터를 주고받는 간편하고 효율적인 방법을 제공합니다.
이를 통해 네비게이션 구조를 훨씬 동적이고 사용자 상호 작용에 반응하도록 만들 수 있죠.
스택 네비게이션 옵션으로 앱 꾸미기!
기본적인 네비게이션에 익숙해졌으니, 이제 스택 네비게이터가 제공하는 다양한 옵션들을 활용해서 앱을 더욱 멋지게 꾸며볼까 합니다.
이번에는 화면 제목 설정, 헤더 스타일 지정, 콘텐츠 스타일 설정까지 꼼꼼하게 알아보겠습니다.
화면 제목 설정하기
스택에 있는 모든 화면은 헤더에 제목을 표시할 수 있습니다.
사용자가 앱 내에서 현재 어느 위치에 있는지 쉽게 알 수 있도록 도와주는 역할을 합니다.
기본적으로 Screen
컴포넌트의 name
prop이 화면 제목으로 표시되는데, 예를 들어 현재 앱에서는 "Home"과 "About"이라고 표시되고 있습니다.
하지만 title
옵션을 사용하면 얼마든지 원하는 대로 제목을 바꿀 수 있습니다.
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
title: "Welcome Home!",
}}
/>
이제 앱을 실행해 보면 HomeScreen
의 제목이 "Welcome Home!"으로 변경된 것을 확인할 수 있을 겁니다.
헤더 스타일 지정하기
앱 테마에 맞춰서 헤더 스타일을 변경하고 싶을 때도 있는데요.
배경색, 텍스트 색상, 글꼴 두께 등 다양한 속성을 조정하여 헤더를 원하는 대로 꾸밀 수 있습니다.
headerStyle
옵션을 사용하면 됩니다!
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
title: "Welcome Home!",
headerStyle: {
// 헤더 스타일 지정!
backgroundColor: "#6a51ae", // 배경색 변경
},
headerTintColor: "fff",
headerTitleStyle: {
// 제목 스타일 지정!
fontWeight: "bold", // 글꼴 두께 변경
},
}}
/>
이제 앱을 실행해 보면 헤더 배경색이 보라색으로, 텍스트 색상은 흰색으로, 글꼴 두께는 굵게 변경된 것을 확인할 수 있을 겁니다.
헤더 왼쪽/오른쪽에 커스텀 컴포넌트 추가하기
headerLeft
와 headerRight
옵션을 사용하면 헤더의 왼쪽과 오른쪽에 커스텀 컴포넌트를 추가할 수도 있습니다.
예를 들어, 오른쪽에 간단한 메뉴 버튼을 추가해 볼까요?
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
title: "Welcome Home!",
headerStyle: {
// 헤더 스타일 지정!
backgroundColor: "#6a51ae", // 배경색 변경
},
headerTintColor: "fff",
headerTitleStyle: {
// 제목 스타일 지정!
fontWeight: "bold", // 글꼴 두께 변경
},
headerRight: () => (
// 오른쪽에 버튼 추가!
<Pressable onPress={() => alert("Menu button pressed!")}>
<Text style={{ color: "#fff", fontSize: 16 }}>Menu</Text>
</Pressable>
),
}}
/>
이제 앱을 실행해 보면 헤더 오른쪽에 "Menu" 버튼이 생긴 것을 확인할 수 있을 겁니다.
버튼을 누르면 "Menu button pressed!"라는 알림창이 나타납니다.
headerLeft
옵션도 동일한 방식으로 사용할 수 있습니다.
화면 콘텐츠 스타일 지정하기
contentStyle
옵션을 사용하면 화면 콘텐츠의 스타일을 지정할 수도 있습니다.
예를 들어, 배경색을 변경해 볼까요?
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
title: "Welcome Home!",
headerStyle: {
// 헤더 스타일 지정!
backgroundColor: "#6a51ae", // 배경색 변경
},
headerTintColor: "fff",
headerTitleStyle: {
// 제목 스타일 지정!
fontWeight: "bold", // 글꼴 두께 변경
},
headerRight: () => (
// 오른쪽에 버튼 추가!
<Pressable onPress={() => alert("Menu button pressed!")}>
<Text style={{ color: "#fff", fontSize: 16 }}>Menu</Text>
</Pressable>
),
contentStyle: {
backgroundColor: "#e8e4f3", // 배경색 변경
},
}}
/>
이제 앱을 실행해 보면 HomeScreen
의 배경색이 연한 회색으로 변경된 것을 확인할 수 있을 겁니다.
모든 화면에 스타일 일괄 적용하기
지금까지 설정한 옵션들은 모두 화면별로 적용되는데요, 스택 내의 모든 화면에 동일한 스타일을 적용하고 싶다면 Stack.Navigator
컴포넌트의 screenOptions
prop을 사용하면 됩니다.
// App.js
<NavigationContainer independent={true}>
<Stack.Navigator
screenOptions={{
headerStyle: {
// 헤더 스타일 지정!
backgroundColor: "#6a51ae", // 배경색 변경
},
headerTintColor: "fff",
headerTitleStyle: {
// 제목 스타일 지정!
fontWeight: "bold", // 글꼴 두께 변경
},
headerRight: () => (
// 오른쪽에 버튼 추가!
<Pressable onPress={() => alert("Menu button pressed!")}>
<Text style={{ color: "#fff", fontSize: 16 }}>Menu</Text>
</Pressable>
),
contentStyle: {
backgroundColor: "#e8e4f3",
},
}}
>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
title: "Welcome Home!",
}}
/>
<Stack.Screen
name="About"
component={AboutScreen}
initialParams={{ name: "Guest" }}
/>
</Stack.Navigator>
</NavigationContainer>
위 코드를 보시면 Stack.Screen
컴포넌트 안에 있던 options 항목을 그대로 Stack.Navigator
컴포넌트 안의 screenOptions 항목으로 옮긴겁니다.
screenOptions
prop에 지정한 스타일은 모든 화면에 공통으로 적용되고, 각 화면에서 개별적으로 설정한 옵션들은 공통 옵션을 덮어씌웁니다.
이제 앱을 실행해 보면 HomeScreen
과 AboutScreen
모두 동일한 스타일이 적용된 것을 확인할 수 있을 겁니다.
다이나믹하게 스택 네비게이터 옵션 변경하기
이전에 HomeScreen
의 제목, 헤더 배경색, 콘텐츠 배경색 등 스택 네비게이터에서 제공하는 다양한 옵션들을 설정하는 방법을 알아봤는데요.
하지만 상황에 따라 옵션을 동적으로 설정해야 할 때가 있습니다.
대표적인 예로 화면 제목을 들 수 있습니다.
"About"처럼 고정된 제목 대신 파라미터로 전달받은 사용자 이름을 표시하는 등 좀 더 개인화된 제목을 만들고 싶을 때가 있을 건데요.
이제 이걸 알아보겠습니다.
1. Stack.Screen
컴포넌트에서 직접 설정하기
헤더 제목을 동적으로 설정하는 한 가지 방법은 Stack.Screen
컴포넌트에서 options
prop에 함수를 전달하는 겁니다.
<Stack.Screen
name="About"
component={AboutScreen}
initialParams={{ name: "Guest" }}
options={({ route }) => ({
title: route.params.name,
})}
/>
options
prop에 함수를 전달하면, 이 함수는 route
prop을 인자로 받아서 객체를 반환해야 합니다.
이 객체 안에 원하는 옵션을 설정하면 되는 거죠.
위 코드에서는 route.params.name
값을 사용하여 제목을 동적으로 설정하고 있습니다.
HomeScreen.js
파일에서 About
화면으로 이동할 때 name
파라미터를 전달하는 코드를 추가하겠습니다.
// screens/HomeScreen.js
// ... (이전 코드와 동일)
<Button
title="Go to About"
onPress={() =>
navigation.navigate('About', { name: 'React' }) // name 파라미터 전달
}
/>
// ... (나머지 코드는 동일)
About
화면의 제목이 전달된 name
값이 'React'로 설정된 것을 확인할 수 있을 겁니다.
2. useLayoutEffect
Hook 사용하기
useLayoutEffect
Hook을 사용하여 동적으로 제목을 설정할 수도 있습니다.
Stack.Screen
컴포넌트에서 options
prop을 주석 처리하고, useLayoutEffect
Hook을 사용하도록 코드를 변경해 볼게요.
<Stack.Screen
name="About"
component={AboutScreen}
initialParams={{ name: "Guest" }}
// options={({ route }) => ({
// title: route.params.name,
// })}
/>
// screens/AboutScreen.js
// ... (이전 코드와 동일)
import { useLayoutEffect } from 'react'; // useLayoutEffect Hook import
const AboutScreen = ({ route, navigation }) => {
const { name } = route.params;
useLayoutEffect(() => { // useLayoutEffect Hook 사용!
navigation.setOptions({ // 옵션 설정
title: name,
});
}, [navigation, name]); // navigation과 name이 변경될 때마다 실행
// ... (나머지 코드는 동일)
};
export default AboutScreen;
useLayoutEffect
Hook은 컴포넌트가 렌더링된 직후에 실행되기 때문에, 화면이 표시되기 전에 제목을 설정할 수 있습니다.
navigation.setOptions
메서드를 사용하여 옵션 객체를 전달하면 되고, 이 객체 안에 원하는 옵션을 설정하면 됩니다.
위 코드에서는 name
변수 값을 사용하여 제목을 동적으로 설정하고 있습니다.
useLayoutEffect
Hook의 두 번째 인자로는 의존성 배열을 전달하는데, 이 배열 안에 포함된 값이 변경될 때마다 useLayoutEffect
Hook 내부의 코드가 다시 실행됩니다.
위 코드에서는 navigation
과 name
이 변경될 때마다 제목이 업데이트되도록 설정했습니다.
앱을 실행하고 About
화면으로 이동해 보면, useLayoutEffect
Hook을 사용했을 때도 제목이 정상적으로 설정되는 것을 확인할 수 있을 겁니다.
그럼 언제 뭘 사용해야 할까요?
Stack.Screen
컴포넌트의options
prop은 제목이나 네비게이션 옵션이 라우트 파라미터에 의해 결정되거나 고정적인 경우에 사용하는 것이 좋습니다.useLayoutEffect
Hook은 네비게이션 옵션이 화면 컴포넌트의 내부 로직, 상태 또는 prop에 의존하거나 렌더링 이후에 업데이트되어야 하는 경우에 사용하는 것이 좋습니다.