React Native는 자바스크립트 프레임워크인 React로 개발하며 네이티브 앱(iOS와 안드로이드)을 만들어주는 프레임워크이다. React Native는 iOS와 안드로이드의 개발 언어로도 네이티브 개발을 할 수 있는 특징이 있는 반면, Expo는 순수하게 자바스크립트와 React로만 개발하고 네이티브 개발은 미리 제공된 모듈만 사용하도록 SDK와 빌드 환경을 제공해주는 형태이다. Expo가 자유도와 기능 지원율은 낮지만 자바스크립트로만 빠르게 앱을 만들 수 있고 별도의 네이티브 구동 환경을 내 PC에 설치하지 않아도 되는 장점이 있다.
최근 React Native는 React Native for web을 발표하면서 또 하나의 플랫폼을 추가했다. Expo도 이에 발 맞추어 6월이 릴리즈된 SDK 33버전에서 Expo Web 베타 버전을 지원하기 시작했다. Expo Web은 모바일 웹을 구현할 수 있도록 해주고 PWA를 지원한다. React Native와 Expo가 제공하는 컴포넌트로 네이티브 앱을 개발할 수 있지만 웹에서도 같은 컴포넌트로 돌아갈 수 있게 만든 것이다. React가 그런 것처럼 웹에서는 당연히 React DOM이 렌더링에 사용된다. 이 글에서는 "React Native for web"보다는 Expo Web을 중점적으로 살펴볼 것이다.
이 글을 읽기 전에 먼저 말하고 싶은 것은 "Write once, run everywhere"는 항상 희망을 주지만 아무 노력을 하지 않아도 된다는 것을 뜻하지는 않는다. iOS, 안드로이드, 웹 브라우저 고유의 플랫폼을 모두 똑같이 맞출 수는 없기 때문에 Expo가 세 가지 플랫폼을 지원하더라도 기능 탐지 등의 방법을 통해 신경써서 앱을 만들어야 하는 모습은 분명히 인정하고 넘어가야 할 것이다. 그럼에도 불구하고 Expo Web에 기대를 거는 것은 프론트 엔드 기술의 가능성에 대한 도전이기 때문이다.
한 가지 더 기대되는 점은 보다 빠른 피드백으로 개발 주기가 빨라질 것이다. 시뮬레이터에서 네이티브 앱으로 통신하는 과정이 아직까지는 느리기 때문에 브라우저에 바로 실행하는 Web 버전을 사용할 경우 빌드 시간, HMR이 빠르고 크롬 개발자 도구를 충분히 사용할 수 있기 때문에 앱의 기본 틀을 만들 때까지 속도가 올라갈 것이다.
이 글에서는 Expo Web 베타 버전을 사용하여 설치와 실행, 구동, 배포, 테스트 방법 등을 알아본다.
React Native는 자바스크립트와 React 프레임워크로 작성하여 멀티 플랫폼에서 동작하는 것이 특징이다. 공식적으로 지원하는 플랫폼이 iOS와 안드로이드이다. Windows는 과거에 마이크로소프트에서 직접 지원하였으나 현재는 Windows 10으로 OS가 통합되면서 Windows 10기반으로 다시 작성하고 있다. 이제는 웹이 플랫폼으로 추가되면서 프론트 엔드 개발자의 마음을 더욱 설레게 한다.
브라우저 지원 범위는 Chrome, Firefox, Edge, Safari 7+, IE 10+이다. Internet Explorer 10이상 지원이라고 하니 아직 Internet Explorer 점유율이 높은 편인 국내 환경에서 다행이라는 생각이 든다.
React는 가상돔 기반으로 실제로 렌더링은 React DOM과 같은 레이어가 처리하는 구조이다. 가상돔과 렌더링 레이어를 분리했기 때문에 렌더링 레이어를 여러 플랫폼에 맞게 구현할 수 있는 구조이기 때문에 "Write once, run everywhere"의 목표에 딱 맞는 형태이다. 즉 React Native에서 지원하는 컴포넌트를 사용하여 앱을 작성하면 플랫폼에 맞게 렌더링이 되는 것이다. 다음 예제 코드는 expo init
의 기본 설정으로 자동 생성된 코드이다. Text
, View
와 같은 React Native의 컴포넌트는 플랫폼 독립적인 컴포넌트이지만, 런타임에서는 각 플랫폼에 맞는 렌더링이 실행된다.
import React from "react";
import { StyleSheet, Text, View } from "react-native";
export default function App() {
return (
<View style={styles.container}>
<Text>Open up App.tsx to start working on your app!</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center"
}
});
iOS와 안드로이드 간에도 어쩔 수 없이 네이티브 기능을 통합하지 못하는 컴포넌트나 기능이 있다. 각자 OS의 네이티브 기능이 다르고 스토어 정책에 따라서 특정 플랫폼에서만 동작하는 컴포넌트나 기능인데, 추가된 웹의 경우도 네이티브 지원 유무에 따라서 기능이 다르다. Expo로 소스 코드를 작성하다 보면 플랫폼에 따라서 분기를 타야하는 때가 있는데 마찬가지로 웹의 지원 범위도 다르다. 예를 들면 연락처(Contacts) 기능은 iOS와 Android에는 있지만, 웹에서는 없는 것이 어떻게 보면 당연하다. Expo Web의 기능 목록은 여기에서 확인할 수 있다.
SDK 33의 릴리즈 노트에 나온 대로 매우 간단하게 설치 및 실행할 수 있다.
npm install -g expo-cli
expo init awesome-expo-web-example
expo init
할 때 여러 가지 옵션을 설정할 수 있는데, tabs
를 선택하면 react-navigation을 사용하여 탭 기반의 여러 화면을 설정한 예제를 사용할 수 있다. 이 예제로 웹과 iOS, 안드로이드 모두 실행해볼 것이다.
설치가 된 후 app.json 파일을 열어 보면 플랫폼에 web
이 추가된 것을 확인할 수 있다.
{
"expo": {
"name": "awesome-expo-web-example",
"slug": "awesome-expo-web-example",
"privacy": "public",
"sdkVersion": "33.0.0",
"platforms": [
"ios",
"android",
"web"
],
...
}
}
그럼 이제 Expo Web을 실행해 보자.
cd awesome-expo-web-example
expo start -w
하지만 웹 버전이 에러를 출력하며 실행이 되지 않을 것이다. 깃헙 이슈에 올라 와 있는데 웹 실행에서만 발생하는 문제로 패치가 되기를 기다려야 한다. 웹 버전의 테스트를 하기 위해서 아래 경로의 소스에서 이미지 파일의 이름을 변경하면 실행이 되고, iOS와 안드로이드는 고친 소스를 다시 원래대로 돌려놓아야 한다.
node_modules/@expo/samples/ExpoLinksView.js
<Image
source={require("./assets/images/expo-icon@2x.png")}
...
/>
-w
옵션은 웹 버전을 브라우저에서 실행시키는 옵션이다.-i
옵션은 iOS 버전을 실행시키는 옵션이다.-a
옵션은 안드로이드 버전을 실행시키는 옵션이다.웹, iOS, 안드로이드 세 플랫폼 모두 하나의 소스로 각자 플랫폼에 맞게 구동되는 것을 확인할 수 있다. 왼쪽부터 웹, iOS, 안드로이드 실행 화면이다.
배포를 위한 빌드도 매우 쉽다. build:ios
, build:android
와 더불어 build:web
이 추가되었다. 하지만 아직까지 베타라 production 빌드를 하고 실행을 하면 오류가 발생한다. 아쉽지만 아직까지는 테스트를 위해서 development 모드로 빌드를 해야 한다.
웹팩 설정을 확장하여 development 모드로 빌드한다. @expo/webpack-config
를 설치하고 webpack.config.js 파일을 작성하여 설정을 확장할 수 있다.
yarn add -D @expo/webpack-config
webpack.config.js 파일 생성
const createExpoWebpackConfig = require("@expo/webpack-config");
module.exports = function(env, argv) {
env.mode = "development";
const config = createExpoWebpackConfig(env, argv);
return config;
};
expo build:web
빌드는 웹팩을 사용하여 자바스크립트와 리소스가 빌드되고 빌드 결과물은 web-build
폴더에 생성된다.
Expo 웹은 PWA(Progressive Web App)를 지원한다. Expo의 app.json 파일의 정보를 바탕으로 PWA의 설정 파일인 manifest.json 파일을 만들어 낸다. 빌드된 index.html을 보면 service-worker
또한 사용하고 있음을 볼 수 있다.
<script>
if ("serviceWorker" in navigator) {
window.addEventListener("load", function() {
navigator.serviceWorker.register("/service-worker.js");
});
}
</script>
크롬 개발자 도구의 Audits(lighthouse)로 확인해보니 PWA 지원에 대한 평가가 나타난다.
Expo로 개발한 자바스크립트 소스나 import되는 파일들 외에도 웹 앱에서 서비스할 정적 파일을 추가할 수 있다. 소스의 루트 폴더에 web
이라는 폴더를 추가하고 하위에 필요한 정적 파일을 추가할 수 있다.
/web
ㄴTOAST UI Grid v4.0.png
ㄴother resources.file
파일을 추가한 후 다시 expo build:web
으로 빌드하면 web-build/TOAST UI Grid v4.0.png
파일이 추가되고 서버에서도 잘 내려오는 것을 확인할 수 있다.
Expo Web이 베타버전이라 production 빌드가 잘 되지 않는 상태이지만, 추후 패치가 되었을 때 빌드된 웹 앱을 간단하게 배포할 수 있는 팁을 공유한다.
Expo Web으로 빌드된 웹앱은 어느 서버든 배포하면 바로 서비스를 할 수 있는데, 간단하게 깃헙 페이지를 사용하여 배포할 수도 있다. FE개발랩의 오픈소스의 API 페이지도 깃헙 페이지를 사용하고 있으며 gh-pages
를 사용하여 빠르고 편리하게 배포하고 있다. Expo Web을 깃헙 페이지에 배포해 보자.
깃헙 저장소는 새로 만들어야 하고 다음과 같은 과정으로 간단하게 깃헙 페이지에 배포할 수 있다.
git remote add origin https://github.com/dongsik-yoo/awesome-expo-web-example.git
npm i --save-dev gh-pages
npx gh-pages -d web-build
스토리북은 컴포넌트 단위의 UI를 개발하고 테스트하는데 유용한 E2E 도구이다. Expo를 development 모드로 iOS와 안드로이드에서 개발하다보면 다소 느린 속도 때문에 답답할 때가 있다. 그리고 스토리북으로 UI를 만들 때 그 쾌적함과 즉각적인 반응은 개발 속도를 높이는데 큰 도움을 주었기 때문에 Expo 개발 시 스토리북을 사용할 수 있다는 것은 큰 장점이라고 생각한다. 스토리북에서 React Native도 지원하고 있는데, 테스트를 해보니 Expo Web 버전 또한 지원함을 알 수 있다.
스토리북 React Native 설치 가이드를 참고하여 간편하게 자동 설치 및 환경 설정을 할 수 있다.
npx -p @storybook/cli sb init --type react_native
react 스토리북 설치와는 다르게 .storybook
폴더가 아니라 storybook
폴더가 만들어 진다. 설치 후에는 엔트리 파일을 스토리북 UI로 변경해야 한다. 테스트를 위해 소스 코드를 바꾸어야 한다는 단점은 있지만 시뮬레이터보다 브라우저에서 더 빠른 주기로 개발할 수 있는 장점이 있다.
App.js 파일 수정
/*
스토리북을 위해 다른 부분은 모두 주석 처리한다.
*/
export default from "./storybook";
index.web.js 파일 추가
스토리북의 React Native 버전에서는 플랫폼별로 다른 컴포넌트를 사용해서 테스트해야 하는 경우 테스트 파일 이름의 형식이 index.{platform}.js
파일이다. 생성된 storybook/stories/Button
폴더를 보면 다음과 같이 두 플랫폼을 위한 파일이 미리 생성되어 있는 것을 볼 수 있다.
여기에 index.web.js
파일을 추가하면 Expo Web을 스토리북으로 테스트할 수 있다.
import React from "react";
import { TouchableHighlight } from "react-native";
export default function Button({ onPress, children }) {
return <TouchableHighlight onPress={onPress}>{children}</TouchableHighlight>;
}
그리고 다시 "expo start -w
"로 Expo Web을 실행하면 브라우저에서 스토리북 UI가 나타나고 react-native
의 컴포넌트를 사용할 수 있다. 스토리북 서버가 뜬 것이 아니라서 약간 반쪽 짜리 느낌이지만 HMR 등 UI 개발에는 문제가 없어 보이고 iOS와 안드로이드 단말에서도 마찬가지 방식으로 동작하고 있다. 실행 후 개발자 도구로 살펴보니 <TouchableHighlight>
컴포넌트가 div로 렌더링 된 것을 확인할 수 있다.
왼쪽부터 웹, iOS, 안드로이드의 스토리북 테스트 실행 화면이다.
"React Native for web" 기반의 Expo Web은 아직 베타 단계라서 앞서 살펴본 것처럼 production 빌드에 오류나 깃헙 페이지에서 동작이 되지 않는 문제가 있다. 그러나 Expo 팀은 사용자들에게 지속적인 피드백을 주면서 안정화를 위한 노력을 계속 진행 중이다. SDK 34에서는 베타를 졸업하고 RC(Release Candidate) 버전을 릴리즈 할 예정이고 SDK 35에서 정식 릴리즈를 계획하고 있다.
React Native가 프론트 엔드 기술을 통해 모바일 네이티브 앱을 만들 수 있는 가능성을 열었고 이제는 다시 웹을 지원하는 것으로 가능성을 열고 있다. 가능성에 대한 도전이 큰 결실을 맺기를 기대하면서 React Native와 Expo팀을 응원한다.