8개 플랫폼 동원 아키텍처: 하나의 TypeScript Monorepo로 Web/API/CLI/Desktop/Extension/Mobile/Mi
요약
하나의 TypeScript Monorepo를 통해 Web, API, CLI, Desktop 등 8개 플랫폼을 동시에 지원하는 아키텍처 설계 방식을 소개합니다. pnpm workspace와 Turborepo를 활용하여 코드 중복을 최소화하고 타입 안정성을 확보하는 실전 엔지니어링 사례를 다룹니다.
핵심 포인트
- pnpm workspace와 Turborepo를 활용한 Monorepo 구조 설계
- 단일 진실 공급원(SSOT)으로서의 공유 타입 패키지 구축
- 플랫폼 파편화 문제를 해결하는 코드 및 로직 공유 전략
- 8개 플랫폼 동시 대응을 위한 효율적인 엔지니어링 워크플로우
8개 플랫폼 동원 아키텍처: 하나의 TypeScript Monorepo로 Web/API/CLI/Desktop/Extension/Mobile/Miniapp을 동시에 출력하는 방법
"AI 애플리케이션을 만들 건데, 먼저 Web으로 출시하고, 그다음엔 미니 앱(Miniapp), 그다음엔 데스크톱 앱이랑 브라우저 확장 프로그램으로 가죠..." —— 제품 매니저의 말이 끝나기도 전에 프론트엔드 리드(Lead)는 벌써 머리가 아파지기 시작했습니다. 8개 플랫폼, 8세트의 코드, 8배의 작업량?
본문은 IHUI AI 프로젝트(오픈 소스 8개 플랫폼 풀스택 AI 운영체제)가 「8개 플랫폼 동원(8-platform same-source)」 아키텍처를 구현하며 얻은 실제 엔지니어링 요약입니다. 여러분은 다음 내용을 보게 될 것입니다: 하나의 TypeScript Monorepo가 어떻게 동시에 8개 플랫폼을 출력하는지, 무엇을 공유하고 무엇을 공유하지 않는지, 플랫폼 간 계약(Contract)을 어떻게 정렬하는지, 그리고 실전 데이터(340개 테이블 / 144개 마이그레이션 / 1300+ API / 5346개 테스트)를 확인할 수 있습니다.
1. 페인 포인트(Pain Point): AI 애플리케이션의 「플랫폼 파편화」 재앙
AI 애플리케이션을 만들 때, 여러분은 아마 다음과 같은 상황에 직면할 가능성이 높습니다:
- Web 플랫폼 우선 출시: Next.js + React로 채팅 페이지 작성
- 상사가 미니 앱(Miniapp) 요구: 별도의 Taro 프로젝트를 생성하고, API client를 다시 작성
- 사용자가 데스크톱 앱 요구: Electron으로 감싸지만, 타입 정의(Type Definition)를 또 한 번 복사
- 운영팀이 브라우저 확장 프로그램 요구: Chrome Extension MV3, 또 다른 한 세트
- CTO가 CLI 요구:
npx ihui로 API를 호출하기 위해 SDK를 다시 작성 - 투자자가 Mobile 요구: React Native, 처음부터 다시 시작
- API 백엔드: Fastify + Drizzle, 프론트엔드 타입과 수동으로만 동기화 가능
결과: 동일한 User 타입이 8개 플랫폼에서 8개의 정의로 존재하며, 필드 하나만 바꿔도 모든 플랫폼에서 폭발합니다. 동일한 API 호출을 8개 플랫폼에서 8번 작성하며, 버그를 8번 수정해야 합니다. 동일한 UI 컴포넌트(버튼/대화창/메시지 버블)를 5개 플랫폼에서 각각 구현하며, 디자인 시안이 바뀌면 모든 플랫폼을 따라가며 수정해야 합니다.
이것은 엔지니어링 능력의 문제가 아니라 아키텍처 문제입니다. 여러분은 8개의 플랫폼을 8개의 독립된 프로젝트로 취급하고 있습니다.
2. 솔루션: pnpm workspace + Turborepo + 공유 packages
IHUI AI의 해결책은 **「8개 플랫폼 동원 Monorepo」**입니다. 모든 플랫폼은 동일한 packages/를 공유하며, 각 플랫폼 고유의 코드는 오직 자신의 apps/<플랫폼명>/ 아래에만 작성합니다.
2.1 저장소 구조
IHUI-AI/
├── apps/
│ ├── web/ # Next.js 15 + React 19 + Tailwind 4
...
2.2 세 가지 핵심 설계 결정
결정 1: 타입은 「단일 진실 공급원(Single Source of Truth)」이며, 코드가 타입을 역으로 의존하게 한다. 반대로 하지 않는다.
packages/types는 전체 monorepo의 계약(Contract) 계층입니다. apps/api의 라우팅 파라미터, apps/web의 API client, apps/cli의 명령 옵션 모두 @ihui/types에서 임포트(Import)합니다. 필드 하나를 수정하면 8개 플랫폼의 타입 체크(Typecheck)에서 즉시 에러가 발생하며, 「프론트엔드가 동기화를 잊어버리는」 상황은 존재하지 않습니다.
결정 2: 데이터베이스 스키마(Schema)를 독립된 패키지로 구성하여 API와 AI 서비스가 공용으로 사용한다.
packages/database는 Drizzle ORM을 사용하여 340개 테이블의 스키마를 정의하고 마이그레이션(Migration) 파일을 생성합니다. apps/api는 스키마를 직접 임포트하여 데이터베이스를 조작하고, apps/ai-service(Python)는 OpenAPI를 통해 동일한 계약을 브릿지(Bridge)하여 소비합니다. 이를 통해 「백엔드에서 필드를 변경했는데 AI 서비스는 여전히 옛날 스키마를 사용하는」 전형적인 드리프트(Drift) 현상을 방지합니다.
결정 3: UI 컴포넌트 계층화: packages/ui에는 로직을 두고, 각 플랫폼 어댑터(Adapter)에는 스타일을 둔다.
shadcn/ui는 Radix + Tailwind를 기반으로 하며, 본래 「로직 + 스타일」 계층 분리 구조입니다. 우리는 packages/ui에 <Button>을 노출하고, apps/web은 이를 직접 사용하며, apps/miniapp-taro는 Taro 어댑터를 사용하여 props 인터페이스는 유지하되 스타일을 재작성합니다. apps/mobile-rn은 RN 어댑터를 사용합니다. API는 일관되게 유지하되, 스타일은 각 플랫폼이 자율적으로 관리합니다.
3. 기술적 세부 사항: 8개 플랫폼이 공유하는 방법
3.1 공유 타입 정의
packages/types/src/chat.ts:
export interface ChatMessage {
id: string;
role: 'system' | 'user' | 'assistant' | 'tool';
...
apps/api 라우트에서 직접 소비:
apps/api 라우트에서 직접 소비:
// apps/api/src/routes/chat.ts
import type { ChatCompletionRequest, ChatCompletionResponse } from '@ihui/types';
import { FastifyInstance } from 'fastify';
...
apps/web의 API 클라이언트:
// apps/web/src/lib/api-client.ts
import type { ChatCompletionRequest, ChatCompletionResponse } from '@ihui/types';
...
apps/cli도 동일한 타입을 사용:
// apps/cli/src/commands/chat.ts
import type { ChatCompletionRequest } from '@ihui/types';
import { program } from 'commander';
...
주요 이점: ChatMessage에 필드를 추가하면 8개 엔드에서 동시에 타입 체크 경고가 발생하며, 5분 내에 전 엔드가 동기화됩니다.
3.2 공유 UI 컴포넌트
packages/ui/src/button.tsx:
import { ButtonHTMLAttributes, forwardRef } from 'react';
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
...
apps/miniapp-taro 어댑터 레이어는 스타일만 덮어쓰면 됩니다:
// apps/miniapp-taro/src/adapters/Button.tsx
import { Button as TaroButton } from '@tarojs/components';
import type { ButtonProps } from '@ihui/ui';
...
API가 완벽하게 일치하여 비즈니스 코드 수정이 필요 없습니다. 운영팀이 Web 채팅 페이지를 미니 프로그램으로 옮길 때, import 경로만 바꾸면 됩니다.
3.3 공유 API 클라이언트 (packages/sdk)
packages/sdk/src/index.ts에서 통합 클라이언트를 노출하여 8개 엔드가 모두 이를 통해 API에 접근하며, 인증(Authentication), 재시도(Retry), 오류 형식 처리를 자동으로 처리합니다:
import type { ChatCompletionRequest } from '@ihui/types';
export class IHUIClient {
...
Web 단에서는 cookie token을 주입하고, CLI 단에서는 설정 파일의 API key를, Mobile 단에서는 SecureStorage token을 주입합니다. 비즈니스 코드는 완전히 동일합니다.
3.4 데이터베이스 스키마 공유
packages/database/src/schema/users.ts:
import { pgTable, uuid, varchar, timestamp, boolean } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
...
API, CLI, 데스크톱, 확장, 모바일 모두 동일한 User 타입을 import하며, 데이터베이스 필드가 변경되면 전 엔드에서 즉시 알게 됩니다.
네, 크로스 플랫폼 계약 정렬: 타입 체크만으로는 부족합니다
타입만 있는 것으로는 충분하지 않습니다. IHUI AI는 4중 방어선을 통해 크로스 플랫폼 연결성을 보장합니다:
방어선 1: OpenAPI 자동 생성
API 라우트는 zod를 사용하여 스키마를 정의하고, 시작 시 자동으로 OpenAPI 문서를 생성합니다. CLI / SDK / Mobile은 openapi-typescript를 사용하여 타입을 생성함으로써 클라이언트와 서버의 계약 일치성을 보장합니다.
방어선 2: 가드 스크립트 check-multi-end-sync.mjs
pre-commit 훅에 있는 스크립트는 스테이징된 변경 사항을 감지합니다. 만약 한 엔드만 수정하고 packages/*를 건드리지 않았다면, 경고 메시지를 표시하여
방어선 3: 전 플랫폼 typecheck + build + test
pnpm turbo build typecheck lint test 명령어 한 줄로 8개 플랫폼 전체를 실행하며, 어느 한 플랫폼이라도 실패하면 전체가 fail 됩니다. CI(지속적 통합) 환경에서 이 단계는 엄격한 진입 장벽(Hard Gate) 역할을 합니다.
방어선 4: 크로스 플랫폼 E2E
핵심 경로(회원가입 → 로그인 → 대화 → 결제)에 대해 크로스 플랫폼 E2E(End-to-End) 테스트를 수행합니다: Web + API + AI-service를 함께 실행하여 전체 경로가 정상적으로 작동하는지 확인합니다.
5. IHUI AI 실전 데이터
이 아키텍처는 단순한 이론이 아닙니다. IHUI AI 저장소의 실제 데이터입니다:
| 지표 | 수치 |
|---|---|
| 데이터베이스 테이블 수 | 340개 |
| ... |
실제 사례: 한 번은 ChatMessage에 토큰 소모량을 통계 내기 위한 tokens 필드를 추가한 적이 있습니다. packages/types를 수정하자마자 8개 플랫폼의 typecheck에서 즉시 14곳의 오류가 보고되었고, 5분 이내에 모두 수정할 수 있었습니다. 만약 8개의 독립된 저장소를 사용했다면, 이러한 변경 사항을 조율하고 배포 및 롤백(Rollback)하는 데 최소 2일이 걸렸을 것입니다.
6. 언제 8개 플랫폼 동원 아키텍처를 사용하지 말아야 하는가
솔직히 말해서, 이 아키텍처에는 비용이 따릅니다:
- 팀 규모가 3명 미만이고, 1~2개의 플랫폼만 개발하는 경우: Monorepo가 필요 없으며, 단일 저장소(Single Repo)가 더 빠릅니다.
- 플랫폼 간 기술 스택 차이가 매우 큰 경우: 예를 들어 하나는 Rust를 사용하고 하나는 Swift를 사용한다면, 공유를 통해 얻는 이득이 매우 낮습니다.
- 강제적인 typecheck 습관이 없는 경우: 타입이 모든 플랫폼에서 동기화되지 않으면 오히려 더 혼란스러워집니다.
우리가 이 아키텍처를 채택한 이유는 IHUI AI가 처음부터 「8개 플랫폼 풀스택 AI 운영체제」를 목표로 설정했기 때문이며, 첫날부터 8개의 플랫폼을 만들 것이라는 점을 알고 있었기 때문입니다. 초기에 투자할수록 이득이 큽니다. 5번째 플랫폼을 개발할 때 Monorepo로 전환하려면 지금보다 10배의 비용이 들 것입니다.
7. 결론
8개 플랫폼 동원 아키텍처의 핵심은 「8개의 플랫폼을 하나의 저장소에 밀어 넣는 것」이 아니라 다음과 같습니다:
- 타입 동원 (Type Single Source of Truth): 하나의 타입을 8개 플랫폼이 공유하며, 한 곳을 수정하면 모든 플랫폼에 경고가 발생합니다.
- Schema 동원: 하나의 Drizzle schema를 백엔드 AI 서비스가 공용으로 사용합니다.
- UI 동원:
packages/ui가 API를 노출하고, 각 플랫폼은 스타일 어댑터(Style Adapter)를 작성하여 대응합니다. - SDK 동원: 하나의 client를 사용하여 각 플랫폼에 인증(Authentication)을 주입합니다.
이 아키텍처 덕분에 IHUI AI는 6개월 만에 8개 플랫폼, 340개 테이블, 1300개 이상의 API, 5346개의 테스트를 독립적으로 인도할 수 있었습니다. 혼자서 이 모든 것을 해낼 수 있었던 핵심 이유는 제가 특별히 과하게 몰입해서가 아니라, 아키텍처가 제가 수정할 때마다 8개 플랫폼에 자동으로 동기화되도록 만들어 주었기 때문입니다.
만약 여러분도 멀티 플랫폼 AI 애플리케이션을 만들고 있다면, 첫날부터 Monorepo와 공유 packages를 사용할 것을 강력히 권장합니다. 늦어질수록 비용은 기하급수적으로 상승합니다.
IHUI AI 소개
IHUI AI는 Apache 2.0 오픈 소스인 원스톱 8개 플랫폼 풀스택 AI 운영체제입니다.
- 🌐 공식 웹사이트: https://aizhs.top
- 💻 GitHub: https://github.com/IHUI-INF-AI/IHUI-AI (Star ⭐로 응원해 주세요)
- 📦 8개 플랫폼 동원: Web / API / CLI / Desktop / Extension / Mobile / Miniapp
- 🤖 176개 모델: OpenAI / Claude / Gemini / Tongyi / DeepSeek / Zhipu / Wenxin / Doubao / Kimi / Ollama
- 💰 가격 정책: Free / Pro ¥49/월 / Team ¥199/인/월 / Enterprise ¥2999/월부터
5분 만에 Fork 하여 배포하세요. ChatGPT Team + Claude Code + Notion AI를 대체하며, 매달 $60 이상을 절약할 수 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기