React Native와 Expo란?
React Native는 Facebook(현 Meta)이 개발한 크로스 플랫폼 모바일 프레임워크입니다. JavaScript와 React를 사용하여 iOS와 Android 앱을 동시에 개발할 수 있습니다.
Expo는 React Native 위에 구축된 프레임워크로, 복잡한 네이티브 설정 없이 빠르게 앱을 개발하고 빌드할 수 있게 해줍니다.
왜 Expo를 선택하는가?
| 항목 | React Native CLI | Expo |
|---|---|---|
| 초기 설정 | Xcode, Android Studio 필수 | Node.js만 있으면 시작 |
| 빌드 | 로컬 빌드 (시간 소요) | EAS Build (클라우드) |
| OTA 업데이트 | 별도 설정 필요 | 내장 지원 |
| 네이티브 모듈 | 자유롭게 추가 | Expo SDK + Dev Client |
환경 설정
1. Node.js 설치
Expo는 Node.js 18 이상이 필요합니다.
# Node.js 버전 확인
node --version
# nvm을 사용하는 경우
nvm install 20
nvm use 20
2. Expo CLI 설치 및 프로젝트 생성
# 프로젝트 생성
npx create-expo-app@latest my-app
cd my-app
# 개발 서버 시작
npx expo start
Expo Go 앱을 스마트폰에 설치하고 QR코드를 스캔하면 즉시 앱을 확인할 수 있습니다.
프로젝트 구조
my-app/
├── app/ # 파일 기반 라우팅
│ ├── _layout.tsx # 루트 레이아웃
│ ├── index.tsx # 홈 화면
│ └── (tabs)/ # 탭 네비게이션
│ ├── _layout.tsx
│ ├── index.tsx
│ └── settings.tsx
├── components/ # 재사용 컴포넌트
├── assets/ # 이미지, 폰트
├── app.json # Expo 설정
└── package.json
핵심 컴포넌트 만들기
기본 화면 구성
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
export default function HomeScreen() {
return (
<View style={styles.container}>
<Text style={styles.title}>오늘의운동</Text>
<Text style={styles.subtitle}>매일 건강한 습관을 만들어보세요</Text>
<TouchableOpacity style={styles.button}>
<Text style={styles.buttonText}>운동 시작하기</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
padding: 20,
},
title: {
fontSize: 32,
fontWeight: 'bold',
marginBottom: 8,
},
subtitle: {
fontSize: 16,
color: '#666',
marginBottom: 32,
},
button: {
backgroundColor: '#333',
paddingVertical: 16,
paddingHorizontal: 32,
borderRadius: 12,
},
buttonText: {
color: '#fff',
fontSize: 18,
fontWeight: '600',
},
});
FlatList로 리스트 렌더링
대량의 데이터를 효율적으로 렌더링하려면 FlatList를 사용합니다.
import { FlatList, View, Text, StyleSheet } from 'react-native';
const exercises = [
{ id: '1', name: '팔굽혀펴기', sets: 3, reps: 15 },
{ id: '2', name: '스쿼트', sets: 4, reps: 12 },
{ id: '3', name: '플랭크', sets: 3, reps: '60초' },
{ id: '4', name: '버피', sets: 3, reps: 10 },
];
function ExerciseItem({ item }) {
return (
<View style={styles.item}>
<Text style={styles.name}>{item.name}</Text>
<Text style={styles.detail}>{item.sets}세트 × {item.reps}</Text>
</View>
);
}
export default function ExerciseList() {
return (
<FlatList
data={exercises}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <ExerciseItem item={item} />}
ItemSeparatorComponent={() => <View style={styles.separator} />}
/>
);
}
const styles = StyleSheet.create({
item: {
padding: 16,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
name: { fontSize: 16, fontWeight: '600' },
detail: { fontSize: 14, color: '#666' },
separator: { height: 1, backgroundColor: '#e5e5e5' },
});
네비게이션 (Expo Router)
Expo Router는 파일 시스템 기반 라우팅을 제공합니다.
탭 네비게이션 설정
// app/(tabs)/_layout.tsx
import { Tabs } from 'expo-router';
export default function TabLayout() {
return (
<Tabs screenOptions={{
tabBarActiveTintColor: '#333',
tabBarStyle: { backgroundColor: '#fff' },
}}>
<Tabs.Screen
name="index"
options={{ title: '홈', tabBarIcon: () => '🏠' }}
/>
<Tabs.Screen
name="workout"
options={{ title: '운동', tabBarIcon: () => '💪' }}
/>
<Tabs.Screen
name="history"
options={{ title: '기록', tabBarIcon: () => '📊' }}
/>
<Tabs.Screen
name="settings"
options={{ title: '설정', tabBarIcon: () => '⚙️' }}
/>
</Tabs>
);
}
상태 관리
useState와 useEffect
import { useState, useEffect } from 'react';
import { View, Text } from 'react-native';
export default function WorkoutTimer() {
const [seconds, setSeconds] = useState(0);
const [isRunning, setIsRunning] = useState(false);
useEffect(() => {
let interval;
if (isRunning) {
interval = setInterval(() => {
setSeconds(prev => prev + 1);
}, 1000);
}
return () => clearInterval(interval);
}, [isRunning]);
const formatTime = (s) => {
const min = Math.floor(s / 60);
const sec = s % 60;
return `${min.toString().padStart(2, '0')}:${sec.toString().padStart(2, '0')}`;
};
return (
<View style={{ alignItems: 'center', padding: 20 }}>
<Text style={{ fontSize: 48, fontWeight: 'bold' }}>
{formatTime(seconds)}
</Text>
</View>
);
}
EAS Build로 앱 빌드
1. EAS CLI 설치 및 설정
# EAS CLI 설치
npm install -g eas-cli
# Expo 계정 로그인
eas login
# 빌드 설정 초기화
eas build:configure
2. eas.json 설정
{
"build": {
"preview": {
"android": {
"buildType": "apk"
}
},
"production": {
"android": {
"buildType": "app-bundle"
},
"ios": {
"buildType": "archive"
}
}
}
}
3. 빌드 실행
# Android APK 빌드 (테스트용)
eas build --platform android --profile preview
# 프로덕션 빌드
eas build --platform android --profile production
# iOS 빌드 (Apple Developer 계정 필요)
eas build --platform ios --profile production
실전 팁
성능 최적화
- 불필요한 리렌더링 방지:
React.memo와useCallback활용 - 이미지 최적화:
expo-image라이브러리로 캐싱 및 지연 로딩 - 리스트 최적화:
FlatList의getItemLayout속성으로 스크롤 성능 향상
자주 사용하는 Expo SDK 패키지
# 로컬 데이터 저장
npx expo install expo-sqlite
# 카메라
npx expo install expo-camera
# 알림
npx expo install expo-notifications
# 광고 (AdMob)
npx expo install expo-ads-admob
결론
React Native + Expo 조합은 모바일 앱 개발의 진입 장벽을 크게 낮춰줍니다. 특히 Expo Router의 파일 기반 라우팅과 EAS Build의 클라우드 빌드 시스템은 개인 개발자에게 매우 유용합니다.
실제로 이 기술 스택을 활용하여 오늘의운동 앱을 개발하고 있으며, SQLite를 활용한 로컬 데이터 관리와 AdMob 광고 연동까지 구현하고 있습니다.