본문으로 건너뛰기

JunkBox 공통 UI 가이드

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 공통 다크 톤 규칙을 그대로 따르는 것을 우선함.

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 하고 필요한 블록만 채움.
  • 로그인, 체스 샌드박스처럼 공통 앱 셸을 직접 상속하지 않는 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-screenjb-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 모바일 하단 고정 액션바

  • 모바일 전용 편집 화면의 주요 액션은 가능하면 하단 고정 액션바에 모아 배치함.
  • 하단 고정 액션바 안에서는 아이콘 버튼과 텍스트 버튼의 폭 정책을 분리함.
  • 목록, 삭제처럼 아이콘 중심의 보조 액션 버튼은 내용이 필요한 최소 가로폭만 차지하도록 구성함.
  • 추가, 저장처럼 텍스트 중심의 주요 액션 버튼은 아이콘 버튼을 제외한 남은 가로폭을 1/n 방식으로 균등 분할함.
  • 하단 고정 액션바 안에서도 버튼 색상 규칙은 동일하게 유지함.
  • 따라서 목록 이동, 추가 패널 열기, 단순 UI 전환 버튼은 jb-button-ghost를 사용하고, 저장, 삭제처럼 즉시 CUD를 일으키는 버튼은 jb-button을 사용함.
  • 삭제가 하단 고정바에 배치되더라도 항상 큰 텍스트 버튼일 필요는 없으며, 사용 빈도가 낮으면 쓰레기통 같은 최소 아이콘 버튼으로 축약할 수 있음.
  • 러닝 실행 화면처럼 타이머 조작 자체가 주요 액션인 화면은 모든 하단 액션을 아이콘 버튼으로 구성할 수 있음.
  • 아이콘 전용 하단 조작 바는 버튼을 하나의 조작 그룹처럼 붙여 배치하고, 목록 이동 같은 보조 버튼도 같은 그룹 안에서 분리되어 보이지 않도록 함.
  • 시작, 일시정지처럼 아이콘 의미가 명확한 실행 조작은 텍스트 라벨을 화면에 함께 표시하지 않고 aria-label로 접근성 이름을 제공함.
  • 처음, 이전, 다음, 마지막 같은 순차 조작은 실행 위치를 직접 바꾸는 버튼으로 취급하며, 저장소 변경을 즉시 확정하지 않으면 jb-button-ghost를 사용함.

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-uiSegmentedTabs를 우선 사용함.
  • 표준 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나 모바일 컨테이너 때문에 문서 스크롤 끝에 배치되면 안 됨.
  • 운동 기록 목록 카드의 날짜는 YYYY-MM-DD (화)처럼 날짜 텍스트와 같은 위계로 요일을 함께 표시함.
  • 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 매크로를 사용하는가

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, /users6:4 분할, 좌측 목록 액션과 우측 편집 액션의 책임 분리, 삭제/조회/저장 성격 순서, 인라인 active 8:2 정렬 패턴의 기준 화면임.
  • 로딩 정책 관점에서 /codes 우측 상세 그리드는 인라인 편집형 전체 로딩 기준 화면으로 봄.
  • 로딩 정책 관점에서 /users, /metas 좌측 목록은 비편집형 마스터 목록의 점진적 스크롤 로딩 기준 화면으로 확장할 수 있음.
  • 로딩 정책 관점에서 /ai-logs는 누적형 데이터의 서버 페이징 기준 화면으로 봄.