diff --git a/apps/mobile-shell/App.tsx b/apps/mobile-shell/App.tsx index c46f0e6..c4dc9c0 100644 --- a/apps/mobile-shell/App.tsx +++ b/apps/mobile-shell/App.tsx @@ -1,10 +1,12 @@ import { POST_MESSAGE_EVENT } from '@comma/bridge'; +import * as SplashScreen from 'expo-splash-screen'; import { StatusBar } from 'expo-status-bar'; import { Platform, SafeAreaView, StyleSheet } from 'react-native'; import { postMessage, WebView } from './src/bridge'; const defaultWebUrl = Platform.OS === 'android' ? 'http://10.0.2.2:5173' : 'http://localhost:5173'; const webUrl = process.env.EXPO_PUBLIC_WEB_URL ?? defaultWebUrl; +SplashScreen.preventAutoHideAsync(); export default function App() { return ( @@ -19,6 +21,7 @@ export default function App() { postMessage(POST_MESSAGE_EVENT.APP_READY, { platform: Platform.OS }); + SplashScreen.hideAsync(); }} /> diff --git a/apps/mobile-shell/app.json b/apps/mobile-shell/app.json index 611f605..86ca33d 100644 --- a/apps/mobile-shell/app.json +++ b/apps/mobile-shell/app.json @@ -7,11 +7,21 @@ "scheme": "comma", "userInterfaceStyle": "automatic", "newArchEnabled": true, + "plugins": [ + [ + "expo-splash-screen", + { + "image": "./assets/splash.png", + "resizeMode": "cover" + } + ] + ], "ios": { "supportsTablet": true }, "android": { - "edgeToEdgeEnabled": true + "edgeToEdgeEnabled": true, + "package": "com.anonymous.comma" } } } diff --git a/apps/mobile-shell/assets/splash.png b/apps/mobile-shell/assets/splash.png new file mode 100644 index 0000000..319e371 Binary files /dev/null and b/apps/mobile-shell/assets/splash.png differ diff --git a/apps/mobile-shell/package.json b/apps/mobile-shell/package.json index 39efed2..971308d 100644 --- a/apps/mobile-shell/package.json +++ b/apps/mobile-shell/package.json @@ -6,18 +6,19 @@ "scripts": { "dev": "expo start", "dev:client": "expo start --dev-client", - "android": "expo start --android", "android:dev-client": "expo run:android", - "ios": "expo start --ios", + "ios": "expo run:ios", "ios:dev-client": "expo run:ios", "web": "expo start --web", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "android": "expo run:android" }, "dependencies": { "@comma/bridge": "workspace:*", "@webview-bridge/react-native": "^1.7.9", "expo": "~53.0.27", "expo-dev-client": "~5.2.4", + "expo-splash-screen": "~0.30.10", "expo-status-bar": "^2.2.0", "react": "19.0.0", "react-native": "0.79.6", @@ -25,6 +26,7 @@ "zod": "^4.4.3" }, "devDependencies": { + "@types/node": "^26.0.1", "@types/react": "~19.0.10", "typescript": "~5.8.3" } diff --git a/apps/mobile-shell/tsconfig.json b/apps/mobile-shell/tsconfig.json index e928203..9c7e1fe 100644 --- a/apps/mobile-shell/tsconfig.json +++ b/apps/mobile-shell/tsconfig.json @@ -1,7 +1,8 @@ { "extends": "expo/tsconfig.base", "compilerOptions": { - "strict": true + "strict": true, + "types": ["node"] }, "include": ["App.tsx", "src"] } diff --git a/apps/web/package.json b/apps/web/package.json index 4d02a1f..dc1ba50 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -17,6 +17,7 @@ "@webview-bridge/web": "^1.7.9", "react": "19.0.0", "react-dom": "19.0.0", + "react-router-dom": "^7.18.0", "vite": "^6.3.0" }, "devDependencies": { diff --git a/apps/web/public/fonts/PretendardVariable.woff2 b/apps/web/public/fonts/PretendardVariable.woff2 new file mode 100644 index 0000000..49c54b5 Binary files /dev/null and b/apps/web/public/fonts/PretendardVariable.woff2 differ diff --git a/apps/web/public/images/apple_logo.svg b/apps/web/public/images/apple_logo.svg new file mode 100644 index 0000000..5753ab0 --- /dev/null +++ b/apps/web/public/images/apple_logo.svg @@ -0,0 +1,10 @@ + +Apple logo + + + + + + + + diff --git a/apps/web/public/images/google_logo.svg b/apps/web/public/images/google_logo.svg new file mode 100644 index 0000000..370ef66 --- /dev/null +++ b/apps/web/public/images/google_logo.svg @@ -0,0 +1,10 @@ + +Google logo + + + + + + + + diff --git a/apps/web/public/images/kakao_logo.svg b/apps/web/public/images/kakao_logo.svg new file mode 100644 index 0000000..b7f8a47 --- /dev/null +++ b/apps/web/public/images/kakao_logo.svg @@ -0,0 +1,11 @@ + +KaKao Logo + + + + + + + + + diff --git a/apps/web/public/images/logo_glass.svg b/apps/web/public/images/logo_glass.svg new file mode 100644 index 0000000..a68fb3d --- /dev/null +++ b/apps/web/public/images/logo_glass.svg @@ -0,0 +1,15 @@ + +Logo + + + + + + + + + + + + + diff --git a/apps/web/public/images/onboardingBackground.svg b/apps/web/public/images/onboardingBackground.svg new file mode 100644 index 0000000..7667a05 --- /dev/null +++ b/apps/web/public/images/onboardingBackground.svg @@ -0,0 +1,10 @@ + +Background + + + + + + + + diff --git a/apps/web/public/images/onboardingBackground_blur.svg b/apps/web/public/images/onboardingBackground_blur.svg new file mode 100644 index 0000000..6a52aa8 --- /dev/null +++ b/apps/web/public/images/onboardingBackground_blur.svg @@ -0,0 +1,17 @@ + +Background + +
+ + + + + + + + + + + + +
diff --git a/apps/web/src/global.css.ts b/apps/web/src/global.css.ts index 31ad8ca..fd14a34 100644 --- a/apps/web/src/global.css.ts +++ b/apps/web/src/global.css.ts @@ -1,4 +1,4 @@ -import { globalStyle } from '@vanilla-extract/css'; +import { globalFontFace, globalStyle } from '@vanilla-extract/css'; globalStyle('*', { boxSizing: 'border-box' @@ -7,3 +7,14 @@ globalStyle('*', { globalStyle('body', { margin: 0 }); + +globalFontFace('Pretendard', { + src: 'url("/fonts/PretendardVariable.woff2") format("woff2")', + fontDisplay: 'swap', + fontWeight: '100 900' +}); + +globalStyle('body', { + margin: 0, + fontFamily: 'Pretendard, -apple-system, BlinkMacSystemFont, system-ui, sans-serif' +}); diff --git a/apps/web/src/main.tsx b/apps/web/src/main.tsx index 7b1bba0..acd08a9 100644 --- a/apps/web/src/main.tsx +++ b/apps/web/src/main.tsx @@ -1,28 +1,10 @@ import { POST_MESSAGE_EVENT } from '@comma/bridge'; -import { - actionButton, - brand, - description, - eyebrow, - panel, - screen, - themeClass, - title -} from '@comma/design-system'; import React from 'react'; import ReactDOM from 'react-dom/client'; import { appBridge } from './bridge'; import './global.css'; - -async function sendOpenExternalUrl() { - await appBridge.openExternalBrowser('https://example.com'); -} - -async function logAppInfo() { - const appInfo = await appBridge.getAppInfo(); - - console.log('app info', appInfo); -} +import { RouterProvider } from 'react-router-dom'; +import { router } from './router/index'; appBridge.addEventListener(POST_MESSAGE_EVENT.APP_READY, (message) => { console.log('app ready', message); @@ -36,20 +18,6 @@ if (!rootElement) { ReactDOM.createRoot(rootElement).render( -
-
-

Web running inside native shell

-

{brand.name}

-

- React 웹앱을 그대로 개발하고, Expo shell은 WebView와 네이티브 기능만 담당합니다. -

- - -
-
+
); diff --git a/apps/web/src/pages/Login.css.ts b/apps/web/src/pages/Login.css.ts new file mode 100644 index 0000000..d84a1bc --- /dev/null +++ b/apps/web/src/pages/Login.css.ts @@ -0,0 +1,87 @@ +import { style } from '@vanilla-extract/css'; + +export const container = style({ + backgroundImage: 'url("/images/onboardingBackground_blur.svg")', + backgroundRepeat: 'no-repeat', + backgroundSize: 'cover', + width: '100vw', + height: '100vh', + backgroundPosition: 'center', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'space-between', + paddingLeft: 32, + paddingRight: 32, + boxSizing: 'border-box' +}); + +export const title = style({ + fontFamily: 'Pretendard, sans-serif', + fontSize: 32, + fontWeight: 200, + color: '#FDFCFC' +}); + +export const desc = style({ + fontFamily: 'Pretendard, sans-serif', + fontSize: 16, + fontWeight: 400, + color: '#C2BFBC', + marginTop: 16 +}); + +export const agreementNotice = style({ + color: '#C2BFBC', + fontFamily: 'Pretendard, sans-serif', + fontSize: 12, + fontWeight: 500, + textAlign: 'center' +}); + +export const agreementAccent = style({ + color: '#DBD8D7', + textDecoration: 'underline', + fontWeight: 600 +}); + +const buttonBase = style({ + width: '100%', + height: 60, + borderRadius: 100, + fontFamily: 'Pretendard, sans-serif', + fontWeight: 500, + fontSize: 20, + border: 'none', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + gap: '5px' +}); + +export const kakaoBtn = style([ + buttonBase, + { + backgroundColor: '#FEE500', + color: '#322E29', + marginBottom: 8 + } +]); + +export const appleBtn = style([ + buttonBase, + { + backgroundColor: '#1A1814', + color: '#FDFCFC', + marginBottom: 8 + } +]); + +export const googleBtn = style([ + buttonBase, + { + backgroundColor: '#FDFCFC', + color: '#322E29', + marginBottom: 24 + } +]); diff --git a/apps/web/src/pages/Login.tsx b/apps/web/src/pages/Login.tsx new file mode 100644 index 0000000..f3025d5 --- /dev/null +++ b/apps/web/src/pages/Login.tsx @@ -0,0 +1,49 @@ +import * as styles from './Login.css'; + +function Login() { + return ( +
+
+ 배경 이미지 +
+ 고립감 없는 + 연결된 휴식 +

+ 불안한 멈춤에서 온전한 쉼으로, +
+ 당신의 쉬는 시간을 새롭게 정의합니다. +

+
+
+
+ + + +

+ 계속 진행하면 서비스 이용약관
+ 개인정보처리방침에 동의하는 것으로 + 간주합니다 +

+
+
+ ); +} + +export default Login; diff --git a/apps/web/src/router/index.tsx b/apps/web/src/router/index.tsx new file mode 100644 index 0000000..00d3f00 --- /dev/null +++ b/apps/web/src/router/index.tsx @@ -0,0 +1,9 @@ +import { createBrowserRouter } from 'react-router-dom'; +import Login from '../pages/Login'; + +export const router = createBrowserRouter([ + { + path: '/', + Component: Login + } +]); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5646661..686f364 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,6 +41,9 @@ importers: expo-dev-client: specifier: ~5.2.4 version: 5.2.4(expo@53.0.27(@babel/core@7.29.7)(react-native-webview@13.13.5(react-native@0.79.6(@babel/core@7.29.7)(@types/react@19.0.14)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.29.7)(@types/react@19.0.14)(react@19.0.0))(react@19.0.0)) + expo-splash-screen: + specifier: ~0.30.10 + version: 0.30.10(expo@53.0.27(@babel/core@7.29.7)(react-native-webview@13.13.5(react-native@0.79.6(@babel/core@7.29.7)(@types/react@19.0.14)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.29.7)(@types/react@19.0.14)(react@19.0.0))(react@19.0.0)) expo-status-bar: specifier: ^2.2.0 version: 2.2.3(react-native@0.79.6(@babel/core@7.29.7)(@types/react@19.0.14)(react@19.0.0))(react@19.0.0) @@ -57,6 +60,9 @@ importers: specifier: ^4.4.3 version: 4.4.3 devDependencies: + '@types/node': + specifier: ^26.0.1 + version: 26.0.1 '@types/react': specifier: ~19.0.10 version: 19.0.14 @@ -87,6 +93,9 @@ importers: react-dom: specifier: 19.0.0 version: 19.0.0(react@19.0.0) + react-router-dom: + specifier: ^7.18.0 + version: 7.18.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) vite: specifier: ^6.3.0 version: 6.4.3(@types/node@26.0.1)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.48.0) @@ -1856,6 +1865,10 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie@1.1.1: + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} + engines: {node: '>=18'} + core-js-compat@3.49.0: resolution: {integrity: sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==} @@ -2132,6 +2145,11 @@ packages: expo-modules-core@2.5.0: resolution: {integrity: sha512-aIbQxZE2vdCKsolQUl6Q9Farlf8tjh/ROR4hfN1qT7QBGPl1XrJGnaOKkcgYaGrlzCPg/7IBe0Np67GzKMZKKQ==} + expo-splash-screen@0.30.10: + resolution: {integrity: sha512-Tt9va/sLENQDQYeOQ6cdLdGvTZ644KR3YG9aRlnpcs2/beYjOX1LHT510EGzVN9ljUTg+1ebEo5GGt2arYtPjw==} + peerDependencies: + expo: '*' + expo-status-bar@2.2.3: resolution: {integrity: sha512-+c8R3AESBoduunxTJ8353SqKAKpxL6DvcD8VKBuh81zzJyUUbfB4CVjr1GufSJEKsMzNPXZU+HJwXx7Xh7lx8Q==} peerDependencies: @@ -2249,6 +2267,7 @@ packages: git-raw-commits@5.0.1: resolution: {integrity: sha512-Y+csSm2GD/PCSh6Isd/WiMjNAydu0VBiG9J7EdQsNA5P9uXvLayqjmTsNlK5Gs9IhblFZqOU0yid5Il5JPoLiQ==} engines: {node: '>=18'} + deprecated: Deprecated and no longer maintained. Use @conventional-changelog/git-client instead. hasBin: true glob@10.5.0: @@ -3072,6 +3091,23 @@ packages: resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} engines: {node: '>=0.10.0'} + react-router-dom@7.18.0: + resolution: {integrity: sha512-Fi0yY6kgtKae/Th2xibdWK0KSdYZ4B53Gyf6wRtomOKWgpNm7H7+DyfDhncdz9FKbpS+1jmDhg3F4WoGJ+yFOA==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + + react-router@7.18.0: + resolution: {integrity: sha512-pTTGt8J+ji1NOmYnjzT+bAJy/1zD+Jp4ziO6cL7T3ZLvXKtusO7BpFqlRXitqpcPVqllsIXFHRMt+2/k3Xn6HQ==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + peerDependenciesMeta: + react-dom: + optional: true + react@19.0.0: resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} engines: {node: '>=0.10.0'} @@ -3189,6 +3225,9 @@ packages: resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} engines: {node: '>= 0.8.0'} + set-cookie-parser@2.7.2: + resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} @@ -5763,6 +5802,8 @@ snapshots: convert-source-map@2.0.0: {} + cookie@1.1.1: {} + core-js-compat@3.49.0: dependencies: browserslist: 4.28.4 @@ -6019,6 +6060,13 @@ snapshots: dependencies: invariant: 2.2.4 + expo-splash-screen@0.30.10(expo@53.0.27(@babel/core@7.29.7)(react-native-webview@13.13.5(react-native@0.79.6(@babel/core@7.29.7)(@types/react@19.0.14)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.29.7)(@types/react@19.0.14)(react@19.0.0))(react@19.0.0)): + dependencies: + '@expo/prebuild-config': 9.0.12 + expo: 53.0.27(@babel/core@7.29.7)(react-native-webview@13.13.5(react-native@0.79.6(@babel/core@7.29.7)(@types/react@19.0.14)(react@19.0.0))(react@19.0.0))(react-native@0.79.6(@babel/core@7.29.7)(@types/react@19.0.14)(react@19.0.0))(react@19.0.0) + transitivePeerDependencies: + - supports-color + expo-status-bar@2.2.3(react-native@0.79.6(@babel/core@7.29.7)(@types/react@19.0.14)(react@19.0.0))(react@19.0.0): dependencies: react: 19.0.0 @@ -7048,6 +7096,20 @@ snapshots: react-refresh@0.17.0: {} + react-router-dom@7.18.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + dependencies: + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + react-router: 7.18.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + + react-router@7.18.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + dependencies: + cookie: 1.1.1 + react: 19.0.0 + set-cookie-parser: 2.7.2 + optionalDependencies: + react-dom: 19.0.0(react@19.0.0) + react@19.0.0: {} regenerate-unicode-properties@10.2.2: @@ -7206,6 +7268,8 @@ snapshots: transitivePeerDependencies: - supports-color + set-cookie-parser@2.7.2: {} + setprototypeof@1.2.0: {} shebang-command@2.0.0: