메인에서 여러 개의 네비게이션을 구현했는데, 요구사항에 맞추어 서로 다른 네비게이션에 존재하는 페이지로 이동하는 것을 구현해야 했다. 만들고 있는 앱의 라우터 구조가 아주 복잡하게 되어 있어 네이게이션 스택 간 이동이 쉽지 않았다.
먼저 라우터 구조를 보면, Router라는 파일 안에 GolfFieldNavigation, CaddyNavigation이 존재하고, GolfFieldNaviagation 안에 TabNavigation인 TabBar 안에 또 여러 Navigation이 존재했다. 리팩토링을 거치지 않은 순수그자체라 아쉬운 부분이 있지만, 현재로서 Stack 간 이동을 구현할 방법을 고민했다.
Router
- GolfFieldNavigation
- TabBar : Tab Navigation
- Home
- ...
- View
- Shop
- Setting : Stack Navigation
- CompositionNavigation
- HoleInfo
- CourseInfo
- AddComposition
- CaddyNavigation
- CaddyView
- CaddyHoliday
- CaddyDetail
- ReservationNavigation
- ReservationView
- ReservationCreate
- ReservationEdit
- OverviewNavigation
- Info
- Direction
- Facility
- Notification
- CaddyNavigation
...
예를 들어, RerservationNavigation 안에 있는 ReservationView에서 Home으로 가고 싶으면, CaddyNavigation -> Setting -> TabBar로 빠져나와야 하는 문제가 있다. 실제로 navigation.navigate('Home');을 실행하면, 해당 이름을 가진 페이지가 없다는 메세지가 뜬다.
또 로그인이 완료된 상태인데 뒤로가기를 누르면, 로그인 창이 다시 뜬다거나, 로그아웃을 했는데 다시 메인화면에 접속이 되는 문제가 발생한다. Navigation Stack을 전혀 고려하지 않았기 때문인데, 이 문제를 방지하기 위해서 @react-navigation/native
에서 제공하는 CommonActions
를 사용할 것이다.
CommonActions
CommonAcitons
는 네비게이션의 동작을 일관성 있게 처리할 수 있도록 한다. 주요 기능은 3가지가 있다.
주요 기능
1. navigate
다른 페이지로의 이동을 구현하며 아래와 같은 변수와 함께 사용한다.
- name : 이동할 화면의 이름
- key : 이동할 화면의 key 값
- params : 이동할 화면으로 전달할 파라미터
navigation.dispatch(
CommonActions.navigate({
name: 'Profile',
params: { userId: 1234 },
})
);
이렇게 사용하면 Profile이라는 화면으로 이동하면서 userId 값을 파라미터로 전달할 수 있다.
2. goBack
이 방식은 이전 화면으로 돌아가고 싶을 때 사용할 수 있다.
navigation.dispatch(CommonActions.goBack());
3. reset
네비게이션의 상태를 초기화할 수 있고 새로운 상태를 전달하는 state 변수와 함께 사용할 수 있다.
navigation.dispatch(
CommonActions.reset({
index: 0,
routes: [
{ name: 'Home' },
],
})
);
앱 내의 모든 스크린을 초기화하고 'Home' 화면으로 이동한다. 보통 로그아웃을 구현할 때 자주 사용된다.
만약, 현재 네비게이션 스택 상태를 유지하면서 이동하고 싶을 때는 아래와 같이 사용할 수 있다.
navigation.dispatch((state) => {
// Remove all the screens after `Profile`
const index = state.routes.findIndex((r) => r.name === 'Profile');
const routes = state.routes.slice(0, index + 1);
return CommonActions.reset({
...state,
routes,
index: routes.length - 1,
});
});
4. setParams
현재 화면이나 다른 화면에 파라미터 값만 전달하고 싶을 때 사용할 수 있다. navigation.dispatch(CommonActions.setParams({ user: 'Wojtek' }));
이러한 방식은 현재 화면에서 파라미터를 업데이트하는 방식이다.
만약에, 특정 화면의 파라미터를 변경하거나 수정하고 싶을 땐 아래와 같이 해당 페이지로 파라미터를 전달할 수 있다.
navigation.dispatch({
...CommonActions.setParams({ user: 'Wojtek' }),
source: route.key,
});
CommonActions와 useNavigation의 차이점
CommonActions와 useNavigation 둘 다 화면 간의 이동을 관리하는데 사용할 수 있다. 주요 차이점은 네비게이션 액션의 제어 수준과 사용 시점이다.
useNavigation은 리액트 네비게이션의 훅으로 네비게이션의 객체를 리턴해서 간단한 화면 전환을 가능하게 한다. 뒤로가기나 화면 간의 기본적인 이동을 간단하게 구현할 수 있게 해서 코드가 직관적이다. 또한 Context 기반으로 동작하여 컴포넌트 트리 내에서 네비게이션 객체를 받아 처리한다.
반면 CommonActions는 관리할 네비게이션 스택이 복잡한 경우 네비게이션 상태를 세밀하게 제어하거나 스택 초기화와 같은 복잡한 액션을 제어할 때 사용할 수 있다.
Stack 초기화 구현
1. 서로 다른 네비게이션 간의 화면 이동 구현(feat.useNavigation의 한계)
const onCalendarViewClick = () => {
Alert.alert('Notice', '홈으로 이동하겠습니까?', [
{
text: '아니요',
style: 'cancel',
},
{
text: '예',
onPress: () => {
navigation.goBack();
},
},
]);
};
기존에는 홈으로 이동하도록 해야 하는데, useNavigation의 한계로 인해서 서로 다른 네비게이션 간의 이동이 불가했다.
const onCalendarViewClick = () => {
Alert.alert('Notice', '홈으로 이동하겠습니까?', [
{
text: '아니요',
style: 'cancel',
},
{
text: '예',
onPress: () => {
navigation.dispatch(
CommonActions.reset({
index: 0,
routes: [{name: 'Setting'}],
})
);
navigation.dispatch(CommonActions.navigate({name: '홈'}));
},
},
]);
};
useNavigation 대신에, CommonAcitons를 사용해서 Setting - ReservationNavigation에 속해 있는 ReservationEdit.tsx
라는 페이지에서 Setting이라는 네비게이션 Stack을 reset하고, 새로운 페이지로 이동하도록 하였다.
2. 로그인이나 로그아웃 시 Stack 초기화 구현
기존 코드는 로그인이 된 후에 Stack이 여전히 남아있어서 뒤로가기를 누르면 로그인 화면으로 다시 되돌아간다.
const handleNextPage = async () => {
if (currentSetup) {
const LoginInfo: LoginInfo = {
type: type,
phoneNumber: currentPhoneNumber,
password: pin,
};
if (type == 'Golf') {
const response = await login(LoginInfo);
if (response) {
navigation.navigate('Main');
} else {
setPin('');
}
} else {
const response = await caddyLogin(LoginInfo);
if (response) {
navigation.navigate('CaddyTabBar');
} else {
setPin('');
}
}
} else {
const PinValidationInfo: PinValidationInfo = {
setup: currentSetup,
password: pin,
};
navigation.navigate('PinValidation', PinValidationInfo);
setPin('');
}
};
const handleNextPage = async () => {
if (currentSetup) {
const LoginInfo: LoginInfo = {
type: type,
phoneNumber: currentPhoneNumber,
password: pin,
};
if (type == 'Golf') {
const response = await login(LoginInfo);
if (response) {
navigation.dispatch(
CommonActions.reset({
index: 1,
routes: [{name: 'Main'}],
})
);
}
} else {
// * 캐디인 경우
const response = await caddyLogin(LoginInfo);
if (response) {
navigation.dispatch(
CommonActions.reset({
index: 1,
routes: [{name: 'CaddyTabBar'}],
})
);
}
}
} else {
const PinValidationInfo: PinValidationInfo = {
setup: currentSetup,
password: pin,
};
navigation.dispatch(
CommonActions.reset({
index: 1,
routes: [{name: 'PinValidation', params: PinValidationInfo}],
})
);
}
};
navigation의 모든 부분을 CommonActions.reset으로 구현하여 기존에 쌓인 네비게이션의 스택을 모두 리셋하고 새로운 페이지로 이동하도록 구현했다.
'FrontEnd > React-native' 카테고리의 다른 글
react-native 애플리케이션 성능 개선하기(feat.useQuery) (0) | 2024.08.22 |
---|---|
Axios 인터셉터로 JWT 로테이션 구현하기 (0) | 2024.08.22 |
스켈레톤 화면이 사용자 경험 개선에 도움이 될까 (0) | 2024.08.04 |