이 페이지에서
1. 문서 목적
이 문서는 JunkBox 관리자 화면 구현 시 공통 UI 원칙을 일관되게 적용하기 위한 기준 문서임.
운영 화면의 레이아웃, 버튼, 그리드, 토스트, 여백, 메시지 사용 규칙을 정의함.
새로운 화면은 화면 전용 CSS 없이 shared-ui 공통 자산만으로 구현하는 것을 기본 원칙으로 삼음.
JunkBox의 UI 원칙은 이 문서를 단일 소스 오브 트루스로 삼으며, 다른 가이드는 UI 원칙을 중복 정의하지 않고 필요 시 이 문서를 참조함.
2. 최우선 원칙
2.1 스타일 중앙화
앱별 CSS 파일을 만들지 않음.
화면별 CSS 파일을 만들지 않음.
특별한 사유가 없으면 모듈별 CSS 파일도 만들지 않음.
<style> 태그와 style="" 인라인 스타일을 사용하지 않음.
모든 스타일은 libs/shared-ui에서 관리함.
화면 구현 시 템플릿은 jb-* 공통 클래스만 조합해서 사용함.
화면별로 필요한 보정이 있더라도 먼저 shared-ui 공통 클래스로 승격할 수 있는지 검토함.
공통 적용 가치가 없는 화면 전용 스타일은 추가하지 않고, 가능한 한 기존 공통 클래스 조합으로 해결함.
2.2 구조와 스타일 분리
각 화면은 데이터 구조와 마크업 구조만 정의함.
색상, 여백, 폰트, 버튼, 반응형 규칙은 shared-ui가 담당함.
화면에서 새로운 모양이 필요하면 먼저 shared-ui에 공통 클래스를 추가한 뒤 사용함.
2.2 시각 톤 기준
JunkBox 공통 다크 UI 톤은 현재의 검은 배경 운영 콘솔 감성을 유지하되, ChatGPT 또는 Open WebUI 다크모드와 유사한 저대비 톤앤톤 체계를 기준으로 삼음.
순백에 가까운 강한 외곽선, 과한 대비, 화면마다 제각각인 강조색 사용을 지양함.
패널, 카드, 입력창, 보조 버튼, 칩, 바텀시트는 모두 저채도 회색 경계 + 아주 약한 레이어 배경 + 절제된 그림자 규칙 안에서 일관되게 구성함.
구분은 가능한 한 강한 테두리보다 배경 톤 차이, 간격, 깊이감으로 해결함.
새로운 운영 화면을 만들 때는 임의의 시각 스타일을 만들기보다 shared-ui 공통 다크 톤 규칙을 그대로 따르는 것을 우선함.
성공·실패처럼 즉시 식별이 필요한 상태는 shared-ui 공통 상태 아이콘을 사용함. 성공은 녹색 IconCircleCheck와 jb-status-icon-success, 실패는 적색 IconCircleX와 jb-status-icon-failure 조합을 사용함.
상태 아이콘의 색상과 제목 사이 간격은 shared-ui 클래스가 담당하며 화면별 색상·여백 CSS를 추가하지 않음.
2.3 TypeScript 우선 구현
운영 화면 인터랙션 소스는 .ts 또는 .tsx로만 작성함.
신규 화면 로직을 .js로 직접 작성하지 않음.
TypeScript strict 기준을 유지함.
API 응답, 테이블 행, 페이지 props는 명시적 타입으로 선언함.
브라우저 전역 변수 주입을 기본 구조로 사용하지 않으며, 환경값은 import.meta.env와 타입 선언을 통해 관리함.
2.4 PC 우선 운영 화면 기준
hub-api 성격의 운영 화면은 FHD 기준 PC 우선으로 설계함.
전체 페이지 스크롤보다 내부 그리드와 패널 스크롤을 우선함.
한 화면에서 더 많은 데이터를 보여주는 밀도를 우선함.
운영 화면은 FHD 기준에서 브라우저 바깥 세로 스크롤이 생기지 않도록 구성함.
스크롤이 필요하면 페이지 전체가 아니라 그리드, 우측 편집 패널, 모달 본문 같은 내부 작업 영역에서만 발생하도록 설계함.
로그인, 포털, workout 모바일 화면처럼 모바일 전용으로 정의된 화면은 이 규칙의 예외이며, 공통 360px 고정 폭 기준을 사용함.
대시보드처럼 PC/모바일 겸용으로 정의된 운영 화면은 PC FHD 밀도와 모바일 max-width: 360px 세로 카드 목록을 모두 만족해야 함.
2.5 화면 문구 작성 기준
사용자가 화면에서 보는 버튼, 라벨, 토스트, 확인창, 상태 문구는 소스 코드에 한국어 문구를 직접 작성함.
텍스트만을 위해 DB 메시지 마스터를 추가하지 않음.
같은 의미의 공통 표현은 문서화된 표현 규칙과 현재 화면 문구를 우선 재사용함.
공통코드 라벨, 메타 설정값처럼 데이터 자체가 DB 마스터데이터인 값은 현재 코드/메타 조회 구조를 사용함.
로그, 콘솔, 주석, 개발자 디버그 문자열은 사용자 노출 UI 텍스트 기준에 포함하지 않음.
2.6 날짜 표기 표준
날짜만 표기할 때의 기본 형식은 YYYY-MM-DD임.
날짜와 시간을 함께 표기할 때의 기본 형식은 YYYY-MM-DD HH24:MI:SS임.
locale 기본 문자열이나 화면별 임의 포맷을 사용하지 않고 공통 포맷 함수 기준으로 통일함.
외부 사이트가 제공한 게시물 날짜처럼 원본 문자열 자체가 사용자에게 의미 있는 source metadata이면, 명확하게 해석 가능한 날짜/시간만 공통 표준으로 정규화하고 해석이 모호한 형식은 원본 문자열을 보존함.
workout 세션 목록 카드의 운동일은 사용자가 운동 요일을 빠르게 구분해야 하므로 YYYY-MM-DD (요일) 형식을 사용함.
3. 공통 자산 위치
3.1 CSS
공통 앱 셸, 패널, 폼, 버튼, 레이아웃 규칙은 libs/shared-ui의 공통 스타일에서 관리함.
Tabulator 공통 스타일도 shared-ui가 관리함.
앱은 빌드된 산출물만 링크함.
공통 CSS 링크는 shared-ui의 stylesheet 매크로에서만 선언함.
공통 베이스 템플릿과 standalone 템플릿은 같은 shared-ui stylesheet 매크로를 호출해 CSS 조합만 선택함.
개별 화면 템플릿에서 hub-ui-base.css, hub-ui-mobile.css, hub-ui-desktop.css, Tabulator vendor CSS, junkbox-tabulator.css를 중복 링크하지 않음.
모바일 화면은 기본적으로 hub-ui-base.css + hub-ui-mobile.css 조합을 사용하고, 데스크탑 운영 화면은 hub-ui-base.css + hub-ui-desktop.css 조합을 사용함.
Tabulator 화면은 vendor/tabulator/tabulator_simple.min.css + junkbox-tabulator.css 조합을 shared-ui stylesheet 매크로의 Tabulator 옵션으로 함께 로드함.
SPA 라우트 전환, skeleton, 터치 피드백, reduced motion 대응처럼 화면 크기와 무관한 상호작용 규칙은 hub-ui-base.css에서 제공함.
모바일 고정 폭, 모바일 바텀 액션바, 모바일 시트처럼 실제 모바일 레이아웃에 종속된 규칙은 hub-ui-mobile.css에서 제공함.
공통 베이스 템플릿은 별도 <style> 블록을 소유하지 않으며, 모든 시각 규칙은 shared-ui CSS 산출물로만 공급됨.
3.2 템플릿
공통 베이스 템플릿을 사용함.
공통 그리드 헤더 마크업을 사용함.
앱 템플릿은 공통 베이스를 extends 하고 필요한 블록만 채움.
로그인, Chess 복기 화면처럼 공통 앱 셸을 직접 상속하지 않는 standalone HTML 템플릿도 CSS 링크를 직접 쓰지 않고 shared-ui stylesheet 매크로를 import해 사용함.
standalone 템플릿은 화면 구조만 소유하고, 공통 CSS 파일 조합과 vendor CSS 링크 선언은 shared-ui 템플릿 책임으로 둠.
3.3 스크립트
Tabulator 공통 헬퍼와 토스트 처리는 shared-ui 공통 스크립트를 사용함.
공통 앱 셸 동작도 shared-ui 스크립트를 사용함.
화면별 TypeScript 코드는 데이터 로딩, 이벤트 연결, API 호출만 담당함.
서버는 초기 props만 주입하고, 브라우저는 이를 읽어 React 화면을 마운트하는 구조를 기본으로 함.
빌드 자산 참조는 고정 파일명 하드코딩이 아니라 Vite manifest 해석 구조를 사용함.
SPA 화면은 공통 앱 셸 링크 클릭을 junkbox:spa-navigate 이벤트로 전환하고, React Router navigation과 shared-ui SPA 유틸로 실제 본문 전환을 처리할 수 있음.
3.4 SPA 상호작용 기준
SPA 적용은 서버 라우트가 같은 앱 셸을 반환하고, 클라이언트 React Router가 화면 선택을 담당하는 구조를 기준으로 함.
SPA 화면의 라우트 본문은 SpaRouteTransition으로 감싸고, route settling은 useSpaRouteSettling으로 처리함.
브라우저 View Transition API는 runSpaViewTransition을 통해 사용하며, 지원하지 않는 브라우저에서도 동일 navigation callback이 실행되어야 함.
빠르게 끝나는 데이터 요청의 로딩 깜빡임은 useStableLoading으로 제어함.
최상위 데이터 로딩 placeholder는 SkeletonBlock을 우선 사용함.
reduced motion 환경에서는 라우트 전환, skeleton shimmer, 오브젝트 안착 애니메이션이 사용자 설정을 존중해야 함.
SPA 전환 체계는 모바일 전용이 아니며, PC 운영 화면도 앱 단위 SPA 구조가 필요한 경우 같은 공통 체계를 사용할 수 있음.
4. 화면 레이아웃 기준
4.1 전역 앱 셸
전역 메뉴 체계는 공통 셸 기준으로 구성함.
데스크탑에서는 상단 메뉴를 기본으로 함.
모바일 폭에서는 토글형 드로어 메뉴를 사용함.
공통 메뉴바의 반응형 기준은 각 화면 본문 폭 정책과 분리함.
PC-only 본문 화면도 모바일 뷰포트에서는 공통 메뉴바가 드로어 메뉴로 전환됨.
모바일 전용 본문 화면이더라도 글로벌 헤더는 필요한 경우 반응형 메뉴 체계를 그대로 유지할 수 있음.
모바일 전용 본문 화면은 shared-ui의 고정 폭 클래스와 safe-area 대응 규칙을 그대로 사용함.
4.2 본문 여백
상단 메뉴바와 본문 좌우 여백은 기본 1rem 기준임.
과한 패딩, 중첩 래퍼, 불필요한 감싸기 div를 만들지 않음.
동일한 결과를 낼 수 있다면 더 얇은 마크업 구조를 우선함.
jb-panel 같은 공용 래퍼가 화면 구조를 한 번 더 감싸서 실질적 이점이 없다면 제거함.
대표 작업 화면은 jb-workspace-screen과 jb-workspace-grid 중심의 얇은 구조를 우선 사용함.
공통 루트 컨테이너와 화면 마운트 루트는 부모 높이를 끝까지 물고 있어야 하며, 화면마다 다른 높이 계산이 생기지 않도록 공통 규칙으로 관리함.
4.3 작업형 화면
FHD 기준 화면은 좌우 폭을 넓게 사용함.
데이터 관리 화면은 패널 간 여백을 최소화함.
페이지 전체 스크롤보다 내부 패널 스크롤을 우선함.
상단 공용 페이지 헤더가 실질적 작업 정보를 늘리지 못하면 숨기고, 화면 본문 안에 간결한 제목만 배치함.
운영 화면 제목은 과도한 display 타이포그래피를 사용하지 않고, h2 수준의 절제된 크기를 기본으로 함.
동일 계열의 관리 화면은 제목 크기와 표기 방식을 통일함.
작업형 화면 제목과 바로 아래 작업 영역 사이에는 너무 붙어 보이지 않도록 공통 간격을 둠.
좌측 선택 그리드와 우측 편집 패널 구조에서는 기본적으로 6:4 비율을 우선 검토함.
상단 요약 패널, 필터/상태 패널, 하단 그리드 패널이 함께 있는 화면은 상단 패널이 내용 높이만큼만 차지하고, 하단 그리드 패널이 남는 높이를 모두 차지하도록 구성함.
상단 요약이나 메타 영역이 줄바꿈으로 높이가 늘어나면 그만큼 아래 그리드 높이가 줄어들어야 하며, 아래 영역 위로 겹치거나 침범하면 안 됨.
포털처럼 카드형 소비 화면은 일반 작업형 화면과 다른 예외 패턴이며, 공통 shared-ui 클래스 내에서만 전용 레이아웃을 제공함.
포털 카드형 소비 화면은 모바일에서도 기본 2열 구조를 유지하며, 카드 내부 타이포와 아이콘 크기만 모바일 기준으로 보정함.
대시보드 화면은 카드형 동적 데이터 화면이며 PC 기준 4열 x 2행 고정 슬롯 배치와 모바일 max-width: 360px 기준 1열 세로 배치를 모두 지원함.
대시보드 큰 카드는 PC 기준 항상 한 슬롯만 차지하고, 카드 수가 한 행보다 적어도 카드 높이를 화면 남은 높이 전체로 늘리지 않음.
대시보드 카드 내부 목록은 고정 카드 높이를 넘으면 카드 내부 스크롤을 사용하고, 페이지 전체 스크롤보다 카드 내부 밀도 조정을 우선함.
대시보드처럼 외부 수집 데이터를 표시하는 화면은 카드별 갱신 시각, 오류 상태, 외부 전체보기 링크를 자체적으로 표현해야 함.
대시보드처럼 홈서버/시스템 메트릭을 표시하는 화면은 요약 수치, 게이지, 세부 목록을 같은 카드 그리드 안에서 배치하고 페이지 전체 스크롤보다 카드 내부 밀도 조정을 우선함.
대시보드의 카드형 정보 행과 지표 타일은 플랫한 저대비 배경과 얇은 경계선만 사용하며, 특정 카드만 강한 색상 음영이나 그라데이션으로 분리하지 않음.
5. 공통코드 화면 레퍼런스
5.1 권장 구조
공통코드 화면은 좌측 코드 타입 목록, 우측 메타 및 코드 목록 구조를 기준으로 함.
좌측은 작은 마스터 목록 그리드임.
우측은 선택된 항목의 메타 편집과 상세 그리드를 함께 관리하는 작업 영역임.
좌측과 우측 그리드 높이는 화면 하단에서 맞춰 끝나야 함.
대표 레퍼런스 구조는 현재 /codes, /metas, /users 화면 계열임.
같은 관리 콘솔 계열 화면은 제목 위치, 패널 밀도, 액션 배치 톤을 가능한 한 동일하게 유지함.
5.2 좌측 패널
좌측은 별도 검색 폼을 두지 않음.
그리드 상단 headerFilter 같은 Tabulator 자체 검색을 우선 사용함.
타입 추가, 타입 저장, 새로고침 같은 최소 액션만 둠.
좌측이 목록 선택과 신규 진입 역할을 담당하는 화면이라면 좌측 액션은 신규, 새로고침처럼 목록 책임에 맞는 버튼만 둠.
우측 편집 데이터와 직접 관련된 저장, 삭제 버튼을 좌측 헤더에 섞어 두지 않음.
5.3 우측 패널
상단에는 꼭 필요한 액션 버튼만 배치함.
메타 입력과 하단 그리드 사이에는 경계선을 둠.
메타 편집과 하위 목록은 한 작업 영역으로 보고 함께 관리함.
우측이 실제 편집 책임을 가지는 화면이라면 저장, 삭제 같은 CUD 버튼은 우측 헤더에 둠.
선택 상태를 보여주는 칩이 실제 작업 판단에 도움이 되지 않으면 두지 않음.
우측 헤더 제목은 h3 수준의 공통 detail title 톤을 사용함.
6. 그리드 표준
6.1 공통 헤더 배치
그리드 상단 좌측에는 총 건수나 상태 텍스트를 둠.
그리드 상단 우측에는 버튼 영역을 둠.
이 구조는 모든 그리드가 동일하게 따름.
6.2 그리드 폭과 높이
특별한 사유가 없으면 가로 스크롤이 생기지 않도록 컬럼 폭을 먼저 조정함.
컬럼은 필요한 최소 폭만 사용함.
그리드는 부모 패널 높이를 채우도록 구성함.
상태성 컬럼은 좁게 유지하고, 설명/본문형 컬럼은 가능한 범위에서 줄여서 편집 영역이나 핵심 데이터 영역에 더 많은 폭을 배정함.
Tabulator 높이는 임의의 고정 px 값보다 부모 패널 기준 100% 채움 방식을 우선 사용함.
화면 전체 높이를 맞춰야 하는 운영 화면에서는 고정 높이 수치로 레이아웃을 밀어 올리지 않음.
6.3 Tabulator 사용 방식
조회, 인라인 수정, 추가, 삭제, 저장은 가능한 한 Tabulator 중심으로 처리함.
화면 바깥에 별도 입력 폼을 두기보다, 그리드 자체 편집 UX를 우선 검토함.
상위 메타와 하위 목록처럼 의미상 다른 데이터는 패널을 분리할 수 있음.
여러 행 삭제를 지원하는 관리 화면은 Tabulator 맨 앞에 선택 체크박스 컬럼을 배치하고, 실제 삭제는 선택 삭제 버튼을 통해 수행함.
행 클릭으로 상세 패널을 갱신하는 화면에서도 체크박스 선택 상태와 상세 편집 대상 선택은 함께 동작할 수 있어야 함.
list editor 같은 선택형 편집 셀은 평상시에도 편집 가능성을 드러낼 수 있도록 공통 chevron affordance를 사용할 수 있음.
선택형 편집 드롭다운은 브라우저 기본 흰색 팝업이 아니라 공통 다크 테마 규칙을 사용함.
일반 select option도 브라우저 기본 흰색 배경을 노출하지 않도록 공통 다크 필드 옵션 색상 규칙을 사용함.
6.4 그리드 로딩 정책
운영 화면의 그리드 로딩 방식은 데이터 성격과 편집 방식에 따라 표준을 구분함.
기준정보 또는 마스터 데이터이면서 그리드 내부에서 직접 수정, 추가, 삭제가 일어나는 화면은 기본적으로 전체 로딩을 사용함.
이 경우 사용자는 현재 로드된 전체 집합을 한 번에 보고 비교하거나 수정할 수 있어야 하며, 스크롤 로딩으로 편집 대상 범위를 나누지 않음.
계속 데이터가 누적되는 로그, 이력, 사용량 집계 성격의 화면은 기본적으로 서버 페이징을 사용함.
이 경우 전체를 한 번에 로딩하거나 점진 스크롤로 끝없이 붙이는 구조를 기본 표준으로 삼지 않음.
기준정보 또는 마스터 데이터이지만 그리드 내부 인라인 편집이 없고, 목록 선택이나 조회 역할만 담당하는 화면은 점진적 스크롤 로딩을 사용할 수 있음.
이 경우 초기 일부 데이터만 로드한 뒤, 내부 그리드 스크롤이 하단에 가까워질 때 다음 청크를 추가 로드하는 방식을 표준으로 함.
점진적 스크롤 로딩 대상 화면의 검색은 가능하면 Tabulator headerFilter를 유지하되, 구현은 서버 재조회 방식으로 설계할 수 있음.
점진적 스크롤 로딩을 구현할 때는 Tabulator 내부 tableholder가 초기 프레임 뒤에 생성될 수 있는 전제를 고려해야 함.
한 화면 안에 목록 선택용 그리드와 실제 편집용 그리드가 함께 있다면, 각 그리드는 역할에 따라 서로 다른 로딩 정책을 사용할 수 있음.
예를 들어 좌측 선택 목록은 점진적 스크롤, 우측 인라인 편집 목록은 전체 로딩을 적용할 수 있음.
이 기준은 단순 구현 취향이 아니라 화면 사용성과 저장 안정성을 위한 공통 설계 원칙으로 간주함.
따라서 새 관리 화면을 설계할 때는 먼저 해당 그리드가 인라인 편집형 마스터, 누적형 조회, 비편집형 마스터 목록 중 어디에 속하는지 분류한 뒤 로딩 방식을 결정함.
현재 rename 계열 화면은 운영 데이터 규모와 사용 패턴을 기준으로 Rename, Favorites, Logs 모두 풀 로딩 방식을 사용함.
따라서 rename 화면 구현과 유지보수에서는 점진 로딩 또는 서버 페이징보다 전체 로드 후 내부 필터와 선택 작업을 우선함.
6.5 검색 기준
별도 검색 폼보다 Tabulator의 headerFilter를 우선 사용함.
같은 목적의 검색 UI를 중복 배치하지 않음.
단순 조회형 리스트 화면은 별도 상단 검색 패널 없이 headerFilter만으로 해결하는 방식을 우선 검토함.
6.6 불린 컬럼 표현
Tabulator 내부의 is_active 같은 불린 컬럼은 기본적으로 체크박스형 tickCross 표현을 사용함.
동일한 의미의 불린 상태를 화면마다 pill, badge, 텍스트로 제각각 표현하지 않음.
별도 사유가 없으면 운영 화면의 활성 여부 컬럼 폭은 좁게 유지함.
6.7 Tabulator 셀 정렬
Tabulator 셀의 세로 정렬은 기본 셀 흐름을 해치지 않는 범위에서만 보정함.
셀 내부 vertical 정렬이 필요하더라도 .tabulator-cell 자체를 flex 같은 다른 레이아웃 모델로 바꾸지 않음.
컬럼 줄바꿈, formatter, ellipsis, 폭 계산에 영향을 줄 수 있는 셀 레이아웃 변경은 피함.
6.8 점수형 셀 표현
점수형 데이터는 가능하면 pill이나 과한 장식보다 플랫한 숫자 표기를 우선함.
점수 단계 구분은 숫자 색상으로 표현할 수 있음.
미확정 상태처럼 별도 주의가 필요한 경우에는 숫자 장식보다 셀 배경 강조를 우선 사용함.
7. 버튼 규칙
7.1 의미 기준 스타일
단순 UI 변경, 화면 전환, 새로고침, 조회, Export 성격 버튼은 jb-button-ghost를 사용함.
실제 DB 또는 YAML 정책 저장소에 대해 생성, 수정, 삭제가 발생하는 버튼은 jb-button을 사용함.
즉 CUD 인터랙션의 최종 확정 버튼은 기본적으로 흰색 바탕 강조 버튼으로 표현함.
화면 상태만 바꾸거나, 패널을 열거나, 선택만 하거나, 다운로드/조회만 수행하는 버튼은 흰색 강조 버튼을 사용하지 않음.
한 화면 안에서 여러 버튼이 함께 있을 때도 사용자가 무엇이 실제 저장을 일으키는지 즉시 구분할 수 있어야 함.
이 기준의 판단 포인트는 지금 이 클릭이 즉시 저장소에 반영되는가임.
예를 들어 화면에 행을 추가하거나, 운동 그룹을 열거나, 픽커를 띄우거나, 임시 편집 상태만 바꾸는 버튼은 결과적으로 저장 화면에 포함되더라도 즉시 DB/YAML에 반영되지 않으므로 jb-button-ghost를 사용함.
반대로 삭제처럼 자주 쓰이지 않는 액션이더라도 해당 클릭이 곧바로 실제 저장소 변경을 확정하면 jb-button을 사용함.
rename의 추가는 임시 행 생성만 수행하므로 jb-button-ghost를 사용함.
rename의 저장, 삭제, Fetch, Process, 초기화는 실제 저장소나 파일 상태를 변경하므로 jb-button을 사용함.
7.2 크기 기준
버튼 크기는 작고 촘촘한 운영 화면 기준을 유지함.
특정 화면만 더 크거나 더 두꺼운 버튼을 만들지 않음.
7.3 액션 묶음 정렬
같은 액션 영역 안에서는 버튼 이름보다 동작 성격을 기준으로 삭제, 조회, 저장 순서로 배치함.
삭제 계열은 선택 삭제, 현재 항목 삭제처럼 저장소에서 데이터를 제거하는 액션을 의미함.
조회 계열은 새로고침, 재조회, 선택 항목 로드, 다운로드처럼 저장소 상태를 변경하지 않는 액션을 의미함.
저장 계열은 저장, 일괄 저장, 적용처럼 생성 또는 수정 결과를 저장소에 확정하는 액션을 의미함.
삭제 계열과 조회 계열이 같은 버튼 영역에 있으면 jb-action-divider로 시각적 칸막이를 둘 수 있음.
portal-menus, codes, metas, users 같은 hub 관리자 화면은 이 액션 묶음 정렬을 기본으로 사용함.
7.4 모바일 하단 고정 액션바
모바일 전용 편집 화면의 주요 액션은 가능하면 하단 고정 액션바에 모아 배치함.
jb-mobile-bottom-bar를 사용하는 모바일 페이지는 본문 스크롤의 마지막 콘텐츠가 액션바에 가려지지 않도록 --jb-mobile-bottom-bar-clearance 하단 여백을 적용해야 함. 이 값은 기본 액션바 높이와 safe-area-inset-bottom을 함께 고려하는 shared-ui 공통 CSS 변수임.
일반 모바일 화면은 jb-mobile-page를 사용하며, 해당 클래스는 공통 하단 여백을 기본 적용함. 기존 화면 구조 때문에 jb-mobile-page의 flex column 간격을 사용할 수 없는 경우에는 jb-mobile-bottom-bar-page를 사용해 같은 폭·좌우 여백·하단 안전 여백 계약만 적용함.
페이지별 pb-* 유틸리티, 인라인 스타일, 앱 전용 CSS로 하단 고정 액션바 여백을 중복 정의하거나 공통 여백을 취소하지 않음.
하단 고정 액션바 안에서는 아이콘 버튼과 텍스트 버튼의 폭 정책을 분리함.
목록, 삭제처럼 아이콘 중심의 보조 액션 버튼은 내용이 필요한 최소 가로폭만 차지하도록 구성함.
추가, 저장처럼 텍스트 중심의 주요 액션 버튼은 아이콘 버튼을 제외한 남은 가로폭을 1/n 방식으로 균등 분할함.
하단 고정 액션바 안에서도 버튼 색상 규칙은 동일하게 유지함.
따라서 목록 이동, 추가 패널 열기, 단순 UI 전환 버튼은 jb-button-ghost를 사용하고, 저장, 삭제처럼 즉시 CUD를 일으키는 버튼은 jb-button을 사용함.
삭제가 하단 고정바에 배치되더라도 항상 큰 텍스트 버튼일 필요는 없으며, 사용 빈도가 낮으면 쓰레기통 같은 최소 아이콘 버튼으로 축약할 수 있음.
러닝 실행 화면처럼 타이머 조작 자체가 주요 액션인 화면은 모든 하단 액션을 아이콘 버튼으로 구성할 수 있음.
아이콘 전용 하단 조작 바는 버튼을 하나의 조작 그룹처럼 붙여 배치하고, 목록 이동 같은 보조 버튼도 같은 그룹 안에서 분리되어 보이지 않도록 함.
시작, 일시정지처럼 아이콘 의미가 명확한 실행 조작은 텍스트 라벨을 화면에 함께 표시하지 않고 aria-label로 접근성 이름을 제공함.
처음, 이전, 다음, 마지막 같은 순차 조작은 실행 위치를 직접 바꾸는 버튼으로 취급하며, 저장소 변경을 즉시 확정하지 않으면 jb-button-ghost를 사용함.
7.4.1 모바일 카드 목록 선택
하나의 모바일 화면에서 상단 등록·수정 폼과 하단 카드 목록을 함께 제공하는 경우, 카드 선택은 현재 폼의 수정 대상을 바꾸는 표준 방식임.
선택 카드는 화면 전용 색상·inline style·상태 텍스트를 추가하지 않고 shared-ui의 jb-mobile-card--selected를 사용함. 이 클래스는 기본 카드보다 명확한 청색 계열 테두리와 내부 강조로 선택 상태를 표시함.
카드의 선택 영역은 버튼으로 구성하고 aria-pressed로 선택 상태를 제공함. 카드 내부의 삭제처럼 별도 동작은 독립 보조 버튼으로 구성함.
신규 입력 전환은 IconPlus 같은 최소 폭 아이콘 버튼으로 제공할 수 있음. 이 동작은 선택 상태와 폼 값을 신규 기본값으로 초기화할 뿐 저장소를 변경하지 않으므로 jb-button-ghost를 사용함.
폼 제목·상태 칩은 신규 등록과 기존 데이터 수정을 구분할 수 있어야 하나, 선택 카드 자체에는 중복 상태 텍스트를 표시하지 않음.
7.4.2 모바일 카드 상태 배지
모바일 카드 목록의 활성·비활성 같은 운영 상태는 Message 앱의 상태 배지를 표준으로 사용함.
상태 배지는 카드 제목 영역의 우측에 배치하고, 상태 변경 여부와 관계없이 동일한 크기·간격을 유지함.
공통 레이아웃 클래스는 shrink-0 whitespace-nowrap rounded-full border px-2.5 py-1 text-[0.72rem] 조합을 사용함. shrink-0과 whitespace-nowrap는 좁은 화면에서 활성, 비활성 텍스트가 두 줄로 분리되는 것을 방지하는 필수 제약임.
활성 상태는 border-emerald-400/20 bg-emerald-400/10 text-emerald-100, 비활성 상태는 border-red-500/20 bg-red-500/10 text-red-100 조합을 사용함.
카드 제목·설명 컨테이너에는 min-w-0을 적용해 제목이 상태 배지를 밀어내지 않도록 함. 상태를 카드 본문에 별도 텍스트로 중복 표시하지 않음.
상태 변경 입력은 모바일에서 네이티브 체크박스 대신 jb-select 외형과 BottomOptionPicker 선택 시트 조합을 우선 사용함. 이 규칙은 Message 일정·반복 설정과 Raid 공대 구인 프로필처럼 모바일 카드 목록으로 상태를 보여 주는 화면에 적용함.
7.5 모바일 그룹 액션 정렬
모바일 편집 화면의 카드 내부 보조 액션은 기본적으로 우측 정렬을 사용함.
직전 기록, 추가, 삭제처럼 현재 카드 문맥에만 적용되는 보조 액션은 좌측 정렬보다 우측 묶음 배치를 우선함.
모바일 바텀시트나 모달 헤더에 배치되는 닫기 같은 짧은 텍스트 버튼은 줄바꿈되지 않도록 whitespace-nowrap 계열 조합과 충분한 가로 패딩을 사용함.
모바일 시트 헤더의 버튼은 제목/설명 영역과 나란히 배치되더라도 버튼 자체가 줄어들어 두 줄로 표시되지 않아야 함.
7.6 모바일 하단 선택 시트
모바일 폭에서 탭, 필터, 보기 전환 옵션이 한 줄 안에 안정적으로 표시되지 않거나 항목 수가 늘어날 수 있으면 인라인 탭을 유지하지 않고 현재 선택값 버튼과 하단 선택 시트 조합을 우선 검토함.
PC 화면에서 인라인 탭이 정보 밀도와 발견성을 유지하는 경우에도 모바일 화면은 같은 옵션 집합을 BottomOptionPicker 기반 하단 선택 시트로 표시할 수 있음.
모바일 전용 화면에서 루틴 모드, 운동 부위, 러닝 단계 유형, 안내 방식처럼 선택 후보를 고르는 UI는 네이티브 <select>보다 jb-select 외형의 선택 버튼과 BottomOptionPicker 하단 선택 시트 조합을 우선 사용함.
jb-select 외형 선택 버튼은 공통 select chevron 배경을 그대로 사용하며, 버튼 내부에 ⌄, ▾, ▼ 같은 별도 텍스트 화살표를 추가하지 않음.
하단 선택 시트는 배경 컨텐츠와 겹쳐도 옵션 텍스트를 안정적으로 읽을 수 있도록 불투명한 다크 패널, 얇은 경계선, 저대비 옵션 카드 톤을 사용함.
하단 선택 시트의 선택 후보는 내부 scroll container를 사용하며, 현재 선택값이 열림 시점에 목록 중앙 근처로 정렬되어야 함.
숫자 선택처럼 값 타입이 특수한 경우에도 시트 외형과 스크롤 동작은 공통 선택 시트 계약을 재사용함.
숫자 선택 하단 시트는 옵션 텍스트를 중앙 정렬함.
텍스트형 보기 전환이나 설명이 있는 옵션 목록은 좌측 정렬을 기본으로 함.
7.7 공통 segmented tab
탭, 필터, 보기 전환, 페이지 크기처럼 같은 옵션 집합 중 하나를 선택하는 UI는 shared-ui의 SegmentedTabs를 우선 사용함.
표준 segmented tab 외형은 대시보드 PC 탭과 같은 낮은 대비, 얇은 경계선, rounded-jb-sm 반경을 사용함.
과하게 둥근 pill 형태는 표준 탭 외형으로 사용하지 않음.
APP/SERVICE 같은 2개 탭은 fill 폭 정책으로 컨테이너 폭을 균등 분할할 수 있음.
운동 부위처럼 항목 수가 늘어날 수 있는 필터 탭은 scrollable 폭 정책으로 가로 스크롤을 허용할 수 있음.
대시보드처럼 PC에서는 인라인 탭이 적합하지만 모바일에서는 화면 폭을 넘는 경우, PC 인라인 탭은 SegmentedTabs를 사용하고 모바일 선택은 BottomOptionPicker 하단 선택 시트로 분리함.
각 항목의 아이콘, 이모지, 보조 표식은 화면별 옵션 데이터의 책임이며 공통 탭 표준의 필수 요소가 아님.
8. 드래그 정렬 규칙
8.1 공통 드래그 핸들
드래그 정렬 UI는 shared-ui의 공통 SortableDragHandle과 공통 sortable 옵션을 우선 사용함.
드래그 핸들 외형은 2열 3행의 6점 핸들 형태를 기준으로 함.
화면별로 GripVertical 같은 별도 아이콘을 직접 사용하지 않음.
드래그 핸들은 정렬 가능한 항목의 앞쪽 또는 카드 헤더 액션 영역처럼 사용자가 자연스럽게 잡을 수 있는 위치에 배치함.
드래그 핸들에는 항목 또는 카드 이름을 포함한 접근성 라벨을 부여함.
8.2 드래그 감도와 잔상
Sortable 기반 정렬은 공통 옵션의 1초 지연 규칙을 사용함.
클릭 즉시 드래그 모드로 진입하지 않으며, 사용자가 핸들을 길게 누른 뒤 이동할 때 정렬이 시작됨.
드래그 중 선택 상태, ghost, fallback 잔상은 shared-ui 공통 클래스가 담당함.
화면별로 delay, ghost class, chosen class, fallback class를 임의로 다르게 지정하지 않음.
포털 카드, 대시보드 큰 카드, 대시보드 투자 항목처럼 같은 상호작용 성격의 정렬 UI는 같은 공통 옵션을 사용함.
9. 폼 규칙
9.1 입력 필드
운영 화면 폼은 컴팩트하게 구성함.
메타 정보는 가능하면 한 줄에 최대한 많이 배치함.
식별자와 짧은 보조 정보는 read-only input 구조를 허용함.
두 컬럼 인라인 필드에서 좌측 텍스트 입력과 우측 active 체크박스를 같이 배치할 때는 라벨 행과 입력 행이 각각 나란히 맞도록 공통 그리드를 사용함.
이 경우 기본 비율은 8:2를 우선 사용함.
9.2 설명 문구
별도 설명 문구를 늘리기보다 입력 필드 구조 안에서 맥락을 해결함.
장식성 헤더 카피를 줄이고 작업 정보 위주로 구성함.
모바일 편집 화면에서는 제목 바로 아래 설명 문구를 기본 전제로 두지 않음.
설명이 없어도 문맥이 충분하면 보조 카피 영역을 생략함.
10. 토스트와 메시지 규칙
10.1 메시지 표시 방식
페이지 본문을 밀어내는 인라인 메시지 영역을 두지 않음.
저장, 실패, 안내는 공통 토스트로 표기함.
토스트는 화면 하단 중앙 기준을 사용함.
10.2 의미 기준 스타일
단순 안내, 조회, UI 상태 메시지는 중립 스타일 토스트를 사용함.
실제 CUD 성공 메시지는 강조 스타일 토스트를 사용함.
오류 메시지는 에러 전용 스타일을 사용함.
10.3 문체
문장형 사용자 메시지는 하십시오체를 사용함.
문장형 메시지는 마침표를 붙임.
버튼, 메뉴, 라벨 같은 단어형 텍스트는 문장형으로 억지 변환하지 않음.
10.4 장시간 작업 로딩 표시
외부 연동이나 파일 작업처럼 사용자가 즉시 결과를 기다려야 하는 액션은 공통 로딩 오버레이를 사용함.
로딩 오버레이는 플랫한 다크 배경과 단색 패널 구조를 사용함.
화면별 임의 로딩 모달을 만들지 않고 공통 로딩 컴포넌트를 재사용함.
11. 타이포그래피 기준
기본 폰트는 Pretendard Variable을 사용함.
모든 화면 텍스트는 서버에서 제공되는 PretendardVariable.woff2 자산을 기준으로 렌더링함.
Tailwind font-mono 유틸리티와 shared-ui의 mono 토큰도 Pretendard를 가리킴.
필드 라벨, eyebrow, 통계 라벨, 테이블 헤더, JSON/코드/AI 로그 상세 텍스트도 별도 모노 폰트를 사용하지 않음.
폰트 선언은 shared-ui의 공통 base CSS에서 중앙화하며, 앱별 CSS나 개별 클래스에서 font-family를 중복 선언하지 않음.
입력, 버튼, 테이블, Tabulator 내부 UI는 공통 루트 폰트를 상속함.
사용자 로컬에 설치된 Pretendard, 유사 sans 폰트, 브라우저 기본 monospace를 우선 사용하지 않음.
전체 폰트 크기는 운영 화면에서 많은 정보를 한 번에 보여줄 수 있도록 작게 유지함.
Tabulator 내부 폰트도 같은 공통 스케일을 따름.
기본 텍스트는 밝은 계열을 사용하고, 보조 텍스트만 muted 색상을 사용함.
관리 화면의 페이지 제목은 h2 수준의 공통 클래스 기반 스타일을 사용함.
페이지 제목은 화면마다 임의의 크기로 조정하지 않고 공통 클래스 기준으로 통일함.
장식용 eyebrow, 과한 소개 문구, 불필요한 보조 카피는 작업형 화면에서 기본적으로 생략함.
날짜와 시간 텍스트도 locale 기본 문자열에 의존하지 않고 공통 포맷 표준을 따름.
JSON, 코드, AI 로그 상세 텍스트 뷰어도 별도 모노 폰트를 강제하지 않고 기본 Pretendard 기준을 사용함.
11.1 입력 필드와 선택 필드 구분
텍스트 입력은 jb-input을 사용함.
선택 입력은 jb-select 또는 Tabulator list editor 공통 클래스를 사용함.
jb-select는 네이티브 <select>뿐 아니라 모바일 하단 선택 시트를 여는 선택 버튼 외형에도 사용할 수 있음.
플랫한 다크 톤 카드/패널 내부에서 input/select를 동일 톤으로 처리해야 하는 화면은 jb-dark-field를 사용할 수 있음.
jb-input에는 select chevron, 우측 구분선, 드롭다운 표시 배경을 적용하지 않음.
jb-select에만 chevron과 드롭다운 성격의 시각 표시를 적용함.
jb-select를 버튼에 사용할 때도 공통 chevron 배경과 우측 구분선이 이미 포함되어 있으므로 화면 컴포넌트가 별도 화살표 아이콘이나 텍스트를 중복 렌더링하지 않음.
jb-dark-field가 select에 사용될 때 option과 optgroup은 다크 배경과 밝은 글자를 유지해야 함.
입력창, select, 버튼의 텍스트 selection 색상은 shared-ui 공통 규칙을 사용함.
12. workout 모바일 전용 패턴
운동 기록, 세션 상세, 운동 계획 화면은 모바일 전용 360px 고정 폭 기준을 사용함.
세션 상세의 숫자 입력은 직접 텍스트 입력보다 바텀시트 숫자 선택 패턴을 우선함.
세션 상세의 직전 기록 기능은 즉시 반영하지 않고 직전 기록 모달 조회 -> 가져오기 -> 확인창 승인 -> 세트 덮어쓰기 흐름을 기본으로 함.
직전 기록 가져오기 확인창에서 취소하면 모달 상태와 현재 세트 입력값을 유지함.
직전 기록 모달은 세트 단위로 중량, 반복, 상태, 그리고 상태에 따라 RPE 또는 FP 한 가지만 보여주는 패턴을 사용함.
세트 입력 그리드는 첫 번째 세트 번호 칼럼 폭을 줄이고, 상태 칼럼이 줄바꿈되지 않도록 같은 컬럼 템플릿을 헤더와 행에 함께 적용함.
세트 입력 그리드의 중량 헤더는 단위를 포함한 중량(kg) 표기를 사용함.
세트 입력 그리드 헤더는 단위 표기 대소문자를 보존해야 하므로 공통 스타일에서 대문자 변환을 강제하지 않음.
숫자 선택 바텀시트와 직전 기록 모달은 호출 시점의 문서 스크롤 위치를 보존하고 닫힌 뒤 같은 위치를 유지해야 함.
하단 고정 액션바는 모바일 viewport 하단 기준으로 고정되며, SPA 전환 wrapper나 모바일 컨테이너 때문에 문서 스크롤 끝에 배치되면 안 됨. 액션바를 사용하는 본문은 jb-mobile-page 또는 jb-mobile-bottom-bar-page의 공통 하단 안전 여백을 사용함.
운동 기록 목록 카드의 날짜는 YYYY-MM-DD (화)처럼 날짜 텍스트와 같은 위계로 요일을 함께 표시함.
체중 관리 목록 카드도 날짜를 YYYY-MM-DD (화) 형식으로 표시하고 요일을 별도 뱃지나 색상으로 분리하지 않음.
체중 관리 카드의 선택 투약 정보는 약명 · 용량 · 부위 통합 뱃지 하나로 표시하며, 부위만 독립 뱃지로 중복하지 않음.
space-y-*로 세로 간격을 제어하는 모바일 폼의 직접 자식 입력 그룹은 블록 요소여야 함. textarea 라벨을 인라인 요소로 두어 앞 입력 행과의 margin이 무시되지 않게 함.
체중 관리 상세의 목록 이동과 삭제는 다른 workout 상세와 같이 최소 폭 아이콘 버튼으로 구성하고 저장 버튼은 아이콘 버튼을 제외한 남은 폭을 사용함.
workout 모바일 화면의 루틴 모드, 운동 부위, 러닝 단계 유형, 안내 방식 선택은 jb-select 외형의 선택 버튼과 BottomOptionPicker 하단 선택 시트를 사용함.
운동 계획의 kg 표기는 정수와 불필요한 trailing zero를 제거하고 실제 소수부가 있을 때만 소수점을 유지함.
13. AI 구현 체크리스트
화면 인터랙션 소스가 .ts 또는 .tsx 기준인가
TypeScript strict 기준을 깨는 임시 완화 설정을 추가하지 않았는가
any를 습관적으로 쓰지 않고 API 응답과 행 타입을 명시했는가
새 화면을 만들 때 화면 전용 CSS 파일을 만들지 않았는가
특별한 사유 없이 모듈 전용 CSS 파일을 만들지 않았는가
인라인 스타일과 <style> 태그를 사용하지 않았는가
새 모양이 필요하면 shared-ui에 공통 클래스로 올렸는가
바깥 래퍼가 실제 역할 없이 중복되지 않았는가
페이지 제목이 공통 h2 톤을 따르는가
페이지 제목과 바로 아래 작업 영역 사이에 공통 간격이 확보되었는가
그리드 헤더가 좌측 상태, 우측 버튼 구조를 따르는가
좌우 분할 작업형 화면에서 기본 6:4 비율과 액션 책임 분리가 지켜졌는가
그리드가 인라인 편집형인지, 누적형 조회인지, 비편집형 마스터 목록인지 먼저 분류한 뒤 그에 맞는 로딩 정책을 적용했는가
인라인 편집형 마스터/기준정보 그리드를 점진적 스크롤로 구현하지 않았는가
누적형 로그/이력 화면을 전체 로딩 기본값으로 구현하지 않았는가
비편집형 마스터 목록 화면에서 점진적 스크롤 로딩을 검토했는가
active 같은 불린 컬럼이 체크박스형 tickCross 표현을 따르는가
active 인라인 필드가 공통 8:2 정렬 기준을 따르는가
Tabulator 셀 정렬을 위해 .tabulator-cell 자체 레이아웃 모델을 바꾸지 않았는가
버튼 스타일이 실제 CUD 여부 기준으로 구분되는가
같은 액션 영역의 버튼 순서가 삭제, 조회, 저장 성격 순서를 따르는가
삭제 계열과 조회 계열 사이에 필요한 경우 jb-action-divider를 사용했는가
Sortable 기반 드래그 정렬 UI가 공통 SortableDragHandle과 공통 sortable 옵션을 사용하는가
사용자 노출 문구가 공통 표현 규칙과 현재 화면 톤을 따르는가
텍스트만을 위해 신규 DB 마스터데이터를 만들지 않았는가
공통코드/메타 값이 필요한 경우 등록된 마스터데이터 키를 먼저 확인했는가
문장형 메시지가 하십시오체와 마침표 규칙을 지키는가
페이지 전체 스크롤 없이 FHD 기준으로 맞추었는가
상단 요약/메타 영역이 커질 때 하단 그리드가 그만큼 줄어드는가
스크롤이 페이지 바깥이 아니라 그리드나 내부 패널 안에서만 발생하는가
Tabulator 컬럼 폭을 먼저 줄여 가로 스크롤을 최소화했는가
날짜가 YYYY-MM-DD 또는 YYYY-MM-DD HH24:MI:SS 표준으로 표기되는가
공통 CSS 링크를 개별 템플릿에서 직접 선언하지 않고 shared-ui stylesheet 매크로를 사용하는가
disabled 버튼이 공통 회색 비활성 외형으로 렌더링되고 활성 강조 버튼처럼 보이지 않는가
14. 구현 금지사항
신규 화면 로직을 .js 파일로 작성
TypeScript strict 모드 비활성화
타입 선언 없이 any를 광범위하게 사용
화면별 CSS 파일 추가
앱별 CSS 파일 추가
특별한 사유 없는 모듈별 CSS 파일 추가
템플릿에 인라인 스타일 추가
같은 목적의 화면에서 제목 크기와 표기 방식을 임의로 다르게 구성
실익 없는 중복 wrapper div 추가
동일 의미의 active 상태를 화면마다 다른 시각 체계로 표현
같은 기능의 검색 UI를 화면 바깥과 그리드 안에 중복 배치
본문을 밀어내는 고정 메시지 바 사용
같은 의미의 사용자 노출 문구를 화면마다 임의로 다르게 작성
텍스트만을 위해 메시지 DB, 메시지 코드, 메시지 조회 유틸을 새로 추가
인라인 편집형 마스터/기준정보 그리드에 점진적 스크롤 로딩을 기본 적용
누적형 로그/이력 화면에 전체 로딩을 기본 적용
비편집형 마스터 목록 화면을 근거 없이 서버 페이징으로 먼저 고정
시각 효과를 위한 과도한 그림자, 색 강조, 넓은 여백 사용
선택 상태를 설명하지 않아도 되는 화면에 관성적으로 status chip을 추가
좌측 목록 액션과 우측 편집 액션의 책임을 한쪽 헤더에 뒤섞기
Tabulator 셀 정렬을 이유로 .tabulator-cell 자체를 flex로 변경
운영 화면에 페이지 전체 세로 스크롤이 생기도록 방치
날짜/시간을 locale 기본 문자열이나 화면별 임의 포맷으로 제각각 출력
공통 CSS와 vendor CSS 링크를 화면 템플릿마다 직접 선언
Sortable 기반 드래그 핸들 외형, delay, ghost, fallback 규칙을 화면별로 임의 재정의
같은 액션 영역에서 삭제, 조회, 저장 성격 버튼을 일관성 없이 섞어 배치
15. 참고 화면
대표 레퍼런스 화면은 apps/hub-api의 /codes 화면임.
단일 그리드형 화면 예시는 portal-menus 계열임.
좌측 선택 + 우측 작업형 화면 예시는 codes, metas, users임.
/codes, /metas, /users는 현재 공통 제목 톤, 얇은 wrapper 구조, Tabulator active 체크박스 표현을 공유하는 기준 화면임.
/metas, /users는 6:4 분할, 좌측 목록 액션과 우측 편집 액션의 책임 분리, 삭제/조회/저장 성격 순서, 인라인 active 8:2 정렬 패턴의 기준 화면임.
로딩 정책 관점에서 /codes 우측 상세 그리드는 인라인 편집형 전체 로딩 기준 화면으로 봄.
로딩 정책 관점에서 /users, /metas 좌측 목록은 비편집형 마스터 목록의 점진적 스크롤 로딩 기준 화면으로 확장할 수 있음.
로딩 정책 관점에서 /ai-logs는 누적형 데이터의 서버 페이징 기준 화면으로 봄.