공간·멀티모달 인터페이스
Spatial Computing과 멀티모달 AI가 디자인 시스템에 가져오는 새로운 차원
2D 스크린의 시대를 넘어, 공간 컴퓨팅과 멀티모달 AI가 인터페이스의 새로운 차원을 열고 있습니다. 디자인 시스템은 3D 공간, 음성, 제스처라는 새로운 모달리티를 포용해야 합니다.
인터페이스 진화의 흐름
디자인 시스템이 준비해야 할 세 가지 새로운 축:
| 차원 | 핵심 변화 | DS 영향 |
|---|---|---|
| 공간 (3D) | 윈도우가 물리 공간에 배치 | depth, volume 토큰 필요 |
| 음성 | 에이전트가 행동을 수행 | 청각 피드백 토큰, 타이밍 설계 |
| 제스처 | 핸드/아이 트래킹 기반 조작 | 인터랙션 영역 확장 |
Spatial Computing 디자인 원칙
Apple visionOS 26 + Liquid Glass
2025년 Apple은 Liquid Glass 디자인 언어를 도입했습니다. 2013년 iOS 7 이후 가장 큰 비주얼 개편으로, visionOS 26에서 공간 인터페이스의 새로운 기준을 제시합니다.
Liquid Glass 핵심 원칙
반투명성(translucency), 깊이감(depth), 유동적 반응성(fluid responsiveness)을 통해 디지털 요소가 물리 환경과 자연스럽게 어우러지는 경험을 만듭니다.
- 표면 앵커링: 앱 윈도우를 벽, 테이블 등 물리적 표면에 정렬하고 "잠금" 가능 (재시작 후에도 동일 위치 유지)
- 위젯 배치: 환경 곳곳에 위젯을 앵커링하여 공간적 정보 배치
- 공유 경험: 같은 방의 사용자 간 실시간 협업, FaceTime을 통한 원격 참여
Meta Horizon OS
Meta는 Horizon OS를 통해 몰입형 경험을 위한 체계적인 UI 프레임워크를 제공합니다.
- Horizon OS UI Set: 몰입형 경험 전용 UI 컴포넌트 세트 (Figma 디자인 파일 제공)
- Spatial SDK: 게임 엔진 없이 Quest 앱 개발 가능
- Navigator: 앱과 빠른 동작의 중앙 허브
배치 원칙
콘텐츠는 사용자로부터 약 1m 거리, 시선보다 약간 아래에 배치하는 것이 인체공학적으로 최적입니다. 장시간 사용 시 목의 피로를 줄이기 위한 핵심 가이드라인입니다.
공간 디자인 토큰
기존 2D 디자인 토큰에 깊이(depth), 볼륨(volume), 공간 레이아웃(spatial layout) 카테고리를 추가해야 합니다.
// tokens/spatial.ts
export const spatialTokens = {
depth: {
near: '0.5m', // 오버레이, 툴팁
default: '1.0m', // 주요 콘텐츠 기본 배치
far: '2.0m', // 배경 정보, 보조 패널
overlay: '0.3m', // 밀착 오버레이 (알림 등)
},
volume: {
small: { width: '0.3m', height: '0.3m', depth: '0.05m' }, // 위젯
medium: { width: '0.6m', height: '0.4m', depth: '0.05m' }, // 패널
large: { width: '1.0m', height: '0.6m', depth: '0.1m' }, // 앱 윈도우
immersive: { width: '2.0m', height: '1.2m', depth: '0.2m' },
},
spatialLayout: {
anchor: {
wall: { surface: 'wall', offset: '0.02m' },
table: { surface: 'table', offset: '0.01m' },
floating: { surface: 'none', offset: '0m' },
},
gazeBias: '-5deg', // 시선보다 약간 아래 (인체공학 최적)
minTargetSize: '60px', // 최소 상호작용 영역
},
} as const2D-공간 토큰 매핑
2D 스크린에서는 px/z-index, 공간에서는 meter/depth로 자동 매핑합니다.
| 2D 토큰 | 스크린 값 | 공간 값 |
|---|---|---|
| spacing.sm | 8px | 0.02m |
| spacing.md | 16px | 0.04m |
| spacing.lg | 24px | 0.06m |
| elevation.raised | z-index: 10 | depth: 0.05m |
| elevation.overlay | z-index: 50 | depth: 0.1m |
| elevation.modal | z-index: 100 | depth: 0.15m |
음성 UI (Voice User Interface)
2026년 Agentic VUI
음성 UI는 단순한 질의응답을 넘어 행동을 수행하는 에이전트로 진화하고 있습니다. 웹 탐색, 일정 조율, 양식 작성 등 복잡한 태스크를 음성만으로 완료할 수 있습니다.
- 하이브리드 처리: 온디바이스 우선(저지연) + 클라우드 보강(복잡한 추론)
- 인지 AI: 명령 기반에서 컨텍스트 인식 대화형 에이전트로 전환
- 멀티턴 대화: 이전 맥락을 유지하며 자연스러운 대화 흐름 지원
음성 디자인 토큰
// tokens/voice.ts
export const voiceTokens = {
feedback: {
confirmation: { tone: 'positive', duration: 'short', haptic: 'light' },
error: { tone: 'alert', duration: 'medium', haptic: 'strong' },
processing: { tone: 'neutral', duration: 'loop', haptic: 'none' },
waiting: { tone: 'subtle', duration: 'short', haptic: 'none' },
},
timing: {
listenTimeout: '5000ms', // 음성 입력 대기
responseDelay: '200ms', // 응답 전 최소 지연
interruptWindow: '500ms', // 끼어들기 허용 구간
sessionTimeout: '30000ms', // 대화 세션 유지
},
personality: {
tone: 'professional-friendly',
verbosity: 'concise',
confirmationLevel: 'destructive-only',
},
} as const음성-시각 연동 패턴
// components/voice-feedback.tsx
interface VoiceFeedbackProps {
state: 'idle' | 'listening' | 'processing' | 'responding' | 'error'
}
/**
* 음성 상태를 시각적으로 표현하는 컴포넌트
* @ai-hint 음성 인터랙션 시 시각 피드백 필수 제공
*/
export function VoiceFeedback({ state }: VoiceFeedbackProps) {
const visualMap = {
idle: { icon: 'mic', color: '{colors.muted}', animation: 'none' },
listening: { icon: 'mic-active', color: '{colors.primary}', animation: 'pulse' },
processing: { icon: 'loader', color: '{colors.primary}', animation: 'spin' },
responding: { icon: 'speaker', color: '{colors.success}', animation: 'wave' },
error: { icon: 'mic-off', color: '{colors.destructive}', animation: 'shake' },
}
return <FeedbackIndicator {...visualMap[state]} />
}멀티모달 통합 전략
모달리티별 강점과 약점
| 모달리티 | 강점 | 약점 | 적합한 태스크 |
|---|---|---|---|
| 텍스트 | 정밀한 입력, 편집 용이 | 느린 입력 속도 | 검색, 데이터 입력, 코드 |
| 음성 | 빠른 명령, 핸즈프리 | 소음 환경 불리, 프라이버시 | 네비게이션, 빠른 액션 |
| 시각/제스처 | 직관적 3D 조작 | 정밀도 낮음, 피로 누적 | 오브젝트 조작, 공간 탐색 |
| 시선(Gaze) | 암시적 의도 파악 | 의도치 않은 활성화 | 포커스 힌트, 관심 추적 |
모달리티 폴백 전략
환경이나 사용자 상태에 따라 최적의 모달리티로 자동 전환해야 합니다.
// lib/modality-resolver.ts
type Modality = 'spatial' | 'voice' | 'touch' | 'text'
interface ModalityCapabilities {
spatial: boolean // WebXR / visionOS 지원 여부
voice: boolean // 마이크 접근 + 저소음 환경
touch: boolean // 터치스크린 존재
text: boolean // 항상 true (최종 폴백)
}
/** 폴백 체인: spatial → voice → touch → text */
export function resolveModality(
capabilities: ModalityCapabilities,
preference?: Modality
): Modality {
if (preference && capabilities[preference]) return preference
const chain: Modality[] = ['spatial', 'voice', 'touch', 'text']
return chain.find((m) => capabilities[m]) ?? 'text'
}디자인 시스템 적응 전략
레이어드 확장 모델
기존 반응형이 스크린 크기에 적응했다면, 멀티모달 시대의 반응형은 모달리티 자체에도 적응해야 합니다. 기존 DS를 버리지 않고, 레이어를 쌓아 확장합니다.
점진적 확장 로드맵
| 단계 | 기간 | 작업 | 산출물 |
|---|---|---|---|
| 1단계 | 1-2주 | 기존 DS 감사, 확장 포인트 식별 | 확장 계획서 |
| 2단계 | 2-4주 | 음성 토큰 + 피드백 패턴 추가 | 음성 지원 DS |
| 3단계 | 4-8주 | 공간 토큰 + 3D 컴포넌트 | 공간 지원 DS |
| 4단계 | 지속적 | 모달리티 리졸버 + 어댑티브 토큰 통합 | 멀티모달 DS |
컴포넌트 어댑터 패턴
하나의 추상 컴포넌트 정의로 여러 모달리티에서 렌더링합니다.
// components/adaptive-button.tsx
interface AdaptiveButtonSpec {
label: string
action: () => void
variant: 'primary' | 'secondary' | 'destructive'
voiceAlias?: string[]
gestureType?: 'tap' | 'pinch' | 'grab'
}
/**
* 모달리티에 따라 자동으로 렌더링 방식 결정
* - screen → Button, voice → VoiceCommand, spatial → SpatialButton
*/
export function AdaptiveButton(spec: AdaptiveButtonSpec) {
const modality = useModality()
switch (modality) {
case 'spatial':
return (
<SpatialButton
label={spec.label}
onGesture={spec.gestureType ?? 'tap'}
onActivate={spec.action}
depth={spatialTokens.depth.default}
/>
)
case 'voice':
return (
<VoiceCommand
trigger={[spec.label, ...(spec.voiceAlias ?? [])]}
onActivate={spec.action}
visualHint={<Button variant={spec.variant}>{spec.label}</Button>}
/>
)
default:
return (
<Button variant={spec.variant} onClick={spec.action}>
{spec.label}
</Button>
)
}
}어댑터 패턴의 장점
디자인 의사결정은 한 번만 내리고, 모달리티별 렌더링은 어댑터가 담당합니다. 새로운 모달리티가 추가되어도 기존 컴포넌트 스펙은 변경하지 않아도 됩니다.