Expo Docs
Expo 공식 문서를 정리
소개
Expo는 Android와 iOS 애플리케이션을 쉽게 개발할 수 있는 프레임워크이다. 파일 기반의 라우팅, 네이티브 모듈의 표준 라이브러리 등을 제공한다.
프로젝트 생성
Expo는 Node.js 가 필요하며, create-expo-app
으로 생성된 프로젝트로 시작하는 것을 권장한다.
새로운 프로젝트를 만들려면 다음 명령어를 실행하면 된다.
1
npx create-expo-app@latest
--template
옵션을 추가할 수 있다
그 다음으로 개발 환경을 설정해보자.
개발 환경 설정
로컬 개발 환경을 설정해보자. 사용자에게 어떻게 보이는지 직접 보기 위해 실제 디바이스를 사용하는 것을 권장한다.
Xcode와 Watchman 설정
1. Xcode 설치
App Store에서 Xcode를 설치한다.
2. Xcode Command Line Tools 설치
Xcode를 열고, 설정(cmd + ,)으로 이동한다. Locations
로 이동한 후에 Command Line Tools에서 가장 최신 버전을 설치한다.
3. iOS Simulator 설치
Xcode > Settings > Components > Platform Support 에서 iOS Simulator를 설치한다.
4. Watchman 설치
Watchman은 파일 시스템의 변화를 감지하는 도구이다. 다음과 같이 설치할 수 있다.
1
2
brew update && \
brew install watchman
5. 프로젝트 설정
1. expo-dev-client
설치
프로젝트 루트 위치에서 다음 명령어를 실행한다.
1
npx expo install expo-dev-client
2. 디바이스에 USB 연결 후 개발자 모드 활성화
- iOS 디바이스에 USB를 연결한다. 디바이스의 잠금을 푼 후 신뢰가 뜰 경우 클릭한다.
- Xcode를 열고, 메뉴바에 Window > Device and Simulators 를 선택한다.
- iOS 디바이스에서 Settings > Privacy & Security를 열고, Developer Mode에 접근한다.
- Developer Mode를 클릭 한 후 Restart 버튼을 클릭한다.
- 디바이스가 재시작한 후 Xcode를 확인해보면 iPhone에 연결된 것을 볼 수 있다.
3. 프로젝트를 디바이스에 실행
- Xcode에서
ios/<yourproject>.xcworkspace
을 연다. 만약 ios 폴더가 존재하지 않을 경우npx expo prebuild -p ios
를 실행한다. - Xcode에서 첫 번째로 선택한 프로젝트에서 TARGETS의 Signing & Capabilities로 이동한다.
Automatically manage signing
을 선택하고Add Account
를 클릭하여 Apple Developer 계정을 연동한다.- Team을 등록한 계정의 팀으로 설정한다.
- 프로젝트 루트에 있는
app.json
에ios.bundleIdentifier
를 추가하여 Xcode가 앱 서명 단계에 대한 프로비저닝 프로필을 생성하도록 한다. - 프로젝트 루트에서 다음 명령을 실행한 후, 연결한 iPhone Device를 선택한다.
1
npx expo run:ios --device
개발 시작하기
1. 개발 서버 시작
다음 명령어로 개발 서버를 시작한다.
1
npx expo start
2. 디바이스에서 앱 열기
위 명령어를 실행하면 터미널에서 QR 코드를 확인할 수 있다. 디바이스에서 QR 코드를 스캔하여 앱을 열 수 있다.
파일 구조
- 기본 프로젝트 파일 구조
app
: 파일 기반의 앱 네비게이션을 포함한다. app의 파일 구조는 앱의 네비게이션을 결정한다.assets
: Android 와 iOS의 앱 아이콘으로 사용되는adaptive-icon.png
파일이 포함된다. 또한 프로젝트의 스플래시 스크린인splash.png
와 앱이 브라우저에서 실행되는 경우favicon.png
가 존재한다.components
: 앱의 라이트와 다크 모드의 색상 스키마에서 사용하는 텍스트 요소를 만드는 ThemedText.tsx 와 같은 React Native 컴포넌트들이 존재한다.constants
: 앱 전체의 색상 값 목록을 포함하는 Color.ts가 포함되어 있다.hooks
: 컴포넌트 간에 공통적인 동작을 공유할 수 있는 React Hooks 가 존재한다. 예를 들어,useThemeColor()
는 현재 테마 기반의 색상을 반환하는 훅이다.scripts
:app
을app-example
로 이동시키는rest-project.js
가 포함된다.app.json
: 프로젝트 설정이나 앱 설정이 포함된다.package.json
: 프로젝트의 의존, 스크립트, 메타데이터를 포함한다.tsconfig.json
: TypeScript 규칙을 포함한다.
프로젝트 초기화
다음 명령어로 보일플레이트 코드를 제거할 수 있다.
1
npm run reset-project
개발 도구
Expo CLI
Expo CLI는 개발 도구이며 새로운 프로젝트를 만들면 expo 패키지와 함께 자동으로 설치된다. npx
를 활용하여 사용할 수 있다. 이는 개발을 빠르게 할 수 있도록 설계됐다. 다음 명령어들은 개발하는 동안 사용되는 Expo CLI 이다.
명령어 | 설명 |
---|---|
npx expo start | 개발 서버를 시작한다 |
npx expo prebuild | Android와 iOS 빌드 |
npx expo run:android | Android 컴파일 |
npx expo run:ios | iOS 컴파일 |
npx expo install package-name | 새로운 라이브러리를 설치하거나 --fix 옵션으로 라이브러리를 검증하고 수정 |
npx expo lint | ESLint를 구성하고 설정 |
EAS CLI
EAS CLI는 Expo 회원으로 로그인하고 빌드, 업데이트, 배포 등 다양한 EAS 서비스로 애플리케이션을 컴파일하는데 사용된다. 또한, 다음 도구들을 사용할 수 있다.
- 앱스토어에 애플리케이션 배포
- 개발, 프리뷰, 운영 애플리케이션 생성
- OTA 업데이트
- 애플리케이션 인증 정보 관리
- iOS 기기에 대한 임시 프로비저닝 프로필 생성
Expo Doctor
Expo Doctor는 Expo 프로젝트의 이슈를 진단하는데 사용되는 CLI이다. 다음 명령어를 통해 사용할 수 있다.
1
npx expo-doctor
이 명령은 앱 설정, package.json, 의존 호환성, 설정 파일, 프로젝트의 전반적인 상태 등의 일반적인 문제에 대한 것을 프로젝트 코드베이스로부터 확인하고 분석한다.
Orbit
Orbit은 다음 기능들을 제공한다.
- 디바이스와 애뮬레이터에 EAS 빌드를 설치하고 실행한다.
- Android 나 iOS에서 EAS를 통해 설치하고 실행한다.
- Android 나 iOS에 snake 프로젝트를 실행한다
- Orbit은 Android .apk 나 iOS .app 등을 지원한다
Navigation: File-based routing
Expo Router
Expo Router는 React Native와 웹 애플리케이션을 위한 라우팅 프레임워크이다. 이를 통해 앱의 화면 간 탐색을 관리하고 여러 플랫폼(Android, iOS 및 웹)에서 동일한 구성 요소를 사용할 수 있다. 앱에서 라우팅 하기 위해 파일 기반의 방식을 사용한다. 또한 네이티브 네비게이션 기능을 제공하며 React Navigation을 기반으로 구축되었다.
app directory
app 디렉터리에 추가한 파일은 네이티브 앱에 라우팅되고, 웹에서도 동일한 경로의 URL에 반영된다.
route 생성
app 디렉터리에 index.tsx 파일을 포함하는 파일이나 중첩 디렉토리를 추가하여 경로를 생성한다.
앱의 초기 경로를 만들려면 다음과 같이 index.tsx를 앱 디렉터리에 추가하면 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import {View, Text, StyleSheet} from 'react-native';
export default function HomeScreen() {
return (
<View style={styles.container}>
<Text>Home Screen</Text>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}
});
app/index.tsx
파일은/
경로에 라우팅된다.
파일명 컨벤션
파일 기반 인덱스는 상위 디렉터리에 라우팅되고 경로 세그먼트를 추가하지 않는다. app/settings/index.tsx
로 파일 구조를 구성할 경우, 해당 파일은 /settings
로 라우팅된다.
app
index.tsx
(matches ‘/’)settings
index.tsx
(matches ‘/settings’)
_layout
파일
레이아웃 파일들은 헤더, 탭 바와 같은 공유 UI 요소를 서로 다른 경로 간에 유지되도록 하는데 사용된다.
프로젝트를 생성하면 기본적으로 app 디렉터리에 루트 레이아웃 파일이 포함된다(app/_layout).
app
index.tsx
(matches ‘/’)_layout
(Root layout)
Root layout
React Native 프로젝트는 싱글 루트 컴포넌트(App.js 나 index.js)로 구성된다. 마찬가지로 app 디렉터리의 레이아웃 파일(_layout.tsx)은 싱글 루트 컴포넌트로 간주된다.
다중 라우팅에서는 Expo Router인 루트 레이아웃 파일은 global providers, themes, styles을 주입하거나, assets와 fonts가 로드될 때 까지 splash screen 렌더링을 지연하거나, 앱의 루트 네비게이션 구조를 정의하는 등 여러 경로 간에 UI를 공유하는 데 사용된다.
다음 코드는 RootLayout
이라는 기본 React 구성 요소로 구성된다.
1
2
3
4
5
export default function RootLayout() {
return (
// ...
)
};
Expo Router를 사용하면
app/_layout.tsx
내부에 정의된 React providers에 앱의 모든 경로에서 접근할 수 있다. 성능을 개선하고 렌더링 횟수를 줄이려면 필요한 경로에만 사용되도록 해야 한다.
Stack navigator
Stack navigator는 앱에서 다른 경로들을 탐색하는 패턴이다. 화면 간 전환 및 탐색 기록 관리가 가능하다. 이는 웹 브라우저에서 탐색 상태를 처리하는 방식과 개념적으로 유사하다.
/details
경로는 추가하려면, details.tsx
파일을 생성하면 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { View, Text, StyleSheet } from 'react-native';
export default function DetailsScreen() {
return (
<View style={styles.container}>
<Text>Details</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
파일을 생성한 후에 다음과 같이 파일을 구성한다.
app
index.tsx
details.tsx
_layout
/
와 /details
경로를 탐색할 수 있도록 다음과 같이 레이아웃 파일을 수정한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { Stack } from 'expo-router';
export default function RootLayout() {
return (
<Stack
screenOptions={{
headerStyle: {
backgroundColor: '#f4511e',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
}}>
<Stack.Screen name="index" />
<Stack.Screen name="details" />
</Stack>
);
}
- 레이아웃 파일의
<Stack.Screen name={routeName}/>
컴포넌트는 스택에 경로를 정의할 수 있도록 해준다.
위의
screenOptions
는 스택의 모든 경로에 대해 옵션을 설정할 수 있도록 해준다.
경로 간 이동
Expo Router는 앱에서 경로간에 이동할 수 있도록 Link
라고 불리는 내장 컴포넌트를 사용한다. 이는 <a>
태그와 href
속성과 개념적으로 유사하다.
이를 Expo Router library로부터 임포트한 후, 이동할 라우트를 href
prop에 값으로 전달하여 사용할 수 있다. 예를 들어/
에서 /details
로 넘기기 위해 index.tsx
파일에 Link
컴포넌트를 추가해라.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import {View, Text, StyleSheet} from 'react-native';
import {Link} from "expo-router";
export default function HomeScreen() {
return (
<View style={styles.container}>
<Text>Home Screen</Text>
<Link href="/details">View details</Link>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}
});
Link
동작 방법
Link
는 기본적으로 자식을 <Text>
로 감싼다. 다른 버튼 컴포넌트를 사용하여 커스텀을 할 수도 있다.
Link
컴포넌트를 사용해 커스텀 버튼 컴포넌트를 감싸고, asChild
prop을 사용해 모든 prop을 Link
의 첫 번째 자식에게 전달해라. 더 자세한 것은 Link
컴포넌트의 prop을 확인해라.
Groups
그룹은 유사한 경로들이나 앱의 특정 섹션을 정리하기 위해 생성된다. 각 그룹은 레이아웃 파일을 가지고, 그룹화된 디렉터리는 괄호 안에 이름을 넣어야 한다(group)
.
예를 들어, app/(home)
디렉터리에 그룹화 할 /
와 /detail
경로가 있을 수 있다. 이는 다음과 같이 파일 구조를 수정해라
- app
_layout.tsx
- Root layout- (home)
index.tsx
- ‘/’details.tsx
- ‘/details’_layout_tsx
- Home layout
Root 레이아웃 파일도 변경되어 (home)
그룹을 포함하며, (home)/index
를 앱의 초기 경로로 사용한다.
1
2
3
4
5
6
7
8
9
import {Stack} from "expo-router";
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="(home)"/>
</Stack>
)
}
Tab navigator
tab navigator 는 tab bar를 사용하여 앱의 다른 섹션 간을 이동하는 일반적인 패턴이다. Expo Router는 Tab
navigation 컴포넌트를 제공한다.
예를 들어, 현재 파일 구조에서 Home(/
와 /details
)과 Settings(/settings
)과 같은 다른 두 섹션을 가질 수 있다. (tabs)
디렉터리를 추가하고, 기존에 존재하던 Home을 옮기고 settings.tsx
를 만들어라.
- app
_layout.tsx
- Root- (tabs)
_layout.tsx
- Tab- (home)
index.tsx
- ‘/’details.tsx
- ‘/details’_layout.tsx
- Home
settings.tsx
- ‘/settings’
(tabs)
안의 어느 파일이나 디렉터리는 tab navigator의 경로가 된다. tab bar를 사용하여 다른 경로 간에 이동을 하려면, (tabs)_layout
에 레이아웃 파일을 추가하고 TabLayout
컴포넌트에 추가하면 된다.
1
2
3
4
5
6
7
8
9
10
import {Tabs} from "expo-router";
export default function TabLayout() {
return (
<Tabs>
<Tabs.Screen name="(home)"/>
<Tabs.Screen name="settings"/>
</Tabs>
)
}
이처럼 동작하게 하려면 app/_layout.tsx
파일의 첫 번째 경로에 (tabs)
를 추가하면 된다.
1
2
3
4
5
6
7
8
9
import { Stack } from 'expo-router';
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="(tabs)" />
</Stack>
);
}
Not found routes
Expo Router는 404와 같은 경로를 다루기 위한 +not-found.tsx
파일을 제공한다. 이 파일은 모든 경로에 매칭되지 않을 경우에 사용된다.
app
디렉터리에 다음 파일을 생성해라.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { Link, Stack } from 'expo-router';
import { View, StyleSheet } from 'react-native';
export default function NotFoundScreen() {
return (
<>
<Stack.Screen options={{ title: "Oops! This screen doesn't exist." }} />
<View style={styles.container}>
<Link href="/">Go to home screen</Link>
</View>
</>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
Navigation: Dynamic routes
동적 경로는 URL에 포함된 동적 세그먼트를 기반으로 하나 이상의 경로를 매칭할 수 있게 해준다. 트는 고유 식별자와 같은 변수 형식이며, 앱은 이 세그먼트를 사전에 정확히 할 수 없다.
다음으로 동적 경로를 다루는 방법에 대해서 알아보자.
Dynamic route convention
경로의 동적 세그먼트는 대괄호로 구성된 파일 이름으로 생성된다(ex. [id].tsx
).
Create a dynamic route
동적 경로를 구성한 예시를 살펴보자.
- app
_layout.tsx
- Root layout- (tabs)
_layout.tsx
- Tab layoutsettings.tsx
- ‘/settings’- (home)
_layout.tsx
- Home layoutindex.tsx
- ‘/’- details
[id].tsx
- ‘/details/1’
위와 같은 파일 구조에서 [id]
는 details/[id].tsx
경로에 대한 정보를 표시하는 데 사용된다. 이 경로는 id
값에 따라 고유한 정보를 표시한다.
이러한 동적 세그먼트 컨벤션은 앱 사용자가 홈 화면에서 상세 화면으로 이동할 때 해당 경로의 동적 세그먼트에 대한 올바른 정보를 볼 수 있도록 해준다.
Use Link
to navigate to a dynamic route
정적 방식 또는 href
객체를 사용해 Link
컴포넌트에 쿼리 파라미터를 전달함으로써, 일반 라우트에서 동적 라우트로 이동할 수 있다.
예를 들어, 다음과 같은 코드는 동적 라우트에 정적인 쿼리 파라미터를 사용하여 이동시킬 수 있도록 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { Link } from 'expo-router';
import { View, Text, StyleSheet } from 'react-native';
export default function HomeScreen() {
return (
<View style={styles.container}>
<Text>Home</Text>
<Link href="/details/1">View first user details</Link>
<Link href="/details/2">View second user details</Link>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
동적 라우트로부터 가져온 pathname
과 params
를 href
객체에 전달하여 사용할 수도 있다.
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
import { Link } from 'expo-router';
import { View, Text, StyleSheet } from 'react-native';
export default function HomeScreen() {
return (
<View style={styles.container}>
<Text>Home</Text>
<Link
href={{
pathname: '/details/[id]',
params: { id: 'bacon' },
}}>
View user details
</Link>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
Access parameters from dynamic segments
URL의 동적 세그먼트는 라우트 컴포넌트의 라우트 파라미터에 접근할 수 있다. 예를 들어, 선택된 경로에 대해 URL 파라미터를 반환하는 useLocalSearchParam
를 사용할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { useLocalSearchParams } from 'expo-router';
import { View, Text, StyleSheet } from 'react-native';
export default function DetailsScreen() {
const { id } = useLocalSearchParams();
return (
<View style={styles.container}>
<Text>Details of user {id} </Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
/
에서/details/1
로 이동할 때,/details/1
이 선택되었으므로useLocalSearchParams
는{ id: '1' }
을 반환한다.
UI: Splash screen and app icon
스플래시 화면과 앱 아이콘은 모바일 앱의 기본 요소이다.
Splash screen
실행 화면과 같은 스플래시 화면은 앱을 열 때 처음 보이는 화면이다. 앱이 로딩되는 동안 스플래시 화면이 표시된 상태로 유지된다. SplashScreen API
를 사용해 스플래시 화면이 사라지는 동작에 대해 수정할 수도 있다.
expo-splash-screen
에는 스플래시 아이콘이나 배경 색상에 대한 속성을 설정할 수 있는 내장 설정 플러그인이 포함된다.
1. Create a splash screen icon
먼저 Figma template을 사용해서 스플래시 화면 아이콘을 만든다. 해당 디자인은 Android와 iOS를 위한 아이콘과 스플래시 이미지들의 최소한의 디자인을 제공한다.
- 권장 사항
- 1024x1024 이미지를 사용
.png
파일을 사용- 투명 배경 사용
2. Export the splash icon as a .png
스플래시 화면 아이콘을 .png
로 내보내기 한 후에 assets/images
에 저장한다. 기본적으로 Expo는 파일 이름으로 splash-icon.png
를 사용한다. 스플래시 화면 파일의 이름을 변경하기로 했다면, 다음 단계에서 해당 이름을 사용해야 한다.
3. Configure the splash screen icon
앱 설정 파일을 열고 plugins
에 다음과 같이 설정한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"expo": {
"plugins": [
[
"expo-splash-screen",
{
"backgroundColor": "#232323",
"image": "./assets/images/splash-icon.png",
"dark": {
"image": "./assets/images/splash-icon-dark.png",
"backgroundColor": "#000000"
},
"imageWidth": 200
}
]
]
}
}
새로운 스플래시 화면이 iOS에 나오지 않으면 다음 명령어를 사용해라.
1
npx expo run:ios --no-build-cache
App icon
앱 아이콘은 사용자의 홈 화면과 앱 스토어에서 보인다. 앱 설정에서 icon
값을 변경하여 바꿀 수 있다.
1
2
3
{
"icon": "./assets/images/icon.png"
}
UI: Safe areas
safe area를 생성하면 앱 화면의 콘텐츠가 올바르게 배치되도록 보장할 수 있다.
Use react-native-safe-area-context
library
react-native-safe-area-context
는 Android와 iOS 디바이스의 safe area 인셋을 처리하기 위한 유연한 API를 제공한다. 또한 <View>
대신에 SafeAreaView
컴포넌트를 제공하여, 화면 컴포넌트에서 safe area를 자동으로 처리할 수 있게 해준다.
Installation
기본 템플릿으로 구성된 프로젝트를 사용중이면 react-native-safe-area-context
를 설치할 필요없다. 설치가 필요하다면 다음 명령어를 실행하면 된다.
1
npx expo install react-native-safe-area-context
Usage
화면 컴포넌트를 컨텐츠를 감싸는 SafeAreaView
를 사용하면 된다. 이는 safe area 인셋이 적용된 <View>
이다.
1
2
3
4
5
6
7
8
9
10
import { Text } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
export default function HomeScreen() {
return (
<SafeAreaView style={{ flex: 1 }}>
<Text>Content is in safe area.</Text>
</SafeAreaView>
);
}
Usage with React Navigation
기본적으로 React Navigation은 safe area를 제공하고 react-native-safe-area-context
를 사용한다.
UI: System bars
system bar는 화면의 모서리에 위치한 UI 요소로, 필수적인 기기 정보와 네비게이션 컨트롤을 제공한다. 모바일 OS에 따라, 시스템 바에는 status bar(Android and iOS), caption bar(Android only), navigation ba(Android and iOS), home indicator(iOS only)가 포함된다.
이러한 컴포넌트들은 배터리 잔량, 시간, 알림 등의 기기 정보를 표시하고, 기기 인터페이스 어디에서든 직접 상호작용할 수 있도록 해준다. 예를 들어, 앱 사용자는 현재 어떤 앱을 사용 중이든 상관없이 status bar를 아래로 내려 빠른 설정과 알림에 접근할 수 있다.
system bar는 모바일 환경의 핵심 요소이며, 이를 올바르게 다루는 방법을 이해하는 것은 앱을 만드는 데 있어 중요하다.
Handling overlaps using safe areas
앱의 일부 컨텐츠는 시스템 바 뒤에 그려질 수 있다. 이를 처리하려면, 겹침을 피하고 시스템 바가 나타나도록 콘텐츠의 위치를 올바르게 조정해야 한다.
Safe ares and edge-to-edge layout on Android
Android의 edge-to-edge를 사용하기 전에는 translucent(반투명) 상태 바와 내비게이션 바를 사용하는 것이 일반적이였다. 이 방식에서는 콘텐츠가 이미 바 뒤에 그려졌기 때문에 safe area를 고려할 필요가 없었다. 지금은 safe area를 사용하여 컨텐츠가 시스템 바에 겹치지 않도록 해야 한다.
Customizing system bars
시스템 바는 앱의 디자인에 맞게 커스텀 될 수 있으며, 다른 시나리오에서 더 나은 가시성을 제공할 수 있다. Expo를 사용할 때 두 라이브러리가 존재한다. expo-status-bar
와 expo-navigation-bar
(Android only).
Status bar configuration
상태 바는 Android와 iOS에서 화면의 최상단에 표시된다. expo-status-bar
를 사용하여 커스텀을 할 수 있다. 이 라이브러리는 style
속성이나 setStatusBarStyle
메서드를 사용하여 앱이 실행되는 동안 상태 바를 수정할 수 있는 StatusBar
컴포넌트를 제공한다.
1
2
3
4
5
6
7
8
import { StatusBar } from 'expo-status-bar';
export default function RootLayout() {
<>
{/* Use light text instead of dark text in the status bar to provide more contrast with a dark background. */}
<StatusBar style="light" />
</>;
}
StatusBar
의 가시성을 설정하기 위해 hidden
속성을 true
로 설정하거나 setStatusBarHidden
메서드를 사용할 수 있다.
Android에서 edge-to-edge가 활성화된 경우, 불투명한 상태 표시줄에 의존하는 expo-status-bar
의 기능들은 사용할 수 없다. 스타일과 표시 여부만 커스텀 할 수 있으며, 다른 속성들은 무시되고 경고가 출력된다.
Navigation bar configuration (Android only)
안드로이드 기기에서, 네비게이션 바는 화면의 아래에 나타난다. expo-navigation-bar
라이브러리를 사용하여 이를 커스텀 할 수 있다. 이 라이브러리는 setStyle
메서드를 사용하는 네비게이션 바의 스타일을 설정할 수 있는 NavigationBar
컴포넌트를 제공한다.
1
2
3
4
5
6
7
8
9
import { NavigationBar } from 'expo-navigation-bar';
import { useEffect } from 'react';
useEffect(() => {
if (Platform.OS === 'android') {
// Set the navigation bar style
NavigationBar.setStyle('dark');
}
}, []);
NavigationBar
가시성을 설정하려면 setVisibilityAsync
메서드를 사용하면 된다.
Android에서 edge-to-edge가 활성화된 경우, 불투명한 내비게이션 바에 의존하는 expo-navigation-bar
의 기능은 사용할 수 없다. 스타일과 표시 여부만 커스텀할 수 있으며, 다른 속성들은 무시되고 경고가 출력된다.
UI: Fonts
Android와 iOS는 각자의 플랫폼 전용 기본 폰트를 제공한다. 일관된 사용자 경험과 앱 브랜딩을 향상시키기 위해 커스텀 폰트를 사용할 수도 있다.
이 가이드는 커스텀 폰트를 프로젝트에 추가하고 로드하는 다양한 방법을 다루며, 폰트와 관련된 추가 정보도 제공한다.
Add a custom font
프로젝트에 커스텀 폰트를 추가하는 방법은 두 가지가 존재한다.
- local assets에 폰트 파일을 추가. 예를 들어, 폰트 파일을
assets/fonts
디렉터리에 추가 - Google Font 패키지를 설치. 예를 들어,
@expo-google-fonts/inter
패키지를 설치
Supported font formats
Expo SDK는 공식적으로 Android, iOS, web 플랫폼 전반에서 OTF 및 TTF 폰트 포맷을 지원한다. 폰트가 다른 포맷이라면 프로젝트가 해당 포맷을 지원하기 위한 추가 설정을 해야 한다.
How to choose between OTF and TTF
사용하는 폰트가 OTF 와 TTF 둘 다 있을 경우, OTF를 권장한다. .otf
파일은 .ttf
파일보다 가볍기 때문이다. 경우에 따라 OTF는 특정 환경에서 TTF보다 약간 더 나은 렌더링을 제공하기도 한다.
Use a local font file
폰트 파일을 assets/fonts
디렉터리에 복사한다.
프로젝트에서 로컬 폰트 파일을 두 방법으로 사용할 수 있다.
expo-font
설정 플러그인을 사용하여 폰트 파일을 포함한다.- 런타임
useFonts
훅을 사용해 폰트 파일을 비동기적으로 로딩한다.
With expo-font
config plugin
expo-font
설정 플러그인은 프로젝트의 네이티브 코드에 하나 이상의 폰트 파일을 내장시킬 수 있다. 이는 다음과 같은 장점으로 인해 권장되는 방법이다.
- 기기에서 앱이 시작할 때 폰트를 즉시 사용할 수 있다.
- 비동기적으로 폰트를 로드하기 위한 코드가 필요하지 않다.
- 폰트는 앱에 번들되어 있기 때문에 앱이 설치된 모든 기기에서 일관되게 사용할 수 있다.
그러나 이 방법은 다음과 같은 제한이 있다.
- 이 방법은 개발용 빌드를 생성해야 하므로 Expo Go에서는 작동하지 않는다.
- SDK 50 이상에서 가능하다.
프로젝트에 폰트를 내장하려면 다음 과정을 따르면 된다.
1. expo-font
라이브러리 설치
1
npx expo install expo-font
2. 앱 설정 파일에서 플러그인 설정
앱 설정 파일에서 fonts
속성에 폰트 파일을 추가해야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
{
"expo": {
"plugins": [
[
"expo-font",
{
"fonts": ["./assets/fonts/Inter-Black.otf"]
}
]
]
}
}
3. 개발 빌드 및 어플 설치
설정 플러그인에 폰트를 넣은 후, 개발 빌드를 수행하고 기기에 설치한다.
특정 fontFamily
style prop 을 지정한 <Text>
를 사용할 수 있다.
1
<Text style={{ fontFamily: 'Inter-Black' }}>Inter Black.</Text>
With useFonts
hook
expo-font
라이브러리의 useFonts
훅은 폰트 파일을 비동기로 로드할 수 있도록 한다. 이 훅은 로딩 상태를 추적하며, 앱이 초기화될 때 폰트를 로드한다.
이 훅은 모든 Expo SDK 버전과 Expo GO에서 동작한다. useFonts
훅을 사용해 폰트를 로드하기 위해서는 다음 과정을 따르면 된다.
1. expo-font
와 expo-splash-screen
라이브러리 설치
1
npx expo install expo-font expo-splash-screen
expo-splash-screen
라이브러리는SplashScreen
컴포넌트를 제공하며, 이를 사용해 폰트가 로드되어 준비될 때까지 앱 렌더링을 지연시킬 수 있다.
2. useFonts
훅 매핑
프로젝트의 최상위 컴포넌트(ex. app/layout.tsx)에서 useFonts
훅을 사용해 폰트 파일을 매핑해라.
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
import { useFonts } from 'expo-font';
import * as SplashScreen from 'expo-splash-screen';
import {useEffect} from 'react';
SplashScreen.preventAutoHideAsync();
export default function RootLayout() {
const [loaded, error] = useFonts({
'Inter-Black': require('./assets/fonts/Inter-Black.otf'),
});
useEffect(() => {
if (loaded || error) {
SplashScreen.hideAsync();
}
}, [loaded, error]);
if (!loaded && !error) {
return null;
}
return (
)
}
3. <Text>
컴포넌트 설정
fontFamily
style prop 을 사용하여 <Text>
에 폰트를 설정한다.
1
<Text style={{ fontFamily: 'Inter-Black' }}>Inter Black</Text>
Use Google Fonts
Expo는 Google Fonts에 등록된 모든 폰트를 완벽하게 지원한다. 이는 @expo-google-fonts
라이브러리를 통해 사용할 수 있다. 이 라이브러리의 폰트 패키지 중 하나를 사용하면, 해당 폰트와 그 변형들을 빠르게 통합할 수 있다.
Google Font를 사용하는 방법은 두 가지이다.
- 설치한 폰트를
expo-font
설정 플러그인으로 포함한다. - 런타임에
useFonts
훅을 사용해 해당 폰트를 비동기적으로 로드한다.
With expo-font
config plugin
1. @expo-google-fonts/inter
설치
1
npx expo install expo-font @expo-google-fonts/inter
2. 앱 설정에 플러그인 구성
1
2
3
4
5
6
7
8
9
10
{
"plugins": [
[
"expo-font",
{
"fonts": ["node_modules/@expo-google-fonts/inter/Inter_900Black.ttf"]
}
]
]
}
3. 개발 빌드 및 설치
설정 플러그인에 폰트를 넣은 후, 개발 빌드를 수행하고 기기에 설치한다.
플랫폼 별로 다르게 폰트를 설정하려면 다음과 같이 적용하면 된다.
1
2
3
4
5
6
7
8
9
10
11
12
import { Platform } from 'react-native';
// Inside a React component:
<Text
style={{
fontFamily: Platform.select({
android: 'Inter_900Black',
ios: 'Inter-Black',
}),
}}>
Inter Black
</Text>
With useFonts
hook
1. 라이브러리 설치
1
npx expo install @expo-google-fonts/inter expo-font expo-splash-screen
2. useFonts
훅 매핑
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
// Rest of the import statements
import { Inter_900Black, useFonts } from '@expo-google-fonts/inter';
import * as SplashScreen from 'expo-splash-screen';
import {useEffect} from 'react';
SplashScreen.preventAutoHideAsync();
export default function RootLayout() {
const [loaded, error] = useFonts({
Inter_900Black,
});
useEffect(() => {
if (loaded || error) {
SplashScreen.hideAsync();
}
}, [loaded, error]);
if (!loaded && !error) {
return null;
}
return (
)
}
3. <Text>
사용
1
<Text style={{ fontFamily: 'Inter_900Black' }}>Inter Black</Text>
Additional information
- Minimal example
- Beyond OTF and TTF
- Platform built-in fonts
- Handle
@expo/vector-icons
initial load - Loading a remote font directly from the web
UI: Assets
static asset은 앱의 바이너리와 번들되는 파일이다. 이 파일 타입은 앱의 코드를 포함하는 JS 번들의 일부가 아니다. static assets는 이미지, 비디오, 음성, SQLite 데이터베이스 파일, 폰트 등이 포함된다. assets은 프로젝트 내에서 로컬로 제공되거나, 네트워크를 통해 원격으로 제공될 수 있다.
이 가이드는 프로젝트에서 static asset을 로드하고 사용하는 방법을 다룬다. 또한 assets을 최적화하고 최소화하는 방법에 대한 추가 정보도 제공한다.
Serve an asset locally
에셋이 프로젝트에 저장되어 있을 때 빌드 시 앱 바이너리에 포함되거나 런타임에 될 수 있다. 이를 require
이나 import
문을 통해 불러올 수 있다.
예를 들어, App.js 에서 example.png 이미지를 불러와서 렌더링 하려면 프로젝트의 assets/images 디렉터리로부터 이미지를 require
로 불러와 다음과 같이 <Image>
컴포넌트에 전달하면 된다.
1
<Image source={require('./assets/images/example.png')}
- 위 예시는 번들러가 불러온 이미지의 메타데이터를 읽어오고 자동으로 가로와 세로를 제공한다.
expo-images
와expo-file-system
과 같은 라이브러리는 로컬 에셋과 함께<Image>
컴포넌트와 유사하게 동작한다.
How are assets served locally
로컬에 저장된 에셋은 개발 환경에서 HTTP를 통해 제공된다. 이는 프로덕션 앱을 위한 빌드 타입에 앱 바이너리에 자동으로 번들되며 디바이스의 디스크로부터 제공된다.
Load an asset at build time with expo-asset
config plugin
빌드 시 에셋을 로드하려면, expo-asset
라이브러리의 설정 플러그인을 사용할 수 있다. 이 플러그인은 네이티브 프로젝트에 에셋 파일로 내장된다.
1. expo-asset
라이브러리 설치
1
npx expo install expo-asset
2. 앱 설정에 에셋 구성
앱 설정 파일에 expo-asset
설정 플러그인을 추가한다.
1
2
3
4
5
6
7
8
9
10
11
12
{
"expo": {
"plugins": [
[
"expo-asset",
{
"assets": ["./assets/images/example.png"]
}
]
]
}
}
3. 개발 빌드 및 컴포넌트와 에셋 사용
빌드 한 후 require
이나 import
문 없이 에셋을 불러오고 사용할 수 있다.
1
2
3
4
5
import { Image } from 'expo-image';
export default function HomeScreen() {
return <Image source={{ uri: 'example' }} style={{ width: 100, height: 100 }} />;
}
Load an asset at runtime with useAssets
hook
expo-asset
라이브러리의 useAssets
훅은 에셋을 비동기적으로 불러올 수 있게 한다. 이 훅은 에셋을 다운로드하여 로컬에 저장하며, 로딩이 완료되면 해당 에셋 인스턴스들의 목록을 반환한다.
1. expo-asset
라이브러리 설치
1
npx expo install expo-asset
2. useAsset
사용
1
2
3
4
5
6
7
8
9
10
import { useAssets } from 'expo-asset';
export default function HomeScreen() {
const [assets, error] = useAssets([
require('path/to/example-1.jpg'),
require('path/to/example-2.png'),
]);
return assets ? <Image source={assets[0]} /> : null;
}
Serve an asset remotely
에셋을 원격으로 제공할 때, 이는 빌드 시 앱 바이너리에 번들되지 않는다. 원격에 호스트되는 프로젝트의 에셋 리소스의 URL을 사용할 수 있다. 예를 들어, 원격 이미지를 렌더링 하기 위한 <Image>
컴포넌트에 URL을 넘기면 된다.
1
2
3
4
5
6
7
import { Image } from 'expo-image';
function App() {
return (
<Image source={{ uri: 'https://example.com/logo.png' }} style={{ width: 50, height: 50 }} />
);
}
Additional information
UI: Color themes
앱은 일반적으로 라이트와 다크 테마를 지원한다.
Configuration
지원할 화면 스타일을 설정하려면, 앱 설정에서 userInterfaceStyle
속성을 사용할 수 있다. 이 속성의 기본 값은 automatic
이다.
예시 설정은 다음과 같다.
1
2
3
4
5
{
"expo": {
"userInterfaceStyle": "automatic"
}
}
android.userInterfaceStyle
이나ios.userInterfaceStyle
에 원하는 값을 지정하여, 특정 플랫폼에 대해userInterfaceStyle
을 설정할 수 있다.
Detect the color scheme
색상 스키마를 감지하려면 다음과 같이 Appearance
나 useColorScheme
를 사용해라.
1
2
3
4
5
6
7
8
9
10
11
import { Appearance, useColorScheme } from 'react-native';
function MyComponent() {
let colorScheme = useColorScheme();
if (colorScheme === 'dark') {
// render some dark thing
} else {
// render some light thing
}
}
UI: Animation
React Native의 Animated API를 사용하여 애니메이션을 보여줄 수 있다. 하지만 애니메이션의 성능을 향상시키기를 원하면 react-native-reanimated
라이브러리를 사용해야 한다. 이 API는 부드럽고 강력하며 유지 관리하기 쉬운 애니메이션을 손쉽게 만들 수 있도록 해준다.
Installation
기본 템플릿을 활용해 프로젝트를 만들었으면 react-native-reanimated
설치를 하지 않아도 된다.
1
npx expo install react-native-reanimated
Usage
Minimal example
다음과 같이 react-native-reanimated
라이브러리를 사용하면 쉽게 애니메이션을생성할 수 있다.
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import Animated, {
useSharedValue,
withTiming,
useAnimatedStyle,
Easing,
} from 'react-native-reanimated';
import { View, Button, StyleSheet } from 'react-native';
export default function AnimatedStyleUpdateExample() {
const randomWidth = useSharedValue(10);
const config = {
duration: 500,
easing: Easing.bezier(0.5, 0.01, 0, 1),
};
const style = useAnimatedStyle(() => {
return {
width: withTiming(randomWidth.value, config),
};
});
return (
<View style={styles.container}>
<Animated.View style={[styles.box, style]} />
<Button
title="toggle"
onPress={() => {
randomWidth.value = Math.random() * 350;
}}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
box: {
width: 100,
height: 80,
backgroundColor: 'black',
margin: 30,
},
});
UI: Store data
데이터 저장은 앱에 구현된 기능에 있어 필수적일 수 있다. 저장하려는 데이터의 유형과 보안 요구사항에 따라 Expo 프로젝트에서 데이터를 저장하는 방법은 다양하다. 이 페이지에서는 다양한 라이브러리를 설명한다.
Expo SecureStore
expo-secure-store
는 키-값 쌍으로 기기 내에 암호화하여 안전하게 저장할 수 있는 방법을 제공한다.
Expo SecureStore API reference
Expo FileSystem
expo-file-system
은 기기 내에 파일 시스템 접근을 제공한다. Expo Go에서는 각 프로젝트 별로 분리된 파일 시스템을 갖으며 다른 Expo 프로젝트의 파일에는 접근할 수 없다. 그러나 이 파일 시스템은 로컬 파일시스템에 다른 프로젝트에 공유되는 내용을 저장할 수 있으며 다른 프로젝트에 로컬 파일을 공유한다. 또한 네트워크 URL로 업로드하고 다운로드하는 기능도 제공한다.
Expo SQLite
expo-sqlite
패키지는 WebSQL과 유사한 API를 통해 쿼리 할 수 있는 데이터베이스에 접근할 수 있도록 해준다. 이 데이터베이스는 앱을 재시작해도 영속화된다. 기존의 데이터베이스를 불러오거나 데이터베이스 여는 것, 테이블 생성, 아이템 삽입, 쿼리와 결과 조회, prepared statement를 사용할 수 있다.
Async Storage
Async Storage는 React Native 앱을 위한 비동기적이며 암호화되지 않은 영속적인 키-값 저장소이다. 이는 간단한 API로 되어 있고 소량의 데이터를 저장하기에 유용하다. 또한 사용자 선호도와 같은 암호화가 필요없는 데이터를 저장할 때 유용하다.
Development builds: Introduction
개발 빌드는 expo-dev-client
라이브러리를 포함한 앱의 “디버그(Debug)” 빌드를 지칭하는 용어이다. 이 라이브러리는 기본 React Native 개발 도구에 기능을 확장해주며, 네트워크 요청 검사 지원, 실행 중인 다양한 개발 서버 간 전환, EAS Update로 배포된 앱 버전 간 전환 등을 할 수있는 ‘런쳐’ UI를 제공한다.
자세한 내용은 링크를 참고하자.
Config plugins: Introduction
네이티브 모듈을 프로젝트에 추가하는 자동 설정이 가능하다. 경우에 따라 모듈이 더 복잡한 설정을 요구하기도 하며, 이때 설정 플러그인을 사용하면 네이티브 프로젝트를 직접 수정하지 않고도 자동으로 설정을 구성해 복잡도를 줄일 수 있다.
What is a config plugin
설정 플러그인은 앱 설정을 확장하고 앱의 사전 빌드 프로세스를 커스텀 하기 위한 시스템이다. 이는 기본적으로 포함되지않은 네이티브 모듈을 추가하거나, 추가 설정이 필요한 네이티브 코드를 삽입하는데 사용할 수 있다.
내부적으로 Expo CLI는 설정 플러그인을 사용해 프로젝트를 관리하기 위한 네이티브 코드를 생성하고 구성한다. 플러그인은 앱 아이콘 생성, 앱 이름 설정, AndroidManifest.xml 및 Info.plist 구성 등의 작업을 수행한다.
플러그인은 네이티브 프로젝트를 위한 번들러처럼 생각할 수 있으며, npx expo prebuild
명령은 모든 프로젝트 플러그인을 평가하여 프로젝트를 번들링하는 과정이라 볼 수 있다. 이 작업을 실행하면 android와 ios 디렉터리가 생성된다. 생성된 이후에는 해당 디렉터리를 수동으로 수정할 수 있지만, 그렇게 하면 이후 안전하게 다시 생성할 수 없고 수동 수정 내용이 덮어써질 수 있다.
Use a config plugin
Expo 설정 플러그인은 대부분 Node.js 모듈에서 제공되며, 다른 패키지처럼 프로젝트에 설치할 수 있다.
예를 들어, expo-camera
는 AndroidManifest.xml 과 Info.plist에 카메라 권한을 추가하는 플러그인이다. 이를 설치하기 위해 다음 명령을 실행하면 된다.
1
npx expo install expo-camera
설치 후 앱 설정에서 expo-camera
를 플러그인에 추가하면 된다.
1
2
3
4
5
{
"expo": {
"plugins": ["expo-camera"]
}
}
일부 설정 플러그인은 옵션을 전달해 구성을 커스텀할 수 있도록 유연성을 제공한다. 이를 위해, 첫 번째 인자로Expo 라이브러리 이름을, 두 번째 인자로 옵션을 담은 객체를 배열 형태로 전달하면 된다. 예를 들어, expo-camera
플러그인은 카메라 권한 메시지를 커스텀할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
{
"expo": {
"plugins": [
[
"expo-camera",
{
"cameraPermission": "Allow $(PRODUCT_NAME) to access your camera."
}
]
]
}
}
npx expo prebuild
를 실행하면 mods
가 컴파일 되고 네이티브 파일들이 수정된다.
이러한 변경 사항은 네이티브 프로젝트를 다시 빌드해야 적용된다. 예를 들어, Xcode로 빌드할 때 적용된다. 관리형 앱에서 설정 플로그인을 사용하는 경우, 해당 플러그인은 eas build
의 사전 빌드 단계에서 적용된다.
Config plugins: Plugins and mods
플러그인은 ExpoConfig
를 인자로 받아 수정된 ExpoConfig
를 반환하는 동기 함수이다.
- 플러그인은
with<Plugin Functionality>
(ex.withFacebook
) 형식으로 이름을 지정해야 한다. - 플러그인은 동기 함수여야 하며, 반환 값은 직렬화 가능해야 한다(
mods
제외). - 선택적으로 두 번째 인자를 전달해 플러그인을 설정할 수 있다.
plugins
는expo/config
의getConfig
메서드로 설정을 읽을 때 항상 실행되지만,mods
는npx expo prebuild
의 “syncing(동기화)” 단계에서만 실행된다.
자세한 내용은 링크를 참고하자.