RustDesk 모바일 Modifier 키 UI 최적화 v4

2026-05-23 (v4 갱신) 대상: 갤럭시 Fold 6 / 6.x" (Android, 한 손 사용) 옵션 7개 비교
질문 — RustDesk 모바일 원격 제어에서 modifier 키(Ctrl·Alt·Shift·Cmd·Win + 방향키·펑션키) UI 패턴 비교. 평가축: 한 손 조작성·키 조합 속도·화면 가림·발견성·접근성 (총 100%). v4 새 옵션: 키보드 바로 위 1줄 + 모드 토글(텍스트 ↔ 명령) — Galaxy Fold 6 같은 큰 폴더블 화면에서 화면 위쪽 가림 문제 해결.

분석 대상 — 7개 UI 패턴

현재 구현 + 모바일 원격 제어 대안 + TeamViewer 방식 + 키보드 위 모드 토글(신규).

11줄 평시 + 3줄 펼침 (현재 RustDesk)

평시 1줄 (Ctrl·Alt·Shift·Tab 등) 항상 표시. 펼치기 버튼 탭 시 3줄 (전체) 로 확장. 위치: 화면 위쪽. 구현: flutter/lib/mobile/pages/remote_page.dart:904-1024.

2FAB + Radial menu

화면 모서리 단일 FAB(56dp). 탭하면 부채꼴 펼침. Material Speed Dial.

3Edge swipe drawer

화면 좌/우 가장자리 swipe → modifier drawer 슬라이드 인. VNC·RDP 패턴.

4Bottom sheet + pinch

핀치 또는 swipe-up → bottom sheet 에 modifier 그리드 노출.

5Pie menu / Quick wheel

Long-press → 8방향 파이 메뉴. 게임 패턴.

6TeamViewer 방식

상단 toolbar + Ctrl+Alt+Del 콤보 + 사전 정의 콤보 메뉴 + Direct keyboard mode 토글.

7키보드 위 1줄 + 모드 토글 ★ 신규

modifier 한 줄을 가상 키보드 바로 위에 배치 (엄지 도달 최적). 모드 토글 버튼 하나로 텍스트 입력 모드명령(편의 버튼) 모드 전환. Galaxy Fold 6 같은 큰 폴더블에서 화면 위쪽 가림 문제 근본 해결.

평가 기준 + 가중치

갤럭시 Fold 6 / 6.x" 한 손 사용 컨텍스트. 합계 100%.

기준가중치근거
한 손 조작성25%Fold 6 펼친 화면(7.6") 한 손 조작 시 위쪽 도달 매우 어려움. 키보드 영역에 가까운 위치가 절대 우위.
키 조합 입력 속도25%Ctrl+Alt+Del·Win+E·Cmd+Tab 같은 multi-modifier 조합 속도.
화면 가림 면적20%원격 화면 가림. 키보드가 어차피 표시되면 그 위 1줄은 추가 가림 ~40dp 로 한정.
발견성15%설명 없이 사용자가 modifier 호출 방법을 찾을 수 있는가.
접근성 (a11y)15%큰 터치 타깃, 시각 피드백, 정밀 제스처 회피.

평가 매트릭스 (v4)

각 기준 10점 만점, 가중 점수 = 원점수 × 가중치 / 10. 총점 10점 만점.

순위 옵션 한 손 (25%) 속도 (25%) 가림 (20%) 발견성 (15%) 접근성 (15%) 총점
1 ⑦ 키보드 위 1줄 + 모드 토글 ★ 신규 10 7 6 8 9 8.00
2 ① 1줄 평시 + 3줄 펼침 (현재 RustDesk) 7 8 4 9 9 7.25
3 ⑥ TeamViewer 방식 6 9 4 9 8 7.10
4 ⑤ Pie menu / Quick wheel 9 7 8 5 5 7.10
5 ② FAB + Radial menu 9 5 8 6 7 7.05
6 ③ Edge swipe drawer 8 6 9 4 5 6.65
7 ④ Bottom sheet + pinch 8 5 7 5 6 6.30

v4: ⑦ 추가, 8.00 으로 신규 1위. 키보드 위 배치 가 한 손 조작 만점(10) — Fold 6 펼친 화면(7.6") 에서 위쪽 도달 어려움을 근본 해결. 속도 7 은 모드 전환 1단계 추가 비용이지만, 모드 전환 자체가 두 모드를 명확히 분리해 인지 부담을 낮춤 (Vim 모달 UX 와 동일 원리).

옵션별 상세

⑦ 키보드 위 1줄 + 모드 토글 ★ 신규 8.00

장점

  • 한 손 도달 최적 — 가상 키보드 바로 위라 엄지가 자연스럽게 닿음 (Fold 6 펼친 화면에서도)
  • 화면 위쪽 완전 자유 — 원격 화면 상단 (메뉴바·작업표시줄) 가림 0
  • 모드 분리 = 인지 부담 ↓ (텍스트 모드와 명령 모드가 명확히 분리)
  • 한 손 두 모드 모두 도달 가능, 토글 버튼 한 번으로 전환
  • 가상 키보드 표시 시에만 modifier 노출 → 평시 가림 0 (키보드 닫으면 자동 사라짐)

단점

  • 모드 전환 1단계 추가 — multi-modifier 조합 시 모드 전환 부담 (속도 -1점)
  • BT 키보드 사용 시에는 가상 키보드가 안 떠서 modifier 표시 안됨 → 별도 toolbar 토글 필요
  • Android 키보드 높이가 IME 마다 달라서 동적 위치 계산 필요 (MediaQuery.viewInsets)
  • 구현 비용 ↑ — 키보드 표시 상태 감지 + 모드 상태 관리 + 위치 동적 조정

선택해야 하는 이유

Galaxy Fold 6 / 갤럭시 7.6" 같은 큰 폴더블·한 손 비중이 매우 높은 사용자에게 절대 우위. 위쪽 도달 어려움이 사용자가 명시한 핵심 불편이므로, 이를 근본 해결.

선택하지 말아야 하는 이유

BT 키보드 위주 파워 유저는 별도 모드 (Direct keyboard mode, TeamViewer ⑥) 가 더 적합. 또 모드 전환 패턴이 캐주얼 사용자에게 학습 곡선 있을 수 있음 (Vim 모달 UX 익숙하지 않은 경우).

① 1줄 평시 + 3줄 펼침 (현재 RustDesk) 7.25

장점

  • 1줄 평시 덕분에 자주 쓰는 Ctrl·Alt·Shift·Tab 즉시 호출
  • 3줄 펼침으로 전체 키 시각화 → 발견성 우수
  • 모든 키 라벨 명시
  • 44dp+ 큰 터치 타깃

단점

  • 1줄 평시 ~40dp 가림 + 3줄 펼침 ~120dp 가림 → 작업 영역 잠식
  • 화면 위쪽 위치 — Fold 6 펼친 7.6" 에서 한 손 도달 어려움
  • 1줄 키 선택이 고정 — 사용자별 커스터마이즈 불가
⑥ TeamViewer 방식 7.10

장점

  • Direct keyboard mode 토글 — BT 키보드 입력 forward (속도 최고)
  • Ctrl+Alt+Del 전용 콤보 버튼
  • 15년+ 검증된 UX, 사용자 익숙도

단점

  • 한 손 도달 약점 (① 동일)
  • 커뮤니티 피드백: 더 많은 콤보·매핑 요청 누적[2][3]
⑤ Pie menu / Quick wheel 7.10

장점

  • 한 손 엄지로 long-press → 회전 입력 즉시 modifier
  • 화면 어디서나 호출 가능
  • iOS Stadia·SteamOS 검증 패턴

단점

  • 8방향 위치 학습 곡선
  • 접근성 ↓
  • long-press 가 우클릭과 충돌
② FAB + Radial menu 7.05

장점

  • 평시 화면 가림 ~3%
  • FAB 위치 고정 → 엄지 친화
  • Material 표준

단점

  • multi-modifier 시 매번 FAB 호출 → 속도 ↓
  • radial 항목 6개 초과시 가독성 ↓
③ Edge swipe drawer 6.65

장점

  • 평시 화면 가림 0
  • VNC·RDP 표준
  • 한 손 엣지 swipe OK

단점

  • 발견성 가장 낮음
  • Android 뒤로 가기 gesture 충돌
  • 접근성 ↓
④ Bottom sheet + pinch 6.30

장점

  • 큰 그리드 영역
  • 큰 터치 타깃 → 접근성 양호

단점

  • 핀치 줌과 충돌
  • 호출 시 화면 하단 50% 가림

최종 추천 — ⑦ 키보드 위 1줄 + 모드 토글

v4 매트릭스 1위 ⑦ 가 사용자(Galaxy Fold 6)의 핵심 불편(화면 위쪽 가림) 을 근본 해결. 8.00 / 10 점, 한 손 도달 만점.

핵심 디자인

  1. 가상 키보드 바로 위에 modifier 한 줄 배치 — MediaQuery.viewInsets.bottom 로 키보드 표시 감지, 키보드 위 ~44dp 영역에 toolbar 슬라이드 인
  2. 모드 토글 버튼 1개 좌하단 또는 우하단 고정 — 아이콘 전환
    • 텍스트 모드 (default): 가상 키보드만, modifier toolbar 숨김. 일반 텍스트 입력
    • 명령 모드: 가상 키보드 위에 modifier toolbar 표시, 일반 입력은 비활성 (또는 modifier 와 동시 입력)
  3. BT 키보드 연결 시: 가상 키보드 안 뜨므로 별도 floating modifier toolbar 활성화 (Direct mode = TeamViewer ⑥ 패턴 차용)
  4. 긴 펼침은 swipe-up 으로 호출 — 방향키·펑션키·콤보 전체 3줄을 임시 노출 후 자동 닫힘

왜 이게 최선인가: Fold 6 펼친 화면에서 한 손으로 위쪽 도달이 가장 큰 페인 포인트. 키보드 위 배치로 엄지 도달 거리를 화면 위쪽 → 키보드 상단으로 단축 (도달 거리 절반 이하). 모드 분리로 인지 부담 낮춤.

도입 비용 + 코드 변경 위치 (⑦ 기준)

중간 비용 (예상 8~16시간). 핵심은 키보드 표시 상태 감지 + 동적 위치 + 모드 상태 관리.

변경 위치해야 할 일
flutter/lib/mobile/pages/remote_page.dart body 구조 현재 화면 상단의 modifier Row 를 제거. Stack 의 Positioned + MediaQuery.of(context).viewInsets.bottom 로 키보드 위 동적 위치에 배치.
remote_page.dart 모드 상태 enum InputMode { text, command } + InputMode _mode = InputMode.text; 추가. 모드별 동작 분기.
remote_page.dart 토글 버튼 고정 위치(예: 좌하단) FloatingActionButton.small. _mode toggle. 텍스트→명령 전환 시 FocusNode.unfocus() 로 가상 키보드는 유지하되 텍스트 입력 비활성.
remote_page.dart KeyToolbar 위젯 (신규) 기존 wrap('Ctrl ', ...) Row 를 별도 위젯으로 추출. _mode == InputMode.command 일 때만 표시. AnimatedSlide 로 부드럽게.
remote_page.dart 키보드 감지 KeyboardVisibilityBuilder (flutter_keyboard_visibility 패키지) 또는 WidgetsBinding.instance.addObserver + didChangeMetrics 로 키보드 표시 감지.
flutter/lib/models/input_model.dart 모드별 입력 처리 분기. 명령 모드에서 modifier 토글 상태 유지 (sticky modifier, Android TalkBack 표준).
(BT 키보드 대응) remote_page.dart 가상 키보드 미표시 + 외부 키보드 감지 시 화면 우하단 floating modifier toolbar 표시 (⑥ TeamViewer Direct mode 동일 패턴).
flutter/lib/mobile/widgets/gesture_help.dart 제스처 도움말에 "⌨ ↔ ⌘ 버튼으로 텍스트/명령 모드 전환" 안내 추가.

구현 스니펫 (Dart 의사 코드)

// remote_page.dart build() 구조
enum InputMode { text, command }
InputMode _mode = InputMode.text;
bool _keyboardVisible = false;

@override
void didChangeMetrics() {
  setState(() {
    _keyboardVisible = WidgetsBinding.instance.window.viewInsets.bottom > 0;
  });
}

// Scaffold body 안:
return Stack(children: [
  remoteCanvas,  // 원격 화면

  // 키보드 위 modifier toolbar (명령 모드 + 키보드 표시 시)
  if (_keyboardVisible && _mode == InputMode.command)
    Positioned(
      left: 0, right: 0,
      bottom: MediaQuery.of(context).viewInsets.bottom,
      child: Material(
        elevation: 4,
        child: SizedBox(
          height: 44,
          child: Row(children: [
            wrap('Ctrl ', () { /* ... */ }),
            wrap(' Alt ', () { /* ... */ }),
            wrap('Shift', () { /* ... */ }),
            wrap('Tab', () { /* ... */ }),
            wrap('Esc', () { /* ... */ }),
            // 더 긴 펼침은 swipe-up 으로 호출
            PopupMenuButton<String>(
              icon: const Icon(Icons.more_horiz),
              tooltip: 'More keys',
              itemBuilder: (_) => const [
                PopupMenuItem(value: 'arrows', child: Text('Arrow keys')),
                PopupMenuItem(value: 'functions', child: Text('F1-F12')),
                PopupMenuItem(value: 'combos', child: Text('Ctrl+Alt+Del, Win+E, …')),
              ],
            ),
          ]),
        ),
      ),
    ),

  // 모드 토글 FAB (좌하단 고정)
  Positioned(
    left: 16, bottom: 16 + MediaQuery.of(context).viewInsets.bottom + 44,
    child: FloatingActionButton.small(
      onPressed: () => setState(() {
        _mode = _mode == InputMode.text ? InputMode.command : InputMode.text;
      }),
      child: Icon(_mode == InputMode.text ? Icons.keyboard : Icons.keyboard_command_key),
    ),
  ),
]);

상황별 대안

출처 / 참고