이 페이지에서
JunkBox apps/message-ui 가이드
1. 애플리케이션 역할
apps/message-ui는 Message 관리 화면 전용 Vite 6 기반 React + TypeScript SPA임.
message-api가 제공하는 서버 렌더링 Message 앱 셸 위에 단일 React 엔트리를 마운트함.
즉시 발송, 발송 이력, 예약 메시지, 정기 메시지 화면과 화면 간 클라이언트 라우팅을 소유함.
실제 메시지 생성·Apprise 호출·이력 저장·스케줄 실행은 UI가 아닌 message-api 서비스 계층이 담당함.
2. 런타임과 엔트리
개발 서버 기본 포트는 15004임.
운영 산출물은 apps/message-ui/dist에 생성되며 message-api가 /message-ui 경로로 서빙함.
/, /message-ui, /message-ui/ 개발 진입은 /message-ui/messages로 리다이렉트됨.
단일 엔트리 src/main.tsx는 shared-ui bootstrap()과 BrowserRouter로 message-ui-root에 마운트함.
배포 자산은 Vite manifest 기준으로 해석하며 해시된 JavaScript 파일명을 템플릿에 하드코딩하지 않음.
3. 라우팅과 앱 셸 연동
/messages: 즉시 발송 화면임.
/message-history: 발송 이력 목록 화면임.
/message-schedules: 예약 메시지 목록 화면임.
/message-schedules/new: 예약 메시지 등록 화면임.
/message-recurrences: 정기 메시지 목록 화면임.
/message-recurrences/new: 정기 메시지 등록 화면임.
공통 앱 셸은 spa_navigation=true에서 같은 origin 메뉴 클릭을 junkbox:spa-navigate 이벤트로 발행함.
Message React 앱은 이 이벤트를 수신해 shared-ui createSpaNavigateOptions(), runSpaViewTransition()과 React Router navigate()로 화면을 이동함.
라우트 변경 시 resolveActiveNav()와 applyActiveNavState()로 앱 셸 메뉴 활성 상태를 동기화함.
라우트 본문은 SpaRouteTransition으로 감싸고 useSpaRouteSettling()으로 스크롤 초기화와 모바일 고정 액션바 안착 상태를 처리함.
메뉴 클릭 뒤 shared-ui closeAppDrawer()를 호출하여 모바일 드로어를 닫음.
4. 개발 서버 프록시
/api/message, /shared-ui, /messages, /message-history, /message-schedules, /message-recurrences는 message-api 개발 서버 18004로 프록시함.
/message-ui/messages, /message-ui/message-history, /message-ui/message-schedules, /message-ui/message-recurrences도 base 경로를 제거해 message-api 웹 라우트로 프록시함.
개발 페이지 HTML은 message-api가 렌더링하고 React 엔트리, Vite client, React refresh 자산은 15004/message-ui/*에서 로드함.
5. 화면 구조
모든 화면은 jb-mobile-page 기반의 모바일 전용 360px 고정 폭과 safe-area 규칙을 사용함.
페이지 제목은 앱 셸 아래 jb-screen-head, jb-page-title-section 구조로 표시함.
즉시 발송 화면은 출처, 제목, 본문, 메시지 유형, Apprise 설정 키를 입력하고 하단 고정 발송 버튼으로 요청함.
발송 이력 화면은 최근 메시지 우선 카드 목록과 재발송 액션을 제공함.
예약·정기 목록 화면은 각각의 목록과 하단 고정 신규 등록 버튼을 제공함.
예약 등록 화면은 공통 메시지 필드와 예약 시각을 입력함.
정기 등록 화면은 일정 이름, 공통 메시지 필드, 반복 유형, 반복 설정을 입력함.
화면별 CUD 주요 액션은 jb-mobile-bottom-bar의 공통 버튼 정책을 사용함.
6. 선택 UI와 공통 자산
공통 React 계층은 @shared-ui/* alias로 libs/shared-ui/src/react를 참조함.
메시지 유형과 정기 반복 유형은 네이티브 <select>를 사용하지 않음.
선택 필드는 jb-select 외형의 버튼과 BottomOptionPicker 하단 선택 시트를 사용함.
BottomOptionPicker 옵션값은 API 계약값을 유지하며, 화면에는 사용자 이해용 라벨을 표시함.
앱은 자체 CSS 파일, <style> 태그, inline style, 앱별 font-family 선언을 소유하지 않음.
공통 CSS는 서버 템플릿의 stylesheet 매크로가 제공하는 hub-ui-base.css + hub-ui-mobile.css만 사용함.
한글·영문·숫자 폰트는 shared-ui PretendardVariable.woff2와 중앙 --jb-font-family 선언을 사용하며 로컬 설치 폰트나 외부 폰트를 우선하지 않음.
공통 아이콘, 토스트, 바텀시트, 앱 셸, 모바일 고정 액션바가 필요하면 화면 로직에서 복제하지 않고 shared-ui를 우선 사용함.
7. UI 문구와 오류 표시
버튼, 라벨, placeholder, 빈 목록, 오류 문구는 소스에 한국어로 직접 작성함.
일반 UI 텍스트를 위해 DB message code나 마스터데이터 키를 만들지 않음.
문장형 사용자 노출 메시지는 하십시오체와 마침표를 사용함.
목록·발송 요청 오류는 API 오류 응답의 안전한 상세 문구 또는 화면의 기본 오류 문구로 표시함.
8. 구현 제약
Message UI는 Apprise URL, 기본 설정 키, 인증 정보, SQLite 파일을 직접 읽거나 노출하지 않음.
외부 셸 스크립트는 UI가 아닌 /api/message/messages API를 사용함.
새 Tailwind utility class를 추가하면 Message UI Vite 빌드와 별도로 shared-ui 스타일 자산을 빌드해야 함.
인증·권한 UI를 추가하는 경우 message-api의 실제 API 인증 정책과 Hub JWT/Cookie/Header 계약을 함께 정리해야 하며, 화면만으로 접근 제어를 구현하지 않음.