모노레포 — Electron + 웹 앱 코드 공유
"Electron 앱과 웹 앱을 동시에 개발한다면 모노레포로 코드를 공유하세요" — 비즈니스 로직, 타입, 유틸리티를 한 곳에서 관리할 수 있습니다.
모노레포 구조
my-project/
├── pnpm-workspace.yaml
├── package.json
├── packages/
│ ├── shared/ # 공유 코드
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── types.ts
│ │ │ ├── utils.ts
│ │ │ └── api.ts
│ │ └── tsconfig.json
│ ├── desktop/ # Electron 앱
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── main/
│ │ │ ├── preload/
│ │ │ └── renderer/
│ │ └── electron.vite.config.ts
│ └── web/ # 웹 앱
│ ├── package.json
│ └── src/
└── tsconfig.base.json
pnpm workspace 설정
# pnpm-workspace.yaml
packages:
- 'packages/*'
// packages/shared/package.json
{
"name": "@myapp/shared",
"version": "1.0.0",
"main": "src/index.ts",
"types": "src/index.ts"
}
// packages/desktop/package.json
{
"name": "@myapp/desktop",
"version": "1.0.0",
"dependencies": {
"@myapp/shared": "workspace:*"
}
}
// packages/web/package.json
{
"name": "@myapp/web",
"version": "1.0.0",
"dependencies": {
"@myapp/shared": "workspace:*"
}
}
공유 코드 설계
// packages/shared/src/types.ts
export interface User {
id: string;
name: string;
email: string;
}
export interface Note {
id: string;
title: string;
content: string;
createdAt: Date;
updatedAt: Date;
}
// packages/shared/src/utils.ts
export function formatDate(date: Date): string {
return new Intl.DateTimeFormat('ko-KR').format(date);
}
export function slugify(text: string): string {
return text.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
}
// packages/shared/src/api.ts
// HTTP 클라이언트는 환경(브라우저/Node.js)에 따라 다를 수 있으므로
// 인터페이스만 공유
export interface ApiClient {
get<T>(path: string): Promise<T>;
post<T>(path: string, data: unknown): Promise<T>;
}
export class NoteService {
constructor(private api: ApiClient) {}
async getAll(): Promise<Note[]> {
return this.api.get('/notes');
}
async create(title: string, content: string): Promise<Note> {
return this.api.post('/notes', { title, content });
}
}
Electron 앱에서 공유 코드 사용
// packages/desktop/src/renderer/App.tsx
import { formatDate, type Note } from '@myapp/shared';
function NoteList({ notes }: { notes: Note[] }) {
return (
<ul>
{notes.map(note => (
<li key={note.id}>
<h3>{note.title}</h3>
<span>{formatDate(note.updatedAt)}</span>
</li>
))}
</ul>
);
}
루트 스크립트
// 루트 package.json
{
"scripts": {
"dev:desktop": "pnpm --filter @myapp/desktop dev",
"dev:web": "pnpm --filter @myapp/web dev",
"build:desktop": "pnpm --filter @myapp/desktop build",
"build:web": "pnpm --filter @myapp/web build",
"build:all": "pnpm -r build",
"test:all": "pnpm -r test"
}
}
면접 포인트 정리
- 모노레포로 Electron/웹 앱 간 타입, 유틸리티, 비즈니스 로직 공유
workspace:*로 로컬 패키지를 의존성으로 연결- 공유 코드는 환경 독립적으로 설계 (Node.js/브라우저 모두 동작)
- pnpm의
--filter옵션으로 특정 패키지만 빌드/실행
모노레포를 다뤘으면, 다음은 Electron Fiddle을 알아봅시다.
댓글 로딩 중...