티스토리 뷰
React Native 시작하기: 설치부터 expo-router 환경설정까지
YG - 96년생 , 강아지 있음, 개발자 희망 2025. 3. 11. 08:06React Native 시작하기: 설치부터 expo-router 환경설정까지
최근 모바일 앱 개발 프로젝트를 시작하면서 React Native를 선택했고, 라우팅 라이브러리를 결정하는 과정에서 많은 고민이 있었습니다. 이 글에서는 React Native 설치부터 라우팅 환경설정까지의 과정을 공유하려고 합니다.
특히 expo-router와 react-navigation 중 어떤 것을 선택할지에 대한 의사결정 과정과 expo-router 세팅 방법을 자세히 다룰 예정입니다.
1. React Native 설치하기
React Native를 시작하는 방법은 크게 두 가지가 있습니다: React Native CLI를 사용하는 방법과 Expo를 사용하는 방법이 있습니다.
이 글에서는 초기 개발 환경 구성이 더 간편한 Expo를 기준으로 설명하겠습니다. 그리고 공식문서에서도 expo를 권장하고 있었습니다.
Get Started with React Native · React Native
React Native allows developers who know React to create native apps. At the same time, native developers can use React Native to gain parity between native platforms by writing common features once.
reactnative.dev
Expo CLI 설치
먼저 Expo CLI를 글로벌로 설치합니다:
npm install -g expo-cli
프로젝트 생성
다음으로 새 프로젝트를 생성합니다:
npx create-expo-app MyAwesomeApp
cd MyAwesomeApp
TypeScript 템플릿을 사용하려면 다음과 같이 실행합니다:
npx create-expo-app MyAwesomeApp --template expo-template-blank-typescript
프로젝트 실행
생성된 프로젝트를 실행하려면:
npx expo start
이제 QR 코드가 터미널에 표시되며, Expo Go 앱(iOS/Android)을 통해 이를 스캔하여 앱을 실행할 수 있습니다.
2. 라우팅 라이브러리 선택: expo-router vs react-navigation
모바일 앱에서 화면 간 이동을 구현하려면 라우팅 라이브러리가 필요합니다. React Native에서 주로 사용되는 두 가지 옵션은 expo-router와 react-navigation입니다.
두 라이브러리 비교 분석
npm trend에서 확인해보면 @react-navigation/native와 expo-router 사이에 큰 차이가 있습니다:
라이브러리 Stars Issues 최신 버전 업데이트 생성일 패키지 크기
@react-navigation/native | 23,855 | 805 | 7.0.15 | 8일 전 | 6년 전 | ~2MB |
expo-router | 38,068 | 827 | 4.0.17 | 2개월 전 | 3년 전 | ~30MB |
expo-router (파일 기반 라우팅)
- Next.js 스타일의 파일 기반 라우팅을 제공
- app/ 디렉토리에 파일을 만들면 자동으로 경로가 생성됨
- 네비게이션 설정이 간단하고 URL 기반 이동이 편리
- 웹과 유사한 개발 경험 제공
- Expo 생태계와 긴밀하게 통합
📌 페이지 이동 방법
import { Link } from 'expo-router';
<Link href="/question">질문 페이지로 이동</Link>
react-navigation (컴포넌트 기반 라우팅)
- 파일 기반이 아닌 컴포넌트 기반의 라우팅 시스템
- Stack, Tab, Drawer 네비게이션 등 다양한 네비게이션 패턴 제공
- 기존 React Native 프로젝트에서 널리 사용됨
- 네비게이션을 수동으로 설정해야 함
📌 사용 예시 (컴포넌트 기반 설정)
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import HomeScreen from './screens/HomeScreen';
import QuestionScreen from './screens/QuestionScreen';
const Stack = createStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Question" component={QuestionScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
패키지 크기 차이가 성능에 미치는 영향
expo-router(30MB)와 react-navigation(2MB)의 크기 차이는 상당히 큰데, 이는 다음과 같은 이유에서 발생합니다:
- expo-router는 내부적으로 react-navigation을 포함함
- 파일 기반 라우팅 및 웹 호환성을 위한 추가 로직 포함
- URL 기반 라우팅을 지원하기 위한 부가 기능 포함
하지만 실제 프로덕션 빌드 시에는 트리 쉐이킹(Tree Shaking)이 적용되어 사용하지 않는 코드가 제거되므로, 최종 앱 크기에는 큰 차이가 없을 수 있습니다.
선택의 기준
결론적으로, 다음과 같은 이유로 expo-router를 선택했습니다:
- 개발 속도: 파일 기반 라우팅으로 빠른 개발이 가능
- 웹 호환성: 웹과 유사한 방식의 라우팅 제공
- 유지보수 용이성: 명시적인 파일 구조로 인한 코드 가독성
- 성능 차이 최소화: 트리 쉐이킹을 통해 최종 빌드에서는 큰 차이가 없을 것으로 예상
- Next.js와의 유사성: Next.js에 익숙한 개발자라면 전환이 쉬움
3. expo-router 설치 및 환경설정
Expo에서 파일 기반 라우팅(expo-router)을 활용하려면 몇 가지 필수 라이브러리를 설치해야 합니다. expo-router를 설정하는 과정과 함께 필요한 라이브러리들의 역할을 정리해보겠습니다.
3-1 expo-router 설치
이 명령어는 expo-router를 사용하기 위해 필요한 필수 라이브러리를 함께 설치하는 과정입니다. 각각의 역할을 살펴보겠습니다.
npx expo install expo-router react-native-safe-area-context react-native-screens expo-linking expo-constants expo-status-bar
3-2. 설치된 라이브러리별 설명
1️⃣ expo-router (파일 기반 라우팅)
🚀 Next.js 스타일의 라우팅을 React Native에서 구현하는 라이브러리
- 기존 react-navigation과 달리 파일 및 폴더 구조를 기반으로 자동 라우팅 지원
- 웹과 모바일에서 동일한 라우팅 패턴 유지 가능
- URL 기반 라우팅 (/home, /profile)을 지원
- 웹 브라우저 호환성 지원 (React Native for Web)
✅ 필수 여부: expo-router를 사용할 경우 반드시 필요
2️⃣ react-native-safe-area-context (노치 및 화면 안전 영역 지원)
📱 아이폰 노치(Notch)나 안드로이드 소프트 키 등이 가리지 않도록 레이아웃을 자동 조정
- SafeAreaView 컴포넌트를 제공하여 노치나 상태바를 피하도록 레이아웃 자동 조정
- 특히 iPhone X 이후 모델에서 상단, 하단 여백 문제 해결
✅ 필수 여부: 대부분의 React Native 프로젝트에서 필수
3️⃣ react-native-screens (네이티브 화면 전환 최적화)
🖥️ React Navigation 또는 expo-router에서 화면 전환을 네이티브 수준으로 최적화
- 기본적으로 네이티브 화면을 사용하여 성능을 향상시킴
- 화면 이동 시 부드러운 애니메이션 및 빠른 전환 가능
- iOS와 Android 네이티브 API 활용 (Fragment, UIViewController)
✅ 필수 여부: expo-router 또는 react-navigation을 사용할 경우 필수
4️⃣ expo-linking (앱 내부 및 외부 링크 처리)
🔗 딥링크(Deep Linking) 및 앱 내부 URL 라우팅을 쉽게 처리하는 라이브러리
- 앱 내에서 특정 화면을 URL을 통해 직접 접근 가능
- 예: myapp://profile/123 → 특정 프로필 페이지로 이동
- expo-router는 내부적으로 expo-linking을 사용하여 URL 기반 네비게이션을 제공
✅ 필수 여부: expo-router를 사용할 경우 필수
5️⃣ expo-constants (앱 환경 변수 및 기기 정보 제공)
📌 앱 실행 환경에 대한 정보를 제공하는 라이브러리
- Constants.expoConfig → 앱 이름, 버전 등 정보 제공
- Constants.deviceName → 사용 중인 기기 정보 제공
- Constants.manifest → 앱의 Expo 설정 정보 제공
✅ 필수 여부: expo-router 또는 환경 변수 사용 시 필수
6️⃣ expo-status-bar (상태바 스타일 관리)
📊 앱의 상태바(상단 바) 스타일을 제어하는 라이브러리
- 배경색, 투명도, 다크 모드 지원
- StatusBar.setBarStyle("light-content") 같은 기능 제공
- iOS 및 Android에서 일관된 상태바 스타일을 적용할 수 있도록 도와줌
✅ 필수 여부: 대부분의 Expo 프로젝트에서 사용됨 (선택적이지만 권장)
3-3. 라이브러리 필수 여부 정리
라이브러리 필수 여부설명
expo-router | ✅ 필수 | 파일 기반 라우팅 지원 |
react-native-safe-area-context | ✅ 필수 | 노치 및 안전 영역 대응 |
react-native-screens | ✅ 필수 | 네이티브 화면 전환 최적화 |
expo-linking | ✅ 필수 | URL 및 딥링크 지원 |
expo-constants | ✅ 필수 | 환경 변수 및 기기 정보 제공 |
expo-status-bar | 🔵 선택 (권장) | 상태바 스타일 관리 |
✅ 결론:
- expo-router를 사용할 경우 모두 설치하는 것이 적절한 선택!
- 만약 react-navigation을 사용할 경우 expo-router는 필요 없음.
- expo-status-bar는 선택 사항이지만 대부분의 앱에서 사용됨.
expo-router 설정을 위한 package.json 수정
package.json 파일에 다음 내용을 추가합니다:
{
"main": "expo-router/entry"
}
기본 폴더 구조 생성
앱의 기본 경로를 설정할 폴더 구조를 생성합니다:
mkdir -p app
첫 번째 화면 생성 (app/index.js)
app/index.js 파일을 생성하여 홈 화면을 정의합니다:
import { View, Text, StyleSheet } from 'react-native';
import { Link } from 'expo-router';
export default function Home() {
return (
<View style={styles.container}>
<Text style={styles.title}>홈 화면</Text>
<Link href="/about" style={styles.link}>
소개 페이지로 이동
</Link>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
padding: 24,
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 16,
},
link: {
fontSize: 16,
color: '#2e78b7',
},
});
두 번째 화면 생성 (app/about.js)
app/about.js 파일을 생성하여 소개 페이지를 정의합니다:
import { View, Text, StyleSheet } from 'react-native';
import { Link } from 'expo-router';
export default function About() {
return (
<View style={styles.container}>
<Text style={styles.title}>소개 페이지</Text>
<Link href="/" style={styles.link}>
홈으로 돌아가기
</Link>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
padding: 24,
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 16,
},
link: {
fontSize: 16,
color: '#2e78b7',
},
});
레이아웃 설정 (app/_layout.js)
app/_layout.js 파일을 생성하여 전체 앱의 레이아웃을 정의합니다:
import { Stack } from 'expo-router';
export default function Layout() {
return (
<Stack>
<Stack.Screen
name="index"
options={{
title: '홈',
}}
/>
<Stack.Screen
name="about"
options={{
title: '소개',
}}
/>
</Stack>
);
}
상단에 탭이 생긴걸 알 수 있었습니다.
4. 중첩 라우팅 및 고급 설정
더 복잡한 앱 구조를 위해 중첩 라우팅을 설정해 보겠습니다.
탭 네비게이션 구현 (app/(tabs)/_layout.js)
탭 네비게이션을 구현하기 위해 다음 파일을 생성합니다:
mkdir -p app/(tabs)
Expo Router에서 레이아웃 그룹 (Layout Group) 이해하기
📌 (tabs) 폴더명은 꼭 그대로 써야 할까?
👉 원하는 이름으로 변경할 수 있습니다!
expo-router에서 (tabs)처럼 소괄호(())를 사용한 폴더명은 레이아웃 그룹(Layout Group)을 의미한다. 레이아웃 그룹을 활용하면 특정 페이지들을 하나의 그룹으로 묶고, 공통 레이아웃을 적용할 수 있다.
📌 원하는 이름으로 변경 가능
예를 들어, 앱이 쇼핑 관련이라면 (shop)으로 바꿀 수도 있다.
mkdir -p app/(shop)
그리고 app/(shop)/_layout.js에서 탭 네비게이션을 설정하면 된다.
✅ 레이아웃 그룹의 역할
- 같은 그룹에 속한 모든 페이지에 공통 레이아웃을 적용할 수 있다.
- app/(shop)/_layout.js 안에서 <Tabs>를 정의하면, (shop) 그룹 안에 있는 페이지들은 자동으로 탭 네비게이션을 따르게 됨.
🎯 원하는 이름을 사용할 수 있음
즉, (tabs), (shop), (main), (dashboard) 등 원하는 이름을 사용 가능하며, 단순히 UI 그룹을 묶는 역할을 한다. 폴더명을 상황에 맞게 설정하면 가독성과 유지보수성이 향상된다!
app/(tabs)/_layout.js 파일을 생성합니다:
import { Tabs } from 'expo-router';
import { Ionicons } from '@expo/vector-icons';
export default function TabLayout() {
return (
<Tabs>
<Tabs.Screen
name="index"
options={{
title: '홈',
tabBarIcon: ({ color }) => <Ionicons name="home" size={24} color={color} />,
}}
/>
<Tabs.Screen
name="profile"
options={{
title: '프로필',
tabBarIcon: ({ color }) => <Ionicons name="person" size={24} color={color} />,
}}
/>
<Tabs.Screen
name="settings"
options={{
title: '설정',
tabBarIcon: ({ color }) => <Ionicons name="settings" size={24} color={color} />,
}}
/>
</Tabs>
);
}
각 탭 페이지 생성
app/(tabs)/index.js, app/(tabs)/profile.js, app/(tabs)/settings.js 파일을 생성합니다:
import { View, Text, StyleSheet, Button } from "react-native";
import { useRouter } from "expo-router";
export default function HomeTab() {
const router = useRouter();
return (
<View style={styles.container}>
<Text style={styles.title}>홈 탭</Text>
<Button
title="모달 열기"
onPress={() => router.push("/modal")} // 모달 화면으로 네비게이션
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
padding: 24,
},
title: {
fontSize: 24,
fontWeight: "bold",
marginBottom: 24,
},
});
다른 탭 페이지도 유사하게 구현합니다.
메인 레이아웃 수정 (app/_layout.js)
탭 네비게이션을 포함하도록 메인 레이아웃을 수정합니다:
메인 레이아웃을 수정하려면 app/_layout.js 파일에 탭 네비게이션을 포함해야 합니다. 이때 tabs/index.tsx를 첫 번째 화면으로 표시하려면, 기존의 app/index.tsx와 about.tsx를 삭제해야 합니다.
그 이유는 app/index.tsx가 우선순위가 가장 높아 tabs/index.tsx가 보이지 않게 되기 때문입니다.
import { Stack } from "expo-router";
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen
name="modal"
options={{ presentation: "modal", title: "모달" }}
/>
</Stack>
);
}
모달 화면 추가 (app/modal.js)
app/modal.js 파일을 생성하여 모달 화면을 정의합니다:
import { View, Text, StyleSheet, Button } from 'react-native';
import { useRouter } from 'expo-router';
export default function Modal() {
const router = useRouter();
return (
<View style={styles.container}>
<Text style={styles.title}>모달 화면</Text>
<Button
title="닫기"
onPress={() => router.back()}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
padding: 24,
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 16,
},
});
app/(tabs)/index.tsx에서 모달 열기 버튼을 만들어줬습니다.
// app/(tabs)/index.js
import { View, Text, StyleSheet, Button } from "react-native";
import { useRouter } from "expo-router";
export default function HomeTab() {
const router = useRouter();
return (
<View style={styles.container}>
<Text style={styles.title}>홈 이거 맞나 탭</Text>
<Button
title="모달 열기"
onPress={() => router.push("/modal")} // 모달 화면으로 네비게이션
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
padding: 24,
},
title: {
fontSize: 24,
fontWeight: "bold",
marginBottom: 24,
},
});
동적 라우팅 구현 (app/item/[id].js)
동적 라우팅을 구현하기 위한 파일을 생성합니다:
mkdir -p app/item
app/item/[id].js 파일을 생성합니다:
import { View, Text, StyleSheet } from 'react-native';
import { useLocalSearchParams } from 'expo-router';
export default function ItemDetail() {
const { id } = useLocalSearchParams();
return (
<View style={styles.container}>
<Text style={styles.title}>아이템 상세</Text>
<Text style={styles.id}>아이템 ID: {id}</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
padding: 24,
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 16,
},
id: {
fontSize: 18,
color: '#666',
},
});
app/_layout.tsx
item/[id]로 접속했을 때 화면을 보여주도록 합니다.
import { Stack } from "expo-router";
type ItemRouteParams = {
id: string;
};
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen
name="modal"
options={{ presentation: "modal", title: "모달" }}
/>
<Stack.Screen
name="item/[id]"
options={({ route }) => {
const { id } = route.params as ItemRouteParams; // 강제로 타입을 지정
return {
title: `아이템 ${id}`,
};
}}
/>
</Stack>
);
}
app/(tabs)/profile.tsx
프로필에서 item/1로 이동하도록 해줬습니다.
import { useRouter } from "expo-router";
import { View, Text, StyleSheet, Button } from "react-native";
export default function ProfileTab() {
const router = useRouter();
return (
<View style={styles.container}>
<Text style={styles.title}>프로필 탭</Text>
<Button title="내 프로필 보기" onPress={() => router.push("/item/1")} />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
padding: 24,
},
title: {
fontSize: 24,
fontWeight: "bold",
},
});
5. 실행 및 확인
설정이 완료되었다면 앱을 실행하여 라우팅이 정상적으로 작동하는지 확인합니다:
npx expo start
결론
이번 글에서는 React Native 설치부터 expo-router를 사용한 라우팅 환경 설정까지의 과정을 살펴보았습니다. expo-router와 react-navigation 중 어떤 것을 선택할지에 대한 고민이 있었지만, 개발 속도와 유지보수 용이성을 고려하여 expo-router를 선택했습니다.
패키지 크기에 대한 우려가 있었지만, 실제 프로덕션 빌드에서는 트리 쉐이킹을 통해 최적화가 이루어지므로 최종 앱 성능에는 큰 영향이 없을 것으로 판단했습니다.
expo-router는 파일 기반 라우팅을 제공하여 Next.js와 유사한 개발 경험을 제공하며, 웹 URL 기반의 직관적인 라우팅 시스템을 통해 앱의 화면 구조를 명확하게 관리할 수 있다는 장점이 있습니다.
물론 프로젝트의 요구사항에 따라 react-navigation이 더 적합한 경우도 있을 수 있으므로, 자신의 프로젝트 특성에 맞게 선택하는 것이 중요합니다.
'react native' 카테고리의 다른 글
Expo에서 'os.availableParallelism is not a function' 오류 해결하기 (0) | 2025.03.11 |
---|---|
React Native 개발 시 Expo 네트워크 연결 문제 해결하기 (0) | 2025.03.11 |
React Native 에서 Admob 사용방법 / 광고 다는 방법 (0) | 2022.02.19 |
React Native Realm SDK 사용방법 , 설치법 (0) | 2022.02.19 |
React Native Navigation , Stack , Tabs 사용하는 방법 React Navigation 라이브러리 (0) | 2022.02.17 |
- Total
- Today
- Yesterday
- C언어
- 우아한테크코스
- javascript
- nodejs
- 프리온보딩
- createPortal
- React
- 원티드
- jest
- error
- 아차산
- 윤성우 열혈C프로그래밍
- 스토리 북
- 북클럽
- 프론트앤드
- NextRequest
- electron
- import/order
- NextApiRequest
- 노개북
- env
- TopLayer
- Storybook
- 위코드
- WSL2
- CLASS
- 초보
- 노마드코더
- nextjs
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |