[React Expo] 시작해보기

현재 다니고 있는 회사에서 갑작스럽게 안드로이드 기반 OS에서 실행되는 서비스를 개발해야할 것 같다는 이야기를 들어
순수 Native 앱이 아닌 그나마 빠르게 개발할 수 있는 React Native로 개발해보는 것으로 방향을 잡았습니다
리액트 네이티브와 Expo 차이는 뭘까?
| 비교항목 | 리액트 네이티브 | Expo |
| 개념 | React 프레임워크(엔진자체) | 리액트 네이티브 기반으로 만든 툴킷 |
| 네이티브 폴더 | ios/android 폴더 노출되어 직접관리 | ios/android 폴더를 숨겨두고 Expo가 알아서 관리 |
| 초기 세팅 | Xcode, Android Studio, 환경변수 설정 필수 (매우 복잡) | Node.js와 스마트폰만 있으면 5분 만에 끝Expo Go 어플을 설치해야함! |
| 네이티브 코드수정 | Java, Swift, Objective-C 코드를 직접 수정 가능 | 기본적으로 불가능했으나, 최근 버전에서는 커스텀 가능 |
| 빌드/배포 | 내 컴퓨터 자원을 써서 직접 빌드 | Expo의 클라우드 서버(EAS)를 통해 윈도우에서도 빌드 가능 |
네이티브 폴더
네이티브 폴더를 직접관리하는 것 과 자동으로 관리한다는 말은 각 os에 구동을 하기 위해 관련된 설정 파일(네이티브)들과 연관된 기능을 개발하거나 수정한다 했을때 각 os의 기능을 맡는 Swift , Kotlin 등의 파일을 직접 수정해야하지만 Expo의 미리 개발된 기능들(푸쉬알림 등)에 대해서 수정을 할 경우 따로 네이티브 파일을 수정할 필요없이 루트에 있는 app.json 설정 파일에 필요한 옵션을 적어두거나 Expo가 미리 만들어 둔 자바스크립트 API를 호출하기만 하면 각 os에 맞는 네이티브 코드가 자동으로 생성되며 반영됩니다
초기세팅
과거 유니티엔진으로 개발을 해보면서 알게된 사실이었지만 안드로이드배포를 위해 초기개발환경 셋팅이 굉장히 까다로운 부분이 많았습니다 안드로이드 스튜디오설치 및 테스트할 스마트폰의 버전과 맞는 SDK 설치하고 환경변수 셋팅까지 이 중에서 하나라도 박자가 안맞으면 뿜어대는 오류들 상당히 골치아팠는데 Expo를 사용하면 이 문제가 해결됩니다 이러니 안쓰고 버틸수 없죠
단점은 없을까?
물론 완벽하지 않습니다 Expo는 카메라, GPS, 푸시알림과 같은 주요 네이티브 기능을 Expo에서 미리 API로 개발을 해뒀기 때문에 끌어다 사용하면 되는데 특수한 경우(금융 앱 보안모듈, 결제 SDK) 등 C계열 또는 네이티브 코드로 작성된 라이브러리들을 연결하여 사용하기가 상당히 까다로운 부분이 있다고합니다 그리고 순수 리액트 네이티브로 빌드된 앱보다 당연히 용량 면에서 좀 더 무거운 점도 있습니다.
과거에는 Expo로 개발하다가 기능 제약의 한계로 인해 리액트 네이티브로 다시 개발하는 경우가 있었는데 최근 Expo에서 필요하면 언제든지 네이티브 코드를 생성하고 커스텀할 수 있도록 expo prebuild 기능을 지원해서 이 문제도 해결됐다고 합니다.
Expo 시작해보기 - 프로젝트 생성
npx create-expo-app@latest my-first-app --template blank-typescript
node 개발을 많이 해보신 분이라면 쉽게 이해할 수 있는 패키지 설치 명령어입니다 "my-first-app"가 프로젝트명입니다.
프로젝트 실행하기
npm run start

실행이 정상적으로 됐다면 위와 같이 QR코드와 url 이 나타나는데 플레이 스토어에서 Expo Go 어플을 설치한 뒤 QR을 스캔하면 실시간으로 변경내용을 모바일에서 확인할 수 있습니다

만약 위와같이 문제가 발생한다면
npx expo start --tunnel
위 명령어로 실행한뒤 다시 QR을 찍으면 정상적으로 연결이 됩니다 예상으로 네트워크환경 문제로인해 연결이 안되는 상황이 있을 수 있어 Tunnel 모드로 실행하면 방화벽을 무시하고 연결이됩니다.
화면 수정해보기
import React, { useState } from 'react';
import { StyleSheet, Text, View, TouchableOpacity } from 'react-native';
export default function App() {
const [count, setCount] = useState(0);
return (
<View style={styles.container}>
<Text style={styles.title}>안녕하세요, 시니어 멘토님!</Text>
<Text style={styles.counterText}>현재 카운트: {count}</Text>
{/* 웹의 <button> 대신 클릭(터치) 가능한 TouchableOpacity 사용 */}
<TouchableOpacity
style={styles.button}
onPress={() => setCount(count + 1)}
>
<Text style={styles.buttonText}>클릭해서 숫자 올리기</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1, // 화면 전체를 다 차지하겠다는 뜻
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center', // 세로 방향 기준으로 가운데 정렬
},
title: {
fontSize: 22,
fontWeight: 'bold',
marginBottom: 20,
},
counterText: {
fontSize: 18,
marginBottom: 20,
color: '#333',
},
button: {
backgroundColor: '#007AFF',
paddingVertical: 12,
paddingHorizontal: 24,
borderRadius: 8,
},
buttonText: {
color: '#fff',
fontSize: 16,
fontWeight: '600',
},
});
스마트폰 화면에 버튼이 정상적으로 보이고 클릭 시 카운트가 된다면 정상적으로 개발환경이 셋팅되었습니다.