[React Native] route registry로 profile별 화면을 연결하는 방법
React Native 통합 앱에서 여러 profile을 관리하다 보면 화면 라우팅이 가장 빨리 복잡해집니다. 어떤 profile은 지도 화면으로 시작하고, 어떤 profile은 목록 화면이 중심이며, 또 다른 profile은 설정이나 안내 화면만 필요할 수 있습니다.
이 차이를 화면 코드 곳곳의 조건문으로 처리하면 새 profile을 추가할 때마다 회귀 오류가 생기기 쉽습니다. 화면 컴포넌트 안에 profile별 조건이 계속 늘어나면 어떤 화면이 어떤 앱에서 열려야 하는지 추적하기 어려워집니다.
route registry는 profile group별로 사용할 수 있는 화면과 시작 화면을 한곳에서 관리하는 방식입니다. 라우팅 정책을 registry로 모아두면 화면 코드는 자신에게 전달된 props와 데이터에 더 집중할 수 있습니다.
route는 단순한 화면 이름 목록이 아니라 앱 사용 흐름의 기준표에 가깝습니다. 이 글에서는 route registry에 어떤 값을 넣어야 하는지, group guard를 어떻게 둘지, smoke test와 Jest로 최소한 무엇을 확인하면 좋은지 정리해보겠습니다.
route registry가 필요한 이유
화면 이동은 앱 사용 흐름의 뼈대입니다. 라우트가 잘못 연결되면 데이터가 정상이어도 사용자는 빈 화면이나 잘못된 상세 화면을 보게 됩니다. 특히 통합 앱에서는 이 profile에서 이 route가 존재하는지를 코드로 보장해야 합니다.
| 문제 | 원인 | 해결 방향 |
|---|---|---|
| 없는 화면으로 이동 | route 이름 직접 입력 | route registry로 제한 |
| 다른 profile 화면 노출 | group guard 없음 | profile group별 허용 route 관리 |
| 첫 화면 오류 | initialRoute 불일치 |
registry에서 시작 화면 관리 |
| 테스트 누락 | 대표 profile만 확인 | group별 smoke test 추가 |
route 이름을 문자열로 화면 곳곳에 흩어두면 변경 시점마다 수정 범위를 찾기 어려워집니다. 그래서 profile group, 시작 화면, fallback 기준을 registry에서 함께 관리하는 편이 좋습니다.
route registry 예시
아래 예시 코드는 profile group별로 사용할 수 있는 route를 정의하고, 특정 group에서 열 수 있는 route인지 확인하는 구조입니다.
type ProfileGroup = 'tour' | 'filedata' | 'utility';
type RouteName = 'Home' | 'Map' | 'List' | 'Detail' | 'Settings';
type RouteConfig = {
name: RouteName;
componentKey: string;
groups: ProfileGroup[];
};
export const routeRegistry: RouteConfig[] = [
{name: 'Home', componentKey: 'HomeScreen', groups: ['tour', 'utility']},
{name: 'Map', componentKey: 'MapScreen', groups: ['tour', 'filedata']},
{name: 'List', componentKey: 'ListScreen', groups: ['tour', 'filedata']},
{name: 'Detail', componentKey: 'DetailScreen', groups: ['tour', 'filedata']},
{name: 'Settings', componentKey: 'SettingsScreen', groups: ['tour', 'filedata', 'utility']},
];
export function getRoutesByGroup(group: ProfileGroup) {
return routeRegistry.filter((route) => route.groups.includes(group));
}
export function canOpenRoute(group: ProfileGroup, routeName: RouteName) {
return getRoutesByGroup(group).some((route) => route.name === routeName);
}
이 구조의 장점은 화면 코드가 profile별 조건을 직접 알 필요가 줄어든다는 점입니다. 라우팅 정책은 registry에서 확인하고, 화면은 렌더링과 사용자 동작 처리에 집중할 수 있습니다.
코드를 나눠보다 보면 route 조건은 화면 컴포넌트보다 별도 registry에 두는 편이 더 관리하기 쉽습니다. 새 profile이 추가될 때도 화면 파일을 여러 개 수정하기보다 registry에서 허용 route를 확인하는 흐름이 더 명확합니다.
initialRoute도 함께 관리하기
profile group마다 시작 화면이 다를 수 있다면 initialRoute도 registry와 가까운 곳에서 관리하는 편이 좋습니다. 시작 화면이 허용 route에 포함되지 않으면 앱 실행 직후 빈 화면이나 fallback 반복이 생길 수 있습니다.
type ProfileRouteConfig = {
group: ProfileGroup;
initialRoute: RouteName;
};
export const profileRouteConfigs: ProfileRouteConfig[] = [
{group: 'tour', initialRoute: 'Map'},
{group: 'filedata', initialRoute: 'List'},
{group: 'utility', initialRoute: 'Home'},
];
export function getInitialRoute(group: ProfileGroup): RouteName {
return profileRouteConfigs.find((config) => config.group === group)?.initialRoute ?? 'Home';
}
운영 관점에서는 시작 화면이 꽤 중요합니다. 앱이 실행되자마자 잘못된 route로 이동하면 나머지 기능이 정상이어도 사용자는 앱이 깨졌다고 느낄 수 있습니다.
group guard를 두는 이유
route registry만 있어도 도움이 되지만, deep link나 알림 클릭처럼 앱 외부에서 특정 화면으로 들어오는 경우 guard가 필요합니다. 외부에서 들어온 route 이름을 그대로 열면 현재 profile에서 지원하지 않는 화면으로 이동할 수 있습니다.
export function resolveRouteOrFallback(
group: ProfileGroup,
requestedRoute: RouteName,
): RouteName {
if (canOpenRoute(group, requestedRoute)) {
return requestedRoute;
}
return group === 'utility' ? 'Home' : 'List';
}
이렇게 fallback을 두면 지원하지 않는 화면 요청이 들어왔을 때 앱이 깨지는 대신 안전한 기본 화면으로 보낼 수 있습니다. 다만 fallback이 너무 조용하면 문제를 놓칠 수 있으므로 개발 환경에서는 경고 로그를 남기는 것이 좋습니다.
실제로 구현 흐름을 따라가다 보면 메뉴에서 보이지 않는 화면도 deep link나 푸시 알림으로 들어올 수 있습니다. 그래서 UI 노출 조건과 실제 route 접근 조건을 함께 관리해야 합니다.
smoke test 기준
profile이 많아질수록 모든 화면을 매번 수동으로 확인하기 어렵습니다. 그래서 최소 smoke test 기준을 정해두는 것이 좋습니다.
| 테스트 항목 | 확인 내용 |
|---|---|
initialRoute |
profile 시작 화면이 열리는지 확인 |
| route list | group에 맞는 화면만 등록되는지 확인 |
| deep link | 허용되지 않은 route가 fallback되는지 확인 |
| empty state | 데이터가 없을 때 빈 화면 안내가 있는지 확인 |
| representative profile | group별 대표 profile이 깨지지 않는지 확인 |
smoke test는 모든 기능을 깊게 검증하는 테스트라기보다, 앱이 기본 흐름을 잃지 않았는지 확인하는 최소 안전망에 가깝습니다. 특히 통합 앱에서는 group별 대표 profile을 하나씩 잡아두면 회귀를 더 빨리 발견할 수 있습니다.
Jest로 registry 함수 테스트하기
Jest에서는 화면을 모두 띄우지 않아도 registry 함수만 따로 테스트할 수 있습니다. route 접근 가능 여부와 fallback 기준은 작은 함수로 분리해두면 테스트하기 쉽습니다.
test('utility group cannot open Map route', () => {
expect(canOpenRoute('utility', 'Map')).toBe(false);
});
test('tour group can open Detail route', () => {
expect(canOpenRoute('tour', 'Detail')).toBe(true);
});
test('unsupported route falls back to safe route', () => {
expect(resolveRouteOrFallback('utility', 'Map')).toBe('Home');
});
이런 테스트는 작아 보이지만 route 정책이 바뀔 때 큰 도움이 됩니다. 화면 렌더링 테스트보다 빠르게 실행할 수 있고, 새 profile을 추가할 때 빠뜨린 route가 있는지도 확인하기 좋습니다.
원본 앱과 다른 UX를 남기는 방법
마이그레이션이나 통합 과정에서는 원본 앱과 UX가 달라질 수 있습니다. 중요한 것은 달라진 사실을 숨기지 않고 근거를 남기는 것입니다.
- 원본 앱에는 있던 화면을 제외했다면 제외 사유를 남긴다.
- 화면 순서가 바뀌었다면 변경 이유를 문서화한다.
- route 이름을 바꿨다면 이전 이름과 새 이름을 매핑한다.
- 아직 parity 확인 전이면 완료로 표시하지 않는다.
이 부분은 단순 문서 작업처럼 느껴질 수 있습니다. 다만 나중에 왜 이 화면이 빠졌는지, 왜 시작 화면이 바뀌었는지 확인해야 할 때 큰 차이가 납니다. 변경 사유를 남겨두면 코드만 보고 판단하기 어려운 UX 결정을 추적할 수 있습니다.
테스트 기준
route registry를 설계했다면 아래 기준을 최소한 확인해보는 것이 좋습니다. 특히 외부 입력으로 들어오는 route는 registry와 guard를 반드시 거쳐야 합니다.
- route 이름을 문자열로 흩뿌리지 않고 registry에서 관리하는지 확인한다.
- profile group별 허용 route가 명확한지 확인한다.
- deep link나 알림 payload의 route를 검증하는지 확인한다.
- 지원하지 않는 route에 fallback이 있는지 확인한다.
- 원본 앱과 다른 UX는 근거를 남겼는지 확인한다.
자주 하는 실수
1. 화면 컴포넌트 안에서 profileId 조건을 계속 늘리는 경우가 있습니다. 처리 속도는 빨라 보일 수 있지만 profile이 늘어나면 어떤 화면이 어떤 앱에 필요한지 추적하기 어려워집니다.
2. 메뉴에서만 route를 숨기고 deep link는 검증하지 않는 경우도 있습니다. 메뉴에 보이지 않는 화면도 외부 입력으로 접근될 수 있으므로 group guard가 필요합니다.
3. initialRoute가 허용 route에 포함되어 있는지 확인하지 않는 경우가 있습니다. 앱 시작 화면이 잘못되면 사용자는 앱이 정상적으로 실행되지 않는다고 느낄 수 있습니다.
4. 원본 앱과 달라진 UX를 기록하지 않는 경우도 있습니다. 나중에 화면이 빠진 이유나 순서가 바뀐 이유를 찾기 어려워집니다.
결론
React Native 통합 앱에서 route registry는 화면 연결의 기준표입니다. profile group별 화면, 시작 route, fallback, 테스트 기준을 한곳에서 관리하면 새 profile을 추가할 때 생기는 회귀를 줄일 수 있습니다.
좋은 구조는 화면 컴포넌트에 profile 조건을 흩어두지 않고, route registry와 group guard에서 접근 가능 여부를 먼저 판단하는 것입니다. 여기에 smoke test와 Jest 테스트를 붙이면 최소한의 라우팅 회귀를 빠르게 확인할 수 있습니다.
또 하나 중요한 점은 원본 앱과 달라진 UX를 반드시 기록하는 것입니다. 통합 과정에서는 화면이 제외되거나 순서가 바뀔 수 있는데, 그 이유를 남겨두면 나중에 유지보수할 때 훨씬 덜 헷갈립니다.