티스토리 뷰

react native

React Native 시작하기: 설치부터 expo-router 환경설정까지

YG - 96년생 , 강아지 있음, 개발자 희망 2025. 3. 11. 08:06

React 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)의 크기 차이는 상당히 큰데, 이는 다음과 같은 이유에서 발생합니다:

  1. expo-router는 내부적으로 react-navigation을 포함함
  2. 파일 기반 라우팅 및 웹 호환성을 위한 추가 로직 포함
  3. URL 기반 라우팅을 지원하기 위한 부가 기능 포함

하지만 실제 프로덕션 빌드 시에는 트리 쉐이킹(Tree Shaking)이 적용되어 사용하지 않는 코드가 제거되므로, 최종 앱 크기에는 큰 차이가 없을 수 있습니다.

 

선택의 기준

결론적으로, 다음과 같은 이유로 expo-router를 선택했습니다:

  1. 개발 속도: 파일 기반 라우팅으로 빠른 개발이 가능
  2. 웹 호환성: 웹과 유사한 방식의 라우팅 제공
  3. 유지보수 용이성: 명시적인 파일 구조로 인한 코드 가독성
  4. 성능 차이 최소화: 트리 쉐이킹을 통해 최종 빌드에서는 큰 차이가 없을 것으로 예상
  5. 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이 더 적합한 경우도 있을 수 있으므로, 자신의 프로젝트 특성에 맞게 선택하는 것이 중요합니다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/03   »
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
글 보관함