웹과 모바일에서 동일한 사용자 경험을 제공하기 위해 단일 React-Native 앱을 작성하고 모바일과 웹 모두 빌드할 수 있는 모노 레포지토리를 구성해보자.
mkdir -p ~/nextjs-rn-monorepo/packages
cd ~/nextjs-rn-monorepo
yarn init
워크 스페이스의 로컬 패키지 경로를 package.json에 추가한다.
// ~/nextjs-rn-monorepo/package.json
{
...
"private": true,
"workspaces": {
"packages": [
"packages/*"
]
},
...
}
mkdir -p ~/nextjs-rn-monorepo/packages/app
cd ~/nextjs-rn-monorepo/packages/app
yarn init -y
// ~/nextjs-rn-monorepo/packages/app/package.json
{
"name": "@nextjs-rn-monorepo/app",
"version": "0.0.1",
"private": true,
"main": "./src/index.ts",
"license": "MIT",
"devDependencies": {
"@types/react": "16.14.0",
"@types/react-native": "0.63.3",
"@types/styled-components": "5.1.4"
},
"peerDependencies": {
"react": "^16.13.1",
"react-native": "^0.63.3",
"styled-components" : "^5.2.1"
}
}
화면에 REACT-NATIVE-WEB과 앱이 동작되는 Platform(ios, android, web)을 출력하는 간단한 앱을 작성한다.
app/src 파일
src ➜ tree . ├── App.tsx ├── SubTitle │ ├── SubTitle.native.tsx │ ├── SubTitle.web.tsx │ └── index.ts └── index.ts
// ~/nextjs-rn-monorepo/packages/app/src/App.tsx
import React from 'react';
import { Platform } from 'react-native';
import Styled from 'styled-components/native';
import SubTitle from './SubTitle';
const Container = Styled.View`
display: flex;
justify-content: center;
align-items: center;
background: #4B7EFF;
height: 100%;
width: 100%;
`;
const Title = Styled.Text`
color: white;
font-size: 32px;
font-weight: 900;
`;
export const App = () => {
console.log(Platform.OS);
return (
<Container>
<Title>REACT NATIVE WEB</Title>
<SubTitle />
</Container>
)
}
// ~/nextjs-rn-monorepo/packages/app/src/index.ts
export { App } from './src/App';
// ~/nextjs-rn-monorepo/packages/app/src/Subtitle.native.tsx
import React from 'react';
import { Platform } from 'react-native';
import Styled from 'styled-components/native';
const Text = Styled.Text`
color: white;
font-size: 24px;
font-weight: bold;
`;
export default function SubTitle() {
return <Text>{`On ${Platform.OS.toUpperCase()}`}</Text>
}
// ~/nextjs-rn-monorepo/packages/app/src/Subtitle.web.tsx
import React from 'react';
import Styled from 'styled-components/native';
const Text = Styled.Text`
color: white;
font-size: 24px;
font-weight: bold;
`;
export default function SubTitle() {
return <Text>On WEB</Text>
}
// ~/nextjs-rn-monorepo/packages/app/src/SubTitle/index.ts
import SubTitle from './SubTitle.native';
export default SubTitle;
react-native 앱을 빌드하기 위한 로컬 패키지를 설정한다.
cd ~/nextjs-rn-monorepo/packages
npx react-native init mobile --version 0.63.3 --template react-native-template-typescript
의존성 패키지 설치 에러가 발생하지만 무시
패키지가 생성되었다면 패키지명을 수정한다.
// ~/nextjs-rn-monorepo/packages/mobile/package.json
{
"name": "@nextjs-rn-monorepo/mobile",
...
}
모든 로컬 패키지의 의존성 패키지들을 제거하고 루트 경로에 재설치한다. ios/andriod 원활할 빌드를 위해 로컬 경로가 아닌 루트 경로의 node_modules로 모든 의존성 패키지를 이동한다.
cd ~/nextjs-rn-monorepo
rm -rf node_modules packages/*/node_modules
yarn install --check-file
metro 번들러가 모바일 빌드시 다른 로컬 패키지나 루트 node_modules의 의존성 패키지를 참조할 수 있도록 워크 스페이스의 루트 경로를 추가한다.
// ~/nextjs-rn-monorepo/packages/mobile/metro.config.js
const path = require('path')
module.exports = {
watchFolders: [path.resolve(__dirname, '../..')],
...
}
ios의 pod 패키지 설치 시 참조하기 위한 react-native 의존성 패키지 경로를 설정한다.
// ~/nextjs-rn-monorepo/packages/mobile/react-native.config.js
module.exports = {
reactNativePath: '../../node_modules/react-native',
}
mobile의 모든 의존성 패키지들이 루트 경로로 이동했기 때문에 ios 빌드 관련 파일들 내 node_modules의 경로도 루트 node_modules로 수정해야 한다.
경로를 모두 수정하였다면 pod 패키지를 모두 설치하고 ios 빌드 설정을 마무리한다.
cd ~/nextjs-rn-monorepo/packages/mobile/ios
pod install
ios 빌드 설정처럼 android 빌드 관련 파일 내 node_moduels의 경로도 루트 node_modules로 수정해야 한다.
경로를 모두 수정하였다면 android 빌드에 필요한 패키지들이 자동 설치될 수 있게 mobile/android 프로젝트를 안드로이드 스튜디오에서 열어 주자.
기존 App.tsx를 삭제하고 @nextjs-rn-monorepo/app에서 작성한 앱을 index.js에 추가한다.
cd ~/nextjs-rn-monorepo/packages/mobile
rm -f App.tsx
yarn add styled-components@5.2.1
yarn workspace @nextjs-rn-monorepo/mobile add @next-rn-monorepo/app@0.0.1
// ~/nextjs-rn-monorepo/packages/mobile/index.js
import { AppRegistry } from 'react-native';
import { App } from '@nextjs-rn-monorepo/app';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => App);
기본 npm 빌드 스크립트로 ios나 anroid를 실행하는 경우 빌드 실패가 발생하는데 이 문제를 우회하기 위해 package.json을 수정한다.
yarn add --dev concurrently
// ~/nextjs-rn-monorepo/packages/mobile/package.json
"scripts": {
"android": "concurrently \"yarn start\" \"react-native run-android --no-packager\"",
"ios": "concurrently \"yarn start\" \"react-native run-ios --no-packager\"",
...
fastlane ios beta 같은 작업들도 monorepo 환경에서 잘 동작하셨었나요??