React Native 통합 앱에서 profile registry를 관리하는 방법
React Native로 여러 앱을 하나의 코드베이스에서 운영하다 보면 가장 먼저 부딪히는 문제가 있다. 앱마다 다른 설정을 어디에 둘 것인가 하는 문제다.
앱 이름, 패키지명, 첫 화면, 기능 사용 여부, 권한, 외부 링크, 데이터 소스가 조금씩 다르면 처음에는 if 문 몇 개로도 버틸 수 있지만 프로파일이 늘어나면 어느 순간부터 조건문이 화면 곳곳에 흩어지고, 새 앱을 추가할 때마다 예상하지 못한 화면이 같이 깨지기 쉽다.
이럴 때는 profile registry를 별도로 두고, 앱별 설정을 한 곳에서 관리하는 방법이 있다. 처음 구현할 때는 조금 번거로워 보일 수 있지만, 여러 앱을 묶어서 운영할수록 설정의 위치와 검증 방식이 유지보수 차이를 크게 만든다.
이 글에서는 React Native 통합 앱에서 profile registry를 설계할 때 어떤 값을 넣어야 하는지, feature flag와 initialRoute를 어떻게 분리하면 좋은지, 새 프로파일을 추가할 때 어떤 회귀 테스트를 확인해야 하는지 정리해 보려 한다.
이 글에서 다룰 문제
통합 앱 구조에서 프로파일 관리가 정리되지 않으면 다음과 같은 문제가 생긴다.
- 특정 앱에서만 보여야 하는 메뉴가 다른 앱에도 노출된다.
- 새 앱을 추가할 때 첫 화면이 잘못 열리거나 빈 화면이 표시된다.
- 지도, 검색, 설정, 상세 화면처럼 기능별 사용 여부가 코드 곳곳에 흩어진다.
- 권한 요청 기준이 앱별로 다르지만 공통 코드에서 한 번에 처리된다.
- 서버 URL, 관리자 URL, 광고 ID 같은 민감 설정이 registry와 코드에 섞인다.
- 한 프로파일 수정이 다른 프로파일의 회귀 오류로 이어져도 빨리 찾기 어렵다.
처음에는 이런 문제가 작아 보일 수 있다. 하지만 프로파일이 3개에서 10개, 20개로 늘어나면 어느 앱이 어떤 기능을 쓰는지 코드만 보고 추적하기 어려워진다. 그래서 통합 앱에서는 프로파일별 차이를 데이터로 관리하고, 화면 코드는 그 데이터를 읽어서 동작하도록 만드는 편이 안정적이다.
profile registry란 무엇인가
profile registry는 앱별 설정을 모아둔 기준표다. React Native 앱 기준으로 보면 하나의 profileId를 기준으로 앱 이름, 엔진 타입, 그룹, 첫 화면, 기능 플래그, 권한, 링크 정책 등을 정의하는 구조라고 볼 수 있다.
예를 들어 관광 앱, 파일 데이터 앱, 등산 앱, 유틸리티 앱을 하나의 React Native 코드베이스에서 관리한다면 각각의 프로파일은 서로 다른 성격을 가질 수 있다.
| 설정 항목 | 예시 | 사용 목적 |
|---|---|---|
profileId |
sampleTourBusan |
빌드, 설치, 테스트에서 사용하는 고유 키 |
displayName |
샘플 관광 앱 | 앱 제목과 화면 표시명 |
engine |
tour, filedata, utility |
앱 기능 계열 분류 |
group |
tour-regional, file-data |
회귀 테스트와 문서 분류 기준 |
initialRoute |
Home, Map, List |
앱 실행 후 첫 화면 결정 |
features |
map, search, settings |
화면과 기능 노출 여부 결정 |
permissions |
location, camera |
권한 요청 기준 |
links |
privacy, support |
외부 링크와 정책 페이지 연결 |
프로파일별 차이를 화면 컴포넌트 내부에서 직접 판단하지 않는 것이 좋을 것 같다. 화면은 현재 프로파일이 지도 기능을 지원하는지, 설정 탭을 보여도 되는지처럼 registry에서 계산된 값을 받아서 렌더링하는 구조가 좋다.
registry에 넣을 값과 넣지 말아야 할 값
profile registry가 커지면 모든 설정을 다 넣고 싶어질 수 있다. 하지만 registry는 공개 가능한 앱 구성 정보와 민감 설정을 분리해서 다뤄야 한다. 실제로 구현해보면 이 경계가 생각보다 흐려지기 쉽다.
| 구분 | registry에 적합한 값 | 주의할 값 |
|---|---|---|
| 앱 식별 | profileId, displayName, group, engine |
실제 내부 관리자 이름이 드러나는 값 |
| 화면 구성 | initialRoute, 탭 순서, 기능 사용 여부 |
관리자 전용 화면 URL |
| 기능 정책 | 지도 사용 여부, 검색 사용 여부, 설정 화면 여부 | API 키, 광고 ID, 토큰 |
| 권한 | 위치 권한 필요 여부, 카메라 사용 여부 | 사용자 식별자, 내부 계정 정보 |
| 링크 | 개인정보 처리방침 URL, 고객지원 URL | 비공개 관리자 페이지 URL |
| 빌드 정보 | flavorName, applicationId 별도 매핑 키 |
keystore 경로와 비밀번호 |
블로그 예제나 공개 문서에 registry 구조를 설명할 때는 실제 서버 URL, 관리자 URL, API 키, 광고 ID, Firebase 설정값, keystore 정보가 들어가지 않도록 예시값으로 바꿔야 한다. registry는 편리한 만큼 민감 정보가 모이기 쉬운 파일이기도 하다.
기본 설계 예시
아래 코드는 실제 사용했던 코드에서 민감정보를 수정한 버전이다. TypeScript 기반 React Native 앱에서 프로파일 설정을 타입으로 고정하고, 화면에서는 helper 함수를 통해 기능 지원 여부를 확인하는 형태다.
type Engine = 'tour' | 'filedata' | 'utility' | 'hybrid';
type RouteName = 'Home' | 'Map' | 'List' | 'Settings';
type FeatureFlags = {
map: boolean;
search: boolean;
settings: boolean;
externalLinks: boolean;
};
type ProfileConfig = {
profileId: string;
displayName: string;
applicationId: string;
engine: Engine;
group: string;
initialRoute: RouteName;
features: FeatureFlags;
permissions: string[];
};
export const profileRegistry: Record<string, ProfileConfig> = {
sampleTourBusan: {
profileId: 'sampleTourBusan',
displayName: '샘플 관광 앱',
applicationId: 'com.example.tour.busan',
engine: 'tour',
group: 'tour-regional',
initialRoute: 'Home',
features: {
map: true,
search: true,
settings: true,
externalLinks: true,
},
permissions: ['location'],
},
sampleFileData: {
profileId: 'sampleFileData',
displayName: '샘플 파일 데이터 앱',
applicationId: 'com.example.filedata',
engine: 'filedata',
group: 'file-data',
initialRoute: 'List',
features: {
map: true,
search: true,
settings: true,
externalLinks: false,
},
permissions: [],
},
};
export function getProfile(profileId: string): ProfileConfig {
const profile = profileRegistry[profileId];
if (!profile) {
throw new Error(`Unknown profileId: ${profileId}`);
}
return profile;
}
export function hasFeature(
profile: ProfileConfig,
feature: keyof FeatureFlags,
): boolean {
return profile.features[feature] === true;
}
이 정도만 분리해도 화면 코드에서 profileId별 조건문을 직접 쓰는 일이 줄어든다. 예를 들어 지도 탭을 보여줄지 판단할 때 profileId === 'sampleTourBusan' 같은 비교를 화면에 넣는 대신 hasFeature(profile, 'map')으로 확인할 수 있다.
처음에는 helper 함수 하나를 거치는 것이 조금 돌아가는 방식처럼 느껴질 수 있지만 프로파일이 늘어날수록 화면 안의 조건문보다 registry와 helper에 모아둔 판단 로직이 훨씬 관리하기 편하다.
initialRoute를 registry에서 관리해야 하는 이유
여러 앱을 통합할 때 첫 화면은 생각보다 자주 달라진다. 관광 앱은 홈 화면이 먼저 필요할 수 있고, 파일 데이터 앱은 리스트나 지도 화면이 더 자연스러울 수 있다. 유틸리티 앱은 특정 기능 화면으로 바로 들어가야 할 수도 있다.
첫 화면을 코드에 고정하면 새 프로파일을 추가할 때마다 네비게이션 코드를 수정해야 한다. 반대로 initialRoute를 registry에 넣어두면 앱별 시작 화면을 데이터로 관리할 수 있다.
function RootNavigator({profileId}: {profileId: string}) {
const profile = getProfile(profileId);
return (
<Stack.Navigator initialRouteName={profile.initialRoute}>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Map" component={MapScreen} />
<Stack.Screen name="List" component={ListScreen} />
<Stack.Screen name="Settings" component={SettingsScreen} />
</Stack.Navigator>
);
}
다만 initialRoute만 바꾼다고 모든 문제가 해결되지는 않는다. 첫 화면에서 필요한 데이터, 권한, 네트워크 상태, 빈 데이터 화면까지 같이 확인해야 한다.
이 부분은 처음 구현할 때 놓치기 쉽다. 라우트는 정상적으로 열리지만 데이터 조건이 맞지 않아서 첫 화면이 비어 보이는 경우가 생길 수 있기 때문이다. 그래서 initialRoute를 바꿀 때는 화면 진입 자체뿐 아니라 초기 데이터 상태까지 같이 봐야 한다.
feature flag는 화면 숨김이 아니라 계약에 가깝다
feature flag를 단순히 메뉴를 숨기는 용도로만 보면 나중에 문제가 생긴다. 예를 들어 map: false인 프로파일은 지도 탭만 숨기면 끝이 아니다. 지도 상세 이동 버튼, 위치 권한 요청, 외부 지도 앱 호출, 관련 테스트도 함께 비활성화되어야 한다.
| feature flag | 함께 확인할 항목 |
|---|---|
map |
지도 탭, 상세 화면 길찾기, 위치 권한, marker 데이터 |
search |
검색 입력창, 검색 결과 빈 화면, 필터 조건 |
settings |
설정 탭, 개인정보 처리방침, 고객지원 링크 |
externalLinks |
외부 브라우저 이동, 앱 스킴, 링크 허용 목록 |
push |
FCM 토큰, 알림 권한, 알림 클릭 시 이동 화면 |
이렇게 보면 feature flag는 단순 UI 옵션이 아니라 프로파일 계약에 가깝다. 특정 기능을 켜면 그 기능에 필요한 화면, 권한, 데이터, 테스트가 함께 준비되어야 한다.
반대로 특정 기능을 끈다면 관련 진입점도 같이 막혀야 한다. 탭에서는 보이지 않는데 딥링크나 상세 버튼을 통해 접근할 수 있다면 사용자는 빈 화면이나 오류 화면을 만나게 될 수 있다.
새 프로파일 추가 절차
새 앱 프로파일을 추가할 때는 코드부터 수정하기보다 아래 순서로 점검하는 것이 좋다.
profileId,applicationId,displayName을 정한다.engine과group을 기존 분류 중 어디에 둘지 결정한다.- 첫 화면으로 사용할
initialRoute를 선택한다. - 지도, 검색, 설정, 외부 링크 같은
feature flag를 채운다. - 필요한 권한과 권한 요청 시점을 확인한다.
- 개인정보 처리방침, 고객지원 링크처럼 공개 가능한 링크만 연결한다.
- 단일 프로파일 빌드와 설치 명령을 실행한다.
- 대표 프로파일 회귀 테스트를 함께 확인한다.
- registry validator나 smoke test가 있다면 새 프로파일을 대상에 포함한다.
이 절차를 문서로 남겨두면 새 앱을 추가할 때 작업자가 바뀌어도 기준이 흔들리지 않는다. 처음 작업할 때는 체크리스트가 조금 번거롭게 느껴질 수 있지만, 프로파일이 많아질수록 체크리스트가 실수를 줄여주는 안전장치가 된다.
selectedProfiles와 회귀 테스트 기준
통합 앱에서는 전체 프로파일을 매번 빌드하기 어렵다. 그래서 selectedProfiles처럼 특정 프로파일만 선택해서 빌드하거나 설치하는 흐름을 둘 수 있다. 중요한 점은 선택 빌드 성공에서 끝내지 않고, 대표 프로파일 회귀 테스트를 함께 설계하는 것이다.
| 테스트 범위 | 확인 목적 | 예시 |
|---|---|---|
| 단일 프로파일 빌드 | 새 프로파일이 빌드되는지 확인 | selectedProfiles=sampleTourBusan |
| 대표 프로파일 smoke test | 공통 코드 변경이 대표 앱에 영향을 주는지 확인 | 관광 대표, 파일 데이터 대표 |
| 기능별 테스트 | feature flag가 화면과 권한에 반영되는지 확인 |
map, search, settings |
| 링크 테스트 | 외부 링크와 개인정보 링크가 안전한지 확인 | 허용 목록, 빈 링크 처리 |
| 빈 데이터 테스트 | 데이터가 없을 때 화면이 깨지지 않는지 확인 | 빈 리스트, 로딩 실패 |
처음에는 단일 앱만 보며 수정하기 쉽다. 하지만 profile registry를 건드리는 작업은 다른 프로파일에도 영향을 줄 수 있다. 그래서 최소한 같은 group의 대표 프로파일 하나는 같이 확인하는 습관을 들이는 편이 좋다.
자주 하는 실수
화면 컴포넌트 안에 profileId 조건문을 계속 추가하는 경우가 있다. 한두 개일 때는 빠르게 보이지만, 앱이 늘어나면 특정 프로파일 조건이 어디에 있는지 찾기 어려워진다. 가능하면 registry와 helper 함수에서 판단하고, 화면은 결과만 받아 렌더링하는 편이 좋다.
feature flag를 UI 숨김 정도로만 보는 경우도 있다. 지도 기능을 끄면 지도 탭만 숨기는 것이 아니라 위치 권한, 길찾기 버튼, 지도 데이터 로더, 테스트 케이스까지 함께 확인해야 한다.
registry에 민감 설정을 같이 넣는 경우도 놓치기 쉽다. 통합 설정 파일은 여러 사람이 자주 열어보는 파일이 되기 쉽기 때문에 공개 가능한 구성 정보와 비밀값을 분리해야 한다. 특히 블로그 글로 정리할 때는 실제 값이 아니라 예시값으로 바꿔서 설명하는 것이 안전하다.
새 프로파일만 확인하고 기존 대표 프로파일을 확인하지 않는 경우도 있다. registry 구조를 바꾸면 공통 네비게이션, 설정 화면, 링크 처리에 영향이 생길 수 있다. 작은 수정이라도 대표 프로파일 smoke test를 같이 확인해보는 것이 좋다.
추천하는 관리 방식
프로파일 수가 적을 때는 TypeScript 파일 하나로 registry를 관리해도 충분하다. 하지만 프로파일이 많아지면 TSV, JSON, YAML 같은 catalog 파일을 기준으로 두고, TypeScript registry를 생성하거나 검증하는 방식도 고려할 수 있다.
| 규모 | 추천 방식 | 이유 |
|---|---|---|
| 1~3개 | TypeScript 상수 | 빠르게 이해하고 수정하기 쉬움 |
| 4~10개 | TypeScript registry + validator | 누락 필드와 잘못된 route를 검증하기 좋음 |
| 10개 이상 | catalog 파일 + 생성/검증 스크립트 | 반복 설정을 표로 관리하기 좋음 |
| 배포 프로파일 다수 | registry + 배포 matrix 문서 | Firebase, keystore, Play Console 확인이 필요함 |
이런 구조를 검토한다면 처음부터 거창한 자동화보다 필수 필드 누락을 막는 validator부터 넣는 편이 좋다. 새 프로파일을 추가할 때 profileId, applicationId, initialRoute, features 같은 값이 비어 있으면 빌드 전에 알려주는 것만으로도 실수를 꽤 줄일 수 있다.
결론
React Native 통합 앱에서 profile registry는 단순 설정 파일이 아니라 여러 앱을 안정적으로 운영하기 위한 기준표다. 앱별 이름, 첫 화면, 기능 플래그, 권한, 링크 정책을 한 곳에서 관리하면 새 프로파일을 추가할 때 확인해야 할 범위가 명확해진다.
중요한 것은 모든 것을 registry에 넣는 것이 아니다. 공개 가능한 구성 정보와 민감 설정을 분리하고, 화면 코드에는 프로파일별 조건문이 흩어지지 않게 관리하는 것이 더 중요하다.
또한 feature flag를 화면 숨김 정도로만 보면 안 된다. 기능 플래그는 권한, 데이터, 링크, 테스트까지 연결된 계약에 가깝게 다뤄야 한다. 이 기준이 있어야 새 프로파일을 추가하거나 기존 프로파일을 수정할 때 사이드 이펙트를 줄일 수 있다.
프로파일이 늘어나는 프로젝트라면 먼저 작은 registry를 만들고, 그다음 validator와 대표 프로파일 smoke test를 붙여보는 것이 좋다. 그렇게 하면 코드 곳곳에 흩어진 조건문을 줄이고, 새 앱을 추가할 때도 확인해야 할 범위가 훨씬 명확해진다.
참고 자료
- React Native 공식 문서의 Navigation, Platform, AppState 관련 문서
- Android 공식 문서의 앱 권한, 빌드 변형, 앱 링크 관련 문서
- 프로젝트 내부 profile registry, catalog, selectedProfiles, smoke test 문서
'개발 노트 > React Native' 카테고리의 다른 글
| [React Native] 통합 앱에서 프로파일별 controller와 model을 분리하는 기준 (0) | 2026.05.20 |
|---|---|
| [React Native] 통합 앱에서 프로파일별 화면 접근을 route guard로 제한하는 방법 (0) | 2026.05.20 |
| [React Native] 통합 앱에서 일반 기기와 태블릿 화면을 함께 최적화하는 기준 (0) | 2026.05.19 |
| [React Native] 통합 앱에서 selectedProfiles로 빌드 대상을 관리하는 방법 (0) | 2026.05.19 |
| [React Native] 통합 앱에서 프로파일 회귀 테스트를 설계하는 방법 (0) | 2026.05.19 |