react-native-web์ React Native์ ์ปดํฌ๋ํธ๋ฅผ ์น ํ๊ฒฝ์ ๋ง๊ฒ ๋ณํํ์ฌ ์น ๋ธ๋ผ์ฐ์ ์์ ์ง์ ์คํ๋๋๋ก ํด์ค๋ค.
React Native๋ก ๋ชจ๋ฐ์ผ ์ฑ์ ๊ฐ๋ฐํ๋ค๊ฐ ์น์ผ๋ก๋ ๋ง๋ค์ด๋ณด๊ณ ์ถ์ด์ ์ฌ์ฉํด ๋ดค๋ค.
# ์ค์น
yarn add react-dom react-native-web
yarn add url-loader
yarn add -D @types/react-dom
# webpack ์ค์น
yarn add -D webpack webpack-cli webpack-dev-server html-webpack-plugin babel-loader @babel/preset-react
์ด์ ์ด ๋ธ๋ก๊ทธ ๋ณด๊ณ ๊ธฐ๋ณธ ์ค์ ํด์ฃผ๋ฉด ๋จ
โ ์ถ๊ฐ์ ์ธ ์ค์
# ์๋ ๋๊ฐ๋ ์ด๋ฏธ ์ค์น ํ์์
# yarn nativewind
# yarn add -D tailwindcss
yarn add -D postcss autoprefixer postcss-loader css-loader style-loader
tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./App.{js,jsx,ts,tsx}',
'./src/**/*.{js,jsx,ts,tsx}',
'./public/**/*.{js,jsx,ts,tsx,html}',
],
theme: {
extend: {},
},
plugins: [],
};
postcss.config.js
module.exports = {
plugins: [require('tailwindcss'), require('autoprefixer')],
};
src/global.css
@tailwind base;
@tailwind components;
@tailwind utilities;
body {
background-color: rgb(233, 236, 239);
}
.root-container {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
}
.root-wrapper {
max-width: 540px;
width: 100%;
height: 100vh;
background-color: #fff;
}
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from '/App';
import './src/global.css'; // โญ๏ธ ์ถ๊ฐ
const rootNode = document.getElementById('root');
ReactDOM.createRoot(rootNode).render(<App />);
...
// css ํ์ผ ๋ก๋ ์ถ๊ฐ
const cssLoader = {
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader'],
include: path.resolve(__dirname, 'src'),
};
module.exports = {
resolve: {
...
extensions: ['.web.js', '.js', '.ts', '.tsx', 'json', '.css'],
},
module: {
rules: [tsxLoader, imgLoader, svgLoader, cssLoader],
},
...
};
module.exports = {
presets: ['module:@react-native/babel-preset', '@babel/preset-react'],
...
}
"scripts": {
"start:web": "webpack serve --config ./webpack.config.js --mode development",
"build:web": "webpack --mode production",
...
},
yarn run start:web
์น๊ณผ ๋ชจ๋ฐ์ผ ํ๊ฒฝ์ ๋ฐ๋ผ ๊ฒฝ๋ก ์ค์ ์ ๋ค๋ฅด๊ฒ ํด์ผ ํ๋ค.
๊ทธ๋ ์ง ์์ผ๋ฉด ์น์์๋ ๊ฒฝ๋ก๊ฐ ๋ณ๊ฒฝ๋์ด๋ URL์ด ์
๋ฐ์ดํธ๋์ง ์๋๋ค.
react-router-dom์ ๋ธ๋ผ์ฐ์ ํ๊ฒฝ์์๋ง ์๋ํ๋ฏ๋ก ๋ชจ๋ฐ์ผ ์ฑ์์๋ ์ฌ์ฉํ ์ ์๋ค.
(๋ชจ๋ฐ์ผ์์ ์ฌ์ฉํ๋ฉด document ๊ฐ์ฒด์ ๊ด๋ จ๋ ์๋ฌ๊ฐ ๋ฐ์ํจ)
# ์ค์น
yarn add react-router-dom
App.tsx
/* App.tsx */
import React from 'react';
import {Platform} from 'react-native';
import {NavigationContainer} from '@react-navigation/native';
import {createNativeStackNavigator} from '@react-navigation/native-stack';
import {BrowserRouter as Router, Routes, Route} from 'react-router-dom';
import HomeScreen from 'screens/HomeScreen';
import DetailMenuScreen from 'screens/DetailMenuScreen';
function App() {
const Stack = createNativeStackNavigator();
const isWeb = Platform.OS === 'web';
return (
<>
{!isWeb && (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
options={{headerShown: false}}
component={HomeScreen}
/>
<Stack.Screen
name="DetailMenu"
options={{headerShown: false}}
component={DetailMenuScreen}
/>
</Stack.Navigator>
</NavigationContainer>
)}
{isWeb && (
<Router>
<Routes>
<Route path="/" element={<HomeScreen />} />
<Route path="/menu/detail" element={<DetailMenuScreen />} />
</Routes>
</Router>
)}
</>
);
}
export default App;
src/screen/HomeScreen.tsx
/* src/screen/HomeScreen.tsx */
import React from 'react';
import {Platform, Pressable, SafeAreaView, StatusBar, Text} from 'react-native';
import {useNavigation} from '@react-navigation/native';
import {NativeStackNavigationProp} from '@react-navigation/native-stack';
import {useNavigate} from 'react-router-dom';
const HomeScreen = () => {
const isWeb = Platform.OS === 'web';
const navigate = isWeb ? useNavigate() : undefined;
const navigation = isWeb
? undefined
: useNavigation<NativeStackNavigationProp<any>>();
const goToDetailMenu = () => {
if (isWeb && navigate) {
navigate('/menu/detail');
return;
}
if (navigation) {
navigation.navigate('DetailMenu');
}
};
return (
<SafeAreaView className="bg-white">
<StatusBar barStyle={'light-content'} backgroundColor={'#fff'} />
<Pressable
onPress={goToDetailMenu}
className="bg-green-300 p-4 text-green-900 m-10 border border-solid border-green-900 rounded">
<Text>Home</Text>
</Pressable>
</SafeAreaView>
);
};
export default HomeScreen;
ํ ์ผ๋ก ๋ถ๋ฆฌ
hooks/useNavi.ts
import { Platform } from 'react-native';
import { useNavigate } from 'react-router-dom';
import { useNavigation } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
const useNavi = () => {
const isWeb = Platform.OS === 'web';
const navigate = isWeb ? useNavigate() : undefined;
const navigation = isWeb ? undefined : useNavigation<NativeStackNavigationProp<any>>();
return {
navigate, // ์น
navigation, // ์ฑ
};
};
export default useNavi;
+
์์ง web์ ์ง์ํ์ง ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ด ๋ง๊ณ ,
์ง์ํ๋๋ผ๋ web์์ ์ ๋๋ก ๋์ํ์ง ์๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค.
๊ทธ๋์ ๊ฐ๋ฅํ ํ ์น์ ๋ณ๋๋ก ๊ฐ๋ฐํ๋ ๊ฒ ์ข์ ๊ฒ ๊ฐ๋ค.