웹 클라이언트
뮤블의 클라이언트는 웹 기반으로 동작하며, 웹용 SSR과 네이티브용 CSR를 모두 지원하는 구조로 이루어져 있습니다.
주요 사용 스택
- 프레임워크: React , React Router v7 , Tauri v2
- 애니메이션 라이브러리: motion 1
- 에디터: Prosemirror
메인 구조
- deprecated는 추후 플러그인 및 패키지로 분리될 예정입니다.
빌드 및 개발 환경 명령어
# cwd: root
pnpm build:libs # 라이브러리(plugin 및 packages) 빌드 명령어
pnpm dev:libs # 라이브러리(plugin 및 packages) watch-build 명령어 (개발 시 사용)
# cwd: apps/client
pnpm build # 빌드
pnpm dev # 개발 서버 실행
pnpm typecheck # 클라이언트 타입 체크
# cwd: root
pnpm build:client # 빌드
pnpm dev:client # 개발 서버 실행- 클라이언트 개발은 대부분 뮤블 플러그인을 수정하는 과정으로 이루어집니다.
- 플러그인 코드를 수정할 경우 디버깅에서 핫로딩을 지원하지만, IDE에서의 타이핑 및 오류 발견을 위해서는 별도의 빌드가 필요합니다.
- 인간 개발자의 경우
pnpm dev:libs를 실행하여 뮤블 플러그인 수정이 실시간으로 빌드되게 할 수 있습니다.pnpm dev:libs는 실시간 변경사항만 반영하므로, 빌드가 안 된 상태에서 사용할 경우 제대로 작동하지 않을 수 있습니다. 사용 전pnpm build:libs를 실행하여 빌드를 해주세요.
- AI 개발자의 경우 플러그인 및 패키지를 수정할 때마다
pnpm build:libs를 실행하여 빌드를 해주세요. - IDE 특성 상 빌드 직후에는 타입이나 오류가 명확히 잡히지 않을 수 있습니다. 이 경우 기다리거나
pnpm typecheck를 실행하여 직접 타입 체크를 해주세요.
개발 참고사항
-
크로스플랫폼에 관하여
- 뮤블은 프로젝트를 클라우드에 실시간 저장하는 옵션(cloud-first)과 로컬에 저장하는 옵션(local-only)을 제공합니다. 따라서 백엔드가 동원되는 기능을 개발할 때는 대부분 이 두 API를 모두 구현해야 합니다,
- 클라우드 저장소 API는
apps/server에서 제공되며,packages/cloud-client에서 추상화된 API 클라이언트 클래스를 제공합니다. - 로컬 저장소 API는
apps/tauri-app에서 제공되며,packages/tauri-commands에서 TypeScript로 포팅된 호출 메서드를 제공합니다. 단, 특정 플러그인 기능에서만 사용하도록 분리된 Tauri API는packages/tauri-commands에서 제공되지 않으며 각 플러그인이 직접@tauri-apps/api를 사용하여 호출합니다. packages/tauri-commands와packages/cloud-client는packages/api-types를 공통의 타입 정의로 사용합니다.- 해당 프론트엔드는
tauri-app에서도 네이티브 앱 용으로 공유됩니다. 웹 배포에는 SSR을 사용하고, 네이티브 앱 배포에는 CSR을 사용합니다. routes.ts및routes/에서 확인 가능하듯 CSR과 SSR용 라우트가 구분되어 있습니다.- 새로운 기능을 구현하거나, 데이터의 구조를 변경할 경우
apps/server,apps/tauri-apps둘 다 구현체 변경이 필요하며,packages/api-types도 대부분의 상황에서 수정해야 합니다.
-
뮤블 플러그인 시스템
본 문단은 클라이언트의 뮤블 플러그인 사용 및 개발에 관한 내용만을 다룹니다. 플러그인 개발 자체에 관한 자세한 내용은 해당 문서를 참고해주세요.
- 뮤블 플러그인 시스템은 2025년 2월부터 도입되었으며, 앞으로 추가되는 기능과 기존의 기능들은 뮤블 플러그인 시스템을 통해 구현되어야 합니다.
- 각 기능은 독립된 플러그인 패키지로 분리되며, 각 플러그인은
@muvel-plugins/네임스페이스를 사용합니다. - 베이스 시스템이 분리되지 않아 의존성을 제거할 수 없어 현재로서는 별개 패키지로 분리하기 힘든 기능의 경우
apps/temp-plugins폴더에 넣은 후apps/system/plugins.ts에서 직접 임포트하여 구현할 수 있습니다. 차후 분리가 가능해질 경우plugins폴더로 옮겨주시면 됩니다. - 플러그인 사용을 위해서는
client내package.json에 해당 의존성을 아래와 같이 추가하여야 합니다.
"dependencies": { "@muvel-plugins/auth": "workspace:*", "@muvel-plugins/background-sync.android": "workspace:*", }- 플러그인이
peerDependencies로 사용하는 라이브러리는 무조건client의package.json에 의존성에 존재해야 합니다. 그렇지 않을 경우 프로덕션에서 오류가 발생할 수 있습니다.
-
번역 키 작성
- 뮤블은
react-i18next라이브러리를 사용하여 번역 키를 작성합니다. - 사용자에게 노출되는 모든 텍스트는 번역 키로 작성하는 것이 원칙입니다.
- 번역 키는
camelCase형식으로 작성하는 것이 원칙입니다. - enum 값을 기반으로 하는 번역 키는 enum 값을 키로 사용해주세요.
- 번역 파일은
app/locales폴더에 있습니다. - 기본 언어 및 폴백 언어는 한국어(
ko.ts)로 설정되어 있습니다. react-i18next를 peerDependencies로 추가할 경우 같은 인스턴스가 공유되므로 번역 키를 공유할 수 있습니다.
- 뮤블은
-
더 이상 유효하지 않은 패턴
컴포넌트 등에는 Atomic Design + Feature-based 패턴을 사용합니다.뮤블 플러그인 시스템이 도입되며, 현재features폴더 내의 기능들은 차차 플러그인으로 분리되며 일부 컴포넌트만 Atomic Design 패턴으로components폴더에 남게 됩니다.네이티브와 웹 모두를 지원해야 하므로환경에 따른 동적 분기가 필요한 기능은 뮤블 플러그인 시스템으로 구현하며, 더 이상 이 후크로 기능을 분기하는 것은 권장하지 않습니다.usePlatformhook을 이용해서 사용 환경을 분기하는 것이 핵심입니다.
최신 의존성 안내사항
프로젝트에서 메인으로 사용되는 최신 라이브러리에 대한 정보를 안내합니다. 아래에 설명된 라이브러리 및 프레임워크들은 구 버전과 API 구조가 크게 차이나므로 주의가 필요합니다.
-
React-Router v7 (2024년 11월 22일 출시)
react-router-dom과 프레임워크인remix가 합쳐지면서react-router패키지로 통합되었습니다.- v7 공식 문서
- v6부터의 마이그레이션
- Remix로부터의 마이그레이션
-
Chakra UI v3 (2024년 10월 22일 출시)
- 대부분의 컴포넌트 사용법이 변경되었기에 문서 참고가 필수적입니다.
- v3 공식 문서
- v2에서 v3로의 마이그레이션
- 구 버전과 자주 헷갈리는 부분
- 사용법이 바뀜:
IconButton,toaster,colorScheme → colorPalette - breakpoint
{base: 'value', sm: 'value'} - color-mode
{base: 'value', _dark: } - HTML 표준으로 바뀜:
isDisabled→disabled,isLoading→loading등 - 삭제됨
Modal(삭제됨),spacing속성(삭제됨) <Divider/>→<Separator/>sx={{...}}→css={{...}}- 네임스페이스 방식으로 변경:
Dialog,Drawer,Alert,Tag,Checkbox등 QRCode,Combobox,Marquee,TreeView,TagsInput등 다양한 신규 컴포넌트가 추가되었습니다.
- 사용법이 바뀜:
-
motion/react v12 (2024년 1월 경 변경)
- 기존 Framer Motion 패키지는 Motion for React라는 이름의 독립 프로젝트로 분리되었습니다.
- 이로 인해
framer-motion패키지는 더 이상 사용되지 않으며,motion패키지를 설치해야 합니다. import { motion } from "framer-motion"→import { motion } from "motion/react"motion()은 deprecated 되었습니다.motion.create()를 사용해 주세요.- Chakra UI v3 컴포넌트와 같이 사용할 경우 타이핑 이슈로
as={}대신asChild를 사용하거나moiton.create()로 미리 만들어 둔 컴포넌트를 활용하세요.
Footnotes
-
해당 라이브러리는
@muvel/ui패키지를 통해 reexport 되어 사용됩니다. ↩
Last updated on