본문으로 건너뛰기

콘솔 UX 표준

본 문서는 콘솔의 모든 화면에서 준수해야 하는 UX 표준과 이를 효율적으로 구현할 수 있는 컴포넌트 구성을 다룹니다. 대부분의 항목은 test/e2e/ux 테스트를 통해 자동으로 검증됩니다 (검증 대상: 페르소나 8개, 뷰포트 4개, 언어 2개, GPU가 없는 제어 플레인 환경).

모든 화면​

보장 항목구현 방법자동 검사 항목
브라우저 탭 이름에 화면명 표시<PageHeader title>이 useDocumentTitle 호출page.title.notPerScreen
문서 구조 내 단일 <h1> 태그 유지<PageHeader> 사용page.heading.noH1
<main>, <nav>, <header> 랜드마크 및 건너뛰기 링크 제공Layout 컴포넌트 적용page.landmark.*, page.skipLink.missing
<html lang> 속성이 선택된 언어와 일치frontend/src/i18n/index.tsi18n.htmlLangMismatch
모든 대화형 컨트롤에 시각적 포커스 링 제공frontend/src/styles/index.css의 :focus-visiblekeyboard.noFocusRing
텍스트와 배경 간 명암비가 WCAG AA 기준 충족테마 토큰 적용contrast.belowWcagAA
최소 뷰포트(390px) 환경에서 가로 스크롤 미발생.gs-shell, hideOnMobile 열 활용responsive.horizontalScroll
모바일 터치 영역 ≥ 44px, 텍스트 크기 ≥ 12px 유지frontend/src/styles/index.css 하단 모바일 오버라이드responsive.tapTargetTooSmall
prefers-reduced-motion 설정을 준수하는 애니메이션 적용frontend/src/styles/index.css—

최상위 하위 화면​

  • 브레드크럼(Breadcrumb) 및 이전 페이지 이동 경로 제공: <PageHeader crumbs={[...]}> 및 <BackLink>를 함께 구성합니다. 네비게이션 경로가 명시되지 않으면 사용자는 브라우저 뒤로 가기 버튼에만 의존하게 되며, 이 과정에서 작성 중이던 폼 데이터가 유실될 수 있습니다.
  • 제목에 구체적인 대상 이름 명시: 단순 화면 종류뿐만 아니라 식별 가능한 대상 명칭을 함께 표기합니다. (예: "부서 편집 — NLP팀")

목록 (Table)​

관련 기능은 frontend/src/components/Table.tsx 및 frontend/src/hooks/useTableState.ts에 구현되어 있으므로, 각 화면에서는 sort, dir, onSort 속성을 전달하여 적용합니다.

  • 정렬 가능한 열: 렌더링된 DOM 노드가 아닌 실제 데이터 원본 값을 기준으로 정렬합니다. 정렬 순서가 불필요한 열은 sortable: false로 설정합니다.
  • 검색, 실시간 일치 항목 수 표시 및 일괄 초기화: <TableToolbar> 컴포넌트를 활용합니다.
  • URL 기반 상태 관리: 검색어, 정렬 기준, 페이지 번호, 탭 상태 등을 쿼리 파라미터로 유지하여 북마크 생성 및 링크 공유가 가능하도록 하고, 상세 행 이동 후 뒤로 가기 시 기존 상태를 복원합니다.
  • 두 가지 빈 상태(Empty State) 구분:
    • 데이터가 존재하지 않는 경우: 첫 번째 항목 생성 버튼과 함께 <EmptyState> 표시
    • 필터링 결과가 없는 경우: 필터 조건 초기화 기능과 함께 <NoResults> 표시
    • (참고: 별도 안내가 없는 빈 테이블은 시스템 오류로 오인될 수 있습니다.)
  • 로딩 스켈레톤 UI: 데이터 로딩 시 빈 패널이나 단독 스피너 대신 <TableSkeleton>을 사용합니다.
  • 고정 헤더 및 페이지네이션: 15행 초과 시 **고정 헤더(Sticky Header)**를 적용하고, 25행 초과 시 페이지네이션을 적용합니다.
  • 다중 선택(행 선택): 일괄 처리가 가능한 액션이 존재하는 테이블에는 행 선택(Checkbox) 기능을 제공합니다.
  • 접근성을 위한 테이블 캡션: 단순 "table"이 아닌 테이블의 목적과 내용을 명확히 설명하는 캡션을 제공합니다.

폼 (Form)​

  • 명시적 라벨 사용: <Field> 컴포넌트가 <label for>, 필수 항목 표시, 힌트, 오류 메시지를 입력 컨트롤에 올바르게 연결하고 aria-describedby, aria-invalid, aria-required 속성을 설정합니다. (플레이스홀더는 입력 시 사라지므로 라벨 대용으로 사용할 수 없습니다.)
  • <form> 태그 활용: 폼을 <form> 요소로 감싸 Enter 키로 제출할 수 있도록 합니다. 이는 키보드 접근성을 높이는 가장 기본적인 방법입니다.
  • Blur 시점의 유효성 검증: 제출 시점이 아닌 Blur(포커스 해제) 시점에 입력값을 검증합니다. 형식에 맞지 않는 이메일이나 잘못된 수량은 입력 직후 명시적으로 안내합니다.
  • 적절한 Input 속성 지정: 가능한 모든 필드에 autocomplete, inputmode, min, max, step 속성을 선언합니다. 이를 통해 비밀번호 관리자 연동을 지원하고 모바일 기기에서 최적화된 키보드 레이아웃을 제공합니다.
  • 비활성화된 주 버튼의 사유 안내: 주 버튼이 비활성화된 경우 <DisabledReason>을 통해 사유를 안내합니다. 설명 없이 비활성화된 버튼은 사용자가 원인을 파악하기 어렵게 만듭니다.
  • 저장되지 않은 변경 사항 보호: useUnsavedGuard를 적용하여 앱 내 이동 및 탭 닫기 시 작업 내용 유실을 방지합니다.

숫자 입력​

모든 숫자 입력 필드는 min, max, step, inputmode 속성을 명시적으로 선언하고, 포커스를 벗어날 때(Blur) 입력값을 유효 범위 내로 보정(Clamp) 처리합니다. HTML 속성은 브라우저 기본 스피너 및 검증을 제어하지만 직접 타이핑하거나 붙여넣는 유효 범위 밖의 입력을 완벽히 차단하지는 못합니다. 입력값 보정 처리가 누락되면 음수 수량이나 잘못된 자릿수의 값이 API 요청으로 전달되어 422(Unprocessable Entity) 에러가 발생할 수 있습니다.

파괴적 작업 (Destructive Actions)​

window.confirm 대신 useConfirm()을 사용합니다. 기본 브라우저 대화 상자는 손실 대상 정보를 명확히 보여줄 수 없고, i18n 번역이 불가능하며, 브라우저 탭 전체를 차단하는 문제가 있습니다.

  • 명확한 대상 이름 명시: 모호한 질문("정말 진행하시겠습니까?") 대신 구체적인 대상을 명시합니다. (예: "vit-base-ft를 종료할까요?")
  • 작업 결과 명시: 정산될 크레딧, 삭제되는 볼륨 데이터, 고아 상태가 되는 세션 등 수행 결과의 영향을 명확히 안내합니다.
  • 불가역적 작업 시 텍스트 입력 검증: 조직·클러스터·공유 데이터셋 삭제, 다수의 세션 동시 종료 등 복구가 불가능한 작업은 대상의 이름을 직접 입력하도록 요구합니다.
  • 포커스 트랩 및 키보드 제어: ESC 키 및 오버레이 클릭은 취소로 처리합니다. 대화 상자가 열려 있는 동안 포커스는 모달 내부에 격리(Focus Trap)되며, 닫힐 때 대화 상자를 호출했던 컨트롤로 포커스가 복원됩니다.
  • 복구 가능한 작업에 대한 실행 취소(Undo) 지원: 예컨대 접근 권한 해제 등의 작업은 즉시 적용 후 실행 취소 버튼이 포함된 토스트 알림을 제공합니다 (pushToast(kind, message, { label, run })). 잦은 작업에서 불필요한 확인 모달을 줄일 수 있습니다. 명시적 텍스트 입력 확인은 되돌릴 수 없는 작업에만 제한적으로 적용합니다.

서버 오류 처리​

요청 거절 및 저장 실패 오류는 토스트 알림뿐만 아니라 폼 상단에도 명시적으로 표시합니다. 5초 후 자동으로 사라지거나 원인 필드를 알 수 없는 메시지는 적절한 오류 안내가 아닙니다. 사용자가 다음 시도를 할 때까지 실패한 액션 요소 근처에 오류 메시지를 유지해야 합니다.

실시간 데이터​

  • 최종 조회 시각 표시 및 수동 새로고침 제공: <PageHeader updatedAt onRefresh>를 구성합니다. 최종 조회 시각이 없으면 데이터가 오래된 것인지 작업이 정지된 것인지 구분할 수 없습니다.
  • 상대 시간 표시 및 호버 시 절대 시간 제공: <Timestamp> 컴포넌트를 사용합니다. "6시간 전"과 같은 상대 시간을 기본 표기하고, datetime 속성 및 툴팁을 통해 정확한 절대 시간을 로그와 대조할 수 있도록 합니다. 상대 시간 텍스트는 타이머를 통해 주기적으로 재렌더링하여 장시간 열려 있는 탭에서도 정확한 시간을 유지합니다.

식별자 및 복사 기능​

세션, 클러스터, 디바이스, 지갑 등의 식별자(ID)는 지원 티켓 작성, kubectl 명령어 실행, 메시지 전달 시 자주 참조됩니다. 모든 식별자 옆에는 피드백(시각적 복사 완료 표시)이 명확한 <CopyButton>을 제공합니다. 피드백이 없는 단순 동작은 중복 클릭 및 작업 불확실성을 유발합니다.

다국어 지원 (i18n)​

  • 영어 번들을 기본(Fallback) 원본으로 관리하며, 한국어 번들은 완전하게 번역된 형태로 제공합니다. 번들 간 i18n 키 일치 여부를 검증합니다.
  • 문장을 부분 조각으로 조합하여 구성하지 않습니다. 동적 값은 플레이스홀더를 사용하고, 문장 내 마크업이 포함된 경우 <Trans> 컴포넌트를 활용하여 언어별 어순 차이에 대응할 수 있도록 합니다.
  • 언어 변경 컨트롤은 로그아웃 화면 및 오류 페이지를 포함하여 시스템 전체 모든 화면에서 접근 가능해야 합니다.
  • 사용자가 직접 입력한 이름(관리자 정의 명칭)은 언어 변경과 무관하게 입력된 원본 그대로 유지합니다.

한국어 표현 가이드라인​

한국어 i18n 번들은 문자열 유형별로 통일된 문체 기준을 적용하여 어조의 일관성을 유지합니다.

  • 명사구 형태 (동사 어미 생략): 화면 제목 및 부제, 테이블 헤더, 필드 라벨, 탭, 버튼, 상태 배지, 필터 명칭에 적용합니다. (예: 노드 비우기, 예약, 차단 / 오프라인 노드)
  • 완성형 문장 형태: 발생한 사건이나 수행할 작업을 설명하는 위치에 사용합니다 (대화 상자 본문, 오류 메시지, 토스트 알림, 빈 상태 안내, 필드 힌트). 문장은 ~합니다 또는 ~하세요 어미로 작성하며, ~해요나 ~함은 사용하지 않습니다. 확인 대화 상자의 제목은 질문형으로 작성합니다 (예: {{name}}을(를) 삭제할까요?).
  • 고유명사 및 단위 표기: 고유 제품명 및 기술 단위는 표준 영문 표기를 유지합니다 (예: GPU, VRAM, CUDA, GiB, DCGM, kubeconfig, CSV). 주요 용어는 시스템 전체에서 동일한 한국어 용어로 통일합니다.
영어 (코드 / Kubernetes)콘솔 표준 한국어 표기
cordon / uncordon차단 / 차단 해제
drain비우기
hold (크레딧 예약)예약
operator오퍼레이터
super_admin시스템 관리자
org_admin조직 관리자
group_admin / member / guest부서 관리자 / 구성원 / 게스트
group (project)부서
organization조직
offering / preset / policy오퍼링 / 프리셋 / 정책
quota쿼터 (스토리지) · 한도 (정책)
session / volume / wallet / credit세션 / 볼륨 / 지갑 / 크레딧
pause / terminate / paused일시정지 / 종료 / 일시정지됨
exclusive / fractional전용 / 공유

화면 신규 추가 절차​

PageHeader와 Table/Field 컴포넌트를 조합하여 기본 구조를 작성하면 대부분의 UX 표준 규격을 자동으로 충족할 수 있습니다. 작성 후에는 해당 화면을 사용하는 페르소나를 지정하여 UX 감사 스크립트를 실행합니다.

UX_PERSONA=researcher node test/e2e/ux/audit.js