Monorepo를 사용하면 React Native 앱, React 웹, 공유 라이브러리를 하나의 저장소에서 관리할 수 있습니다.


Monorepo 구조

PLAINTEXT
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)

공유 패키지 예시

TSX
// 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 }),
}));
TSX
// 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),
};
TSX
// 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 설정

JSON
// turbo.json
{
  "$schema": "https://turbo.build/schema.json",
  "globalDependencies": ["**/.env"],
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "dist/**"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    },
    "lint": {},
    "test": {}
  }
}
YAML
# pnpm-workspace.yaml
packages:
  - 'apps/*'
  - 'packages/*'
BASH
# 전체 빌드
pnpm turbo build

# 특정 앱만 실행
pnpm turbo dev --filter=mobile
pnpm turbo dev --filter=web

# 공유 패키지 변경 시 의존 앱 자동 리빌드

Metro 설정 (React Native)

JS
// 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 로 작성하세요
댓글 로딩 중...