Rich Editor Note: 노션 에디터 유사 메모 앱

Rich Editor Note: 노션 에디터 유사 메모 앱

📅기간
2025-04 ~ 2025-07
🔗GitHub
https://github.com/treephesians/bible-note
기술
React NativeSQLiteSupabaseReact개인
🎯종류
개인

소개

노션과 같은 온/오프 환경에서 사용 가능한 메모앱이며, 기본 노트앱과 다르게 Rich Text Editor를 제공합니다.
두 프로젝트를 포함한 모노레포입니다.
  • app-bible-note: React Native 모바일 앱 (iOS/Android)
  • web-bible-note: React + Vite 기반 웹 에디터
핵심은 “웹 기반 리치 텍스트 에디터”를 모바일 앱의 WebView로 안정적으로 구동하기 위한 기술 설계입니다.
또한 노션과 같이 오프라인 환경에서도 메모장을 활용할 수 있도록, 정적 서버를 실행시키도록 만들었습니다.

프로젝트 구조

  • app-bible-note/ 모바일 앱 소스
  • web-bible-note/ 웹 에디터 소스
  • editor.zip 에디터 정적 자원 예시(루트에 위치, 배포/테스트용)

아키텍처 개요

  • 웹 에디터(web-bible-note)
    • React + TipTap(프로즈미러 기반) + Vite
    • 커스텀 노드/입력 규칙으로 성경 구절 블록을 생성Supabase를 통한 노트 저장
  • 모바일 앱(app-bible-note)
    • React Native 0.79 + Hermes. WebView로 에디터를 로드
    • 원격 editor.zip을 내려 받아 기기 내에 압축 해제한 뒤 로컬 정적 서버로 서빙
    • 네트워크 상황과 플랫폼 제약에도 일관된 UX 제공

웹 에디터 구현

에디터 코어

  • 파일: web-bible-note/src/features/editor/useMyEditor.ts
  • 핵심 포인트:
    • TipTap useEditor 기반. StarterKit에 heading 레벨 제한, Placeholder, Focus 적용.
    • 커스텀 CustomBackspaceExtension
      • 빈 블록에서의 백스페이스 동작을 제어해 heading/리스트/커스텀 노드에서 자연스럽게 단락으로 전환하거나 리스트 리프팅을 수행.
    • onUpdate에서 디바운스 자동 저장(1초).
      • 저장은 updateNote(noteId, title, content)로 Supabase에 반영.
const editor = useEditor({ extensions: [ StarterKit.configure({ heading: { levels: [1, 2, 3] } }), BibleVerse, BibleVerseInput, CustomBackspaceExtension, Focus.configure({ className: "has-focus", mode: "all" }), Placeholder.configure({ /* 노드 타입별 placeholder */ }), ], content: initialContent || "<p>@ 명령어를 입력하고 성경 구절을 작성해보세요.</p>", onUpdate: ({ editor }) => { if (editor) debouncedSave(editor.getJSON()); }, immediatelyRender: false, });

성경 구절 기능(커스텀 노드 2종)

  • 입력 노드: BibleVerseInput (@ 입력 규칙으로 생성)
    • 파일: web-bible-note/src/features/editor/extensions/bible-verse/BibleVerseInput.ts
    • @ 입력 시 블록 삽입 → 엔터 시 현재 입력 텍스트를 구절 레퍼런스로 파싱, fetchBibleVerse(reference)로 컨텐츠를 비동기 조회 → 현재 입력 블록을 bibleVerse 노드로 치환하고, 필요시 뒤에 단락 자동 생성 및 커서 이동.
addInputRules() { return [ new InputRule({ find: /@ $/, handler: ({ chain, range }) => { chain().deleteRange(range).insertContent({ type: "bibleVerseInput" }).run(); }, }), ]; },
  • 표시 노드: BibleVerse
    • 파일: web-bible-note/src/features/editor/extensions/bible-verse/BibleVerse.ts
    • NodeView로 React 컴포넌트를 연결해 리치한 렌더링. reference, content, id 속성 보유, 드래그/선택 가능.
export const BibleVerse = Node.create<BibleVerseOptions>({ name: "bibleVerse", group: "block", selectable: true, draggable: true, atom: true, addNodeView() { return ReactNodeViewRenderer(BibleVerseComponent); }, });

모바일 앱 구현(설계 중심)

로딩 전략과 실행 모드

  • 파일: app-bible-note/App.tsx
  • 전략:
    • 로컬 개발 모드
      • .envIS_LOCAL_WEBVIEWtrueLOCAL_SERVER_URL로 바로 접속.
    • 원격 배포 모드
      • 앱이 실행되면서 원격 editor.zip을 다운로드/압축 해제
      • 앱 내 로컬 정적 서버(127.0.0.1:3000)로 서빙 후 WebView가 해당 URL로 접속.
if (IS_LOCAL_WEBVIEW === 'true') { setOrigin(LOCAL_SERVER_URL); setPreparingServer(false); return; } // 원격 모드: FileService 진행 상태 콜백 등록 → 완료 시 ServerService.startServer(rootDir)
 

정적 자원 준비

  • 파일: app-bible-note/src/services/FileService.ts
  • 역할:
    • 웹 루트 디렉토리 생성/검증 → 로컬 version 읽기 → 원격 버전과 비교(현재 샘플은 '1.1' 하드코딩; 실제 구현 시 서버에서 조회).
    • downloadFile()REMOTE_SERVER_URL/editor.zip 다운로드(진행도 콜백으로 UI 반영)
    • → 존재/사이즈 검증 → unzip()으로 압축 해제 → 필요 시 editor/ 서브폴더 감지
    • version 갱신 → 완료 콜백으로 서버 시작 지점 알림.
    • 오류/네트워크 이슈 시 Android는 번들 자산(copyFileAssets('editor', fileDir))로 폴백.
기술 포인트:
  • 파일 I/O: @dr.pogodin/react-native-fs
  • 압축 해제: react-native-zip-archive
  • 진행 상황 UI: DownloadProgress를 상태로 노출 → WebViewScreen에서 스피너/메시지 표시
 

로컬 정적 서버

  • 파일: app-bible-note/src/services/ServerService.ts
  • 역할:
    • @dr.pogodin/react-native-static-serverfileDir을 루트로 바인딩, 127.0.0.1:3000에서 서빙.
    • 간단한 mod_alias/mod_rewrite 설정으로 유연한 경로 처리.
    • 서버 시작/중지와 상태 변경 콜백(ServerStatus) 제공.
const server = new Server({ fileDir: rootDir, hostname: '127.0.0.1', port: 3000, /* ... */ }); const res = await server.start(); if (res) { this.updateStatus(res, true); }
 

WebView 화면 및 키보드 UX

  • 파일: app-bible-note/src/screens/WebViewScreen.tsx
  • 역할:
    • origin이 준비될 때까지 다운로드/압축/서버 준비 상태를 UI로 표시.
    • injectedJavaScript로 입력 폰트/선택 동작 UX 보완, 포커스/블러 이벤트를 React Native로 전달.
    • 키보드 툴바(KeyboardToolbar)를 별도 오버레이로 제공, iOS/Android 모두 자연스러운 편집 경험.
    • onShouldStartLoadWithRequest에서 허용된 오리진만 로드
    • 외부 링크는 Linking.openURL로 브라우저 위임.
 

정적 패키지 구조와 호환성

  • editor.zip은 두 가지 구조를 모두 지원합니다.
      1. zip 루트에 빌드 결과물을 직접 포함
      1. zip 내부 최상위에 editor/ 폴더를 두고 그 아래에 빌드 결과물 포함
  • 압축 해제 후 editor 서브폴더 유무를 감지해 서버 루트를 설정합니다.
  • version 파일을 함께 관리하여 업데이트 여부를 판단합니다(Android 폴백 시 editor/version 번들 자산과 비교).
 

보안/성능 고려

  • WebView는 로컬 정적 서버에서만 자원을 로드하므로, 네트워크 의존도를 낮추고 오프라인 친화적입니다.
  • 캐시 정책은 개발 편의와 일관성을 위해 LOAD_NO_CACHE를 사용(필요시 빌드 단계에서 조정 가능)
 

Next Step

  • 원격 버전 조회/서명 검증(무결성 체크) 도입
  • 증분 업데이트(전체 zip 재다운로드 대신 diff/etag 기반)
  • Supabase 오프라인 큐/재시도(네트워크 복구 시 동기화)
  • 딥링크/성경 구절 공유 → 에디터 포커스 이동