Monorepo — 웹과 모바일 코드 공유하기
Monorepo를 사용하면 React Native 앱, React 웹, 공유 라이브러리를 하나의 저장소에서 관리할 수 있습니다.
Monorepo 구조
my-monorepo/
├── apps/
│ ├── mobile/ # React Native 앱
│ │ ├── app.json
│ │ ├── package.json
│ │ └── src/
│ └── web/ # Next.js 웹
│ ├── next.config.js
│ ├── package.json
│ └── src/
├── packages/
│ ├── shared/ # 공유 비즈니스 로직
│ │ ├── package.json
│ │ └── src/
│ │ ├── api/
│ │ ├── hooks/
│ │ ├── utils/
│ │ └── types/
│ ├── ui/ # 공유 UI 컴포넌트
│ │ ├── package.json
│ │ └── src/
│ └── config/ # 공유 설정
│ ├── eslint/
│ └── tsconfig/
├── package.json
├── turbo.json
└── pnpm-workspace.yaml
공유 가능한 코드
| 공유 가능 | 공유 불가 |
|---|---|
| API 클라이언트 | 네이티브 컴포넌트 (View, Text) |
| 비즈니스 로직 | 스타일 (StyleSheet vs CSS) |
| 타입 정의 | 네비게이션 |
| 유틸리티 함수 | 플랫폼별 라이브러리 |
| 상태관리 (Zustand) | 이미지/에셋 |
| API 호출 (React Query) | |
| 유효성 검증 (Zod) |
공유 패키지 예시
// packages/shared/src/hooks/useAuth.ts
// React Native와 웹 모두에서 사용 가능
import { create } from 'zustand';
interface AuthStore {
user: User | null;
token: string | null;
login: (email: string, password: string) => Promise<void>;
logout: () => void;
}
export const useAuthStore = create<AuthStore>((set) => ({
user: null,
token: null,
login: async (email, password) => {
const { user, token } = await authApi.login(email, password);
set({ user, token });
},
logout: () => set({ user: null, token: null }),
}));
// packages/shared/src/api/userApi.ts
import { apiClient } from './client';
export const userApi = {
getProfile: () => apiClient.get<User>('/users/me').then(r => r.data),
updateProfile: (data: Partial<User>) => apiClient.put('/users/me', data),
};
// packages/shared/src/utils/validation.ts
import { z } from 'zod';
export const loginSchema = z.object({
email: z.string().email('올바른 이메일을 입력하세요'),
password: z.string().min(8, '8자 이상 입력하세요'),
});
export type LoginForm = z.infer<typeof loginSchema>;
Turborepo 설정
// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env"],
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "dist/**"]
},
"dev": {
"cache": false,
"persistent": true
},
"lint": {},
"test": {}
}
}
# pnpm-workspace.yaml
packages:
- 'apps/*'
- 'packages/*'
# 전체 빌드
pnpm turbo build
# 특정 앱만 실행
pnpm turbo dev --filter=mobile
pnpm turbo dev --filter=web
# 공유 패키지 변경 시 의존 앱 자동 리빌드
Metro 설정 (React Native)
// apps/mobile/metro.config.js
const path = require('path');
const { getDefaultConfig } = require('expo/metro-config');
const projectRoot = __dirname;
const monorepoRoot = path.resolve(projectRoot, '../..');
const config = getDefaultConfig(projectRoot);
// monorepo의 node_modules를 찾을 수 있도록 설정
config.watchFolders = [monorepoRoot];
config.resolver.nodeModulesPaths = [
path.resolve(projectRoot, 'node_modules'),
path.resolve(monorepoRoot, 'node_modules'),
];
module.exports = config;
정리
- Monorepo로 API 클라이언트, 비즈니스 로직, 타입, 유틸리티 를 웹과 모바일에서 공유할 수 있습니다
- UI 컴포넌트와 스타일링은 플랫폼별 로 따로 구현해야 합니다
- Turborepo 로 빌드 캐싱과 의존성 관리를 자동화합니다
- Metro 설정에서 monorepo root의 node_modules를 참조 하도록 설정해야 합니다
- 공유 패키지는 플랫폼 독립적인 순수 TypeScript 로 작성하세요
댓글 로딩 중...