인터랙션과 상태
누를 수 있어 보이게, 누르면 반응하게, 기다리거나 실패해도 알 수 있게 만드는 법이다.
목차 (13)
누를 수 있어 보이게: 시그니파이어
화면의 모든 것은 클릭할 수 있다. 그래서 디지털 화면에서 중요한 것은 "할 수 있는가"(어포던스)보다 "할 수 있다는 것이 보이는가"(시그니파이어)다. 두 개념과 노먼의 행동 단계는 HCI와 UX 노트에 있고, 여기서는 그 신호를 화면 요소로 옮기는 법만 본다.
- 관례를 따른다: 사람은 다른 앱에서 배운 모양을 기대한다. 밑줄 친 파란 글자는 링크, 테두리 있는 상자는 입력 칸, 오른쪽 꺾쇠는 다음 화면이다. 새 모양을 만들면 그 뜻을 다시 가르쳐야 한다.
- 평평한 디자인의 함정: 그림자와 테두리를 다 걷어 내면 버튼과 제목이 똑같은 글자가 된다. 장식은 줄여도 누를 수 있다는 신호 하나는 남긴다.
- 호버에만 기대지 않는다: 마우스를 올려야 드러나는 신호는 터치 화면에서는 아예 보이지 않는다. 기본 모양만으로 알아볼 수 있어야 한다.
버튼 위계: 주·보조·텍스트 버튼
한 화면에 버튼이 여럿이면 무게를 나눈다. 가장 흔하고 바라는 다음 행동 하나를 주 버튼(채움)으로, 다른 선택을 보조 버튼(테두리)으로, 취소나 건너뛰기처럼 가벼운 것을 텍스트 버튼으로 둔다. GOV.UK 디자인 시스템도 주 버튼은 화면의 주된 행동에만 쓰고 한 페이지에 여러 개 두지 말라고 적는다.
- 문구는 결과를 말한다: "확인"·"예" 대신 "저장"·"삭제"처럼 누르면 일어날 일을 쓴다. 대화상자 문구만 읽지 않고 버튼만 봐도 무엇을 고르는지 안다.
- 위험한 동작은 따로: 되돌릴 수 없는 삭제는 빨간 경고 버튼으로 구분하되, 색만이 아니라 문구로도 무엇이 일어나는지 밝힌다. GOV.UK는 경고 버튼을 쉽게 되돌릴 수 없는 심각한 동작에만 쓰라고 한다.
- 순서는 플랫폼을 따른다: 주 버튼을 오른쪽에 두는 관례가 흔하지만 플랫폼과 디자인 시스템마다 다르다. 한 제품 안에서는 늘 같은 자리에 둔다.
컴포넌트 상태: 기본·호버·포커스·누름·비활성·선택·로딩
버튼 하나도 여러 상태를 가진다. 상태마다 모양이 달라야 사람은 "지금 눌러도 되는지, 눌렀는지, 처리 중인지"를 안다. 디자인 시안에 기본 모양만 그리고 나머지를 개발자 몫으로 남기면, 상태마다 제각각인 화면이 나온다. 상태는 처음부터 표로 정해 둔다.
| 상태 | 언제 | 보이는 신호 | 주의 |
|---|---|---|---|
| 기본(enabled) | 아무 일도 없을 때 | 누를 수 있다는 기본 모양 | 다른 상태의 출발점 |
| 호버(hover) | 마우스가 위에 있을 때 | 색을 조금 바꾸고 커서를 손 모양으로 | 터치에는 없다. 정보를 호버에만 두지 않는다 |
| 포커스(focus) | 키보드·스크립트로 포커스가 왔을 때 | 요소 둘레의 링(outline) | 색만 바꾸지 말고 테두리로. 아래 절 |
| 누름(pressed) | 누르고 있는 동안 | 조금 작아지거나 진해짐 | 입력을 받았다는 즉시 피드백 |
| 비활성(disabled) | 지금은 쓸 수 없을 때 | 흐린 색, 반응 없음 | 왜 못 쓰는지 알린다. 흐려도 글자는 읽혀야 한다 |
| 선택(selected) | 켜짐·고름이 유지될 때 | 채움, 체크, 굵은 테두리 | 호버·포커스와 겹쳐도 구분되게 |
| 로딩(loading) | 누른 동작을 처리하는 동안 | 버튼 안 회전 표시, "저장 중" | 다시 누르기를 막는다(이중 제출) |
NN/g의 버튼 상태 글은 비활성 상태에 aria-disabled="true"를 달아 화면 낭독기가 상태를 읽게 하라고 하고, 로딩 상태는 보통 기본 모양에 회전 표시를 더한 것이라고 설명한다. 상태는 따로 놀지 않고 사건에 따라 서로 바뀐다. 아래 그림은 그 전이를 한 단계씩 따라간다.
처음: 아무 일도 없는 기본 상태다.
비활성 버튼은 조심해서 쓴다. 제출 버튼을 처음부터 비활성으로 두면 사용자는 무엇이 빠졌는지 모른 채 버튼만 누른다. 누를 수 있게 두고 눌렀을 때 빠진 칸을 알려 주거나, 비활성이라면 이유를 바로 옆에 적는다.
포커스 링을 지우면 안 되는 이유
브라우저는 포커스가 간 요소에 테두리(포커스 링)를 그린다. 마우스로 누를 때 이 링이 거슬린다며 outline: none으로 지우는 일이 흔한데, 그러면 키보드만으로 쓰는 사람은 지금 어디에 있는지 알 수 없다. MDN도 포커스 스타일을 지우면 눈이 보이는 키보드 사용자에게 키보드 내비게이션을 쓸 수 없게 만든다고 적는다.
해법은 지우는 것이 아니라 :focus-visible로 다시 그리는 것이다. 이 선택자는 브라우저가 "포커스를 보여 줘야 한다"고 판단할 때만 맞는다. 보통 마우스로 버튼을 누를 때는 맞지 않고, 키보드로 옮겨 오거나 글자를 입력할 칸에 포커스가 가면 맞는다.
/* 마우스 클릭에는 링이 없고, 키보드로 오면 또렷한 링이 생긴다 */
button:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}- 대비: MDN은 WCAG 2.1의 비텍스트 대비 기준(1.4.11)에 따라 포커스 표시가 주변과 3:1 이상 대비되어야 한다고 적는다.
- 기준: WCAG의 2.4.7(포커스 보임, AA)은 키보드로 다루는 화면에 포커스 표시가 보이는 방식이 있어야 한다고 요구한다. 2.2에서는 포커스가 고정 헤더 같은 것에 완전히 가리지 않아야 한다는 2.4.11(AA)이 더해졌다. 키보드 내비게이션 전반은 반응형·모바일·접근성 노트에 있다.
피드백: 즉시, 기다리는 동안, 끝났을 때
사람은 누른 뒤 무슨 일이 일어나는지 계속 확인한다. 피드백은 세 시점에 필요하다. 누른 순간에는 "받았다", 기다리는 동안에는 "하고 있다", 끝나면 "됐다" 또는 "안 됐다, 이렇게 하라"다. 사람이 즉각적이라고 느끼는 0.1초, 흐름이 끊기지 않는 1초, 주의를 붙잡아 둘 수 있는 10초라는 응답 시간의 세 한계는 HCI와 UX 노트에 있다.
NN/g의 진행 표시 글은 이렇게 정리한다. 약 1초가 넘는 동작에는 진행 표시를 둔다. 2~10초는 반복 애니메이션(스피너)이 맞고, 10초 이상은 얼마나 됐는지 보이는 진행률을 쓴다. 1초 미만에 반복 애니메이션을 띄우면 오히려 산만하다. "로딩 중…"처럼 멈춰 있는 글자는 시스템이 멈췄는지 알 수 없게 하므로 쓰지 말고, "두 번 누르지 마세요" 같은 경고 대신 첫 번째 누름을 받았다는 것을 보이라고 한다.
로딩 UX: 스피너, 스켈레톤, 진행률, 낙관적 UI
기다림을 보여 주는 방법은 크게 셋이다. NN/g의 스켈레톤 글에 따르면 1초 안에 끝나면 아무것도 필요 없고, 스피너는 영상이나 대시보드 카드처럼 한 부분을 2~10초 기다릴 때, 스켈레톤(글과 사진 자리를 회색 막대로 먼저 그린 화면)은 페이지 전체를 10초 안쪽으로 기다릴 때 맞고, 10초를 넘으면 진행률을 강하게 권한다. 스켈레톤은 실제 화면의 모양을 미리 보여 주므로 내용이 들어와도 자리가 덜 흔들린다. 틀만 있고 모양이 없는 스켈레톤은 권하지 않는다.
아예 기다림을 없애는 방법도 있다. 낙관적 UI(optimistic UI)는 서버 응답을 기다리지 않고 성공했다고 보고 화면을 먼저 바꾼 뒤, 드물게 실패하면 되돌리고 알린다. React의 useOptimistic은 요청이 진행 중일 때만 낙관적 값을 보여 주고, 요청이 실패해 실제 값이 바뀌지 않으면 화면이 자연히 원래 값으로 돌아가게 만든다.
누르기 전: 좋아요 41개. 두 경우가 같다.
낙관적 UI의 조건은 둘이다. 거의 항상 성공해야 하고, 실패했을 때 되돌려도 사용자가 손해 보지 않아야 한다. 실패 알림은 무엇이 되돌려졌는지 분명히 말하고 다시 시도를 붙인다. 오프라인에서 쌓아 두었다가 나중에 보내는 앱의 동기화 충돌은 모바일 개발 노트에 있다.
빈 상태와 오류 상태
빈 상태(empty state)는 목록이나 대시보드에 보일 것이 없는 화면이다. 처음 가입한 사람, 검색 결과가 없는 사람, 다 처리한 사람이 모두 이 화면을 본다. NN/g의 빈 상태 글은 세 가지를 권한다. 시스템 상태를 알린다(불러오는 중에는 진행 표시, 다 불러왔는데 없으면 분명한 문구). 이 기능이 무엇이고 어떻게 채우는지 가르친다. 그리고 첫 행동으로 가는 길을 둔다. 몇 초 뒤 내용으로 바뀔 "결과 없음"을 먼저 띄우면 사람은 기다리지 않고 없다고 믿어 버린다.
오류 상태는 무엇이 안 됐는지, 사용자의 작업은 어떻게 됐는지, 이제 무엇을 하면 되는지를 알려야 한다. NN/g의 오류 메시지 지침의 요지는 이렇다. 원인 가까이에 보인다. 오류 코드 대신 사람의 말로 무엇이 문제인지 정확히 쓴다. 고치는 법을 알려 준다. 사용자를 탓하지 않는다. 그리고 입력을 지우지 않는다. 시스템 전체가 멈춘 경우라면 사과와 함께 할 수 있는 다른 일을 보여 준다.
입력 폼의 오류 메시지를 언제, 어디에, 어떻게 쓰는지는 폼과 입력 노트에서 따로 다룬다.
실행 취소 vs 확인 대화상자
지우는 동작을 지키는 방법은 둘이다. 하기 전에 묻는 확인 대화상자와, 바로 하고 나중에 무를 수 있게 하는 실행 취소(undo)다. NN/g는 확인 대화상자를 작업을 날리거나 돈이 들거나 되돌릴 수 없는 심각한 동작에만 쓰라고 한다. 너무 자주 물으면 사람은 읽지 않고 누르는 버릇이 들어, 정작 실수했을 때도 그대로 확인해 버린다. 그래서 되돌릴 수 있다면 실행 취소가 먼저다.
- 되돌리기를 만들려면: 지운 것을 바로 없애지 말고 잠시 "지움 표시"만 해 두었다가 시간이 지나면 정리하거나 휴지통으로 옮긴다. 스낵바의 "실행 취소"는 그 표시를 지운다.
- 그래도 확인이 필요하다면: 무엇이 일어나는지 구체적으로(파일 이름, 영향받는 사람 수) 쓰고, 버튼은 "예/아니요" 대신 "파일 삭제/유지"처럼 결과로 적고, "예"를 미리 골라 두지 않는다. 아주 드물고 위험한 동작에는 이름을 직접 입력하게 하는 식으로 일부러 마찰을 더한다.
- 이 사이트의 규칙: 이 사이트의 도구들은 지우는 동작에 브라우저
confirm()을 쓰지 않는다. 브라우저가 반복된 대화상자를 막으면confirm()이 늘 거짓을 돌려줘 삭제가 조용히 무시되기 때문이다. 곧바로 지우고 상태 줄에 되돌리기를 띄운다.
마이크로 인터랙션: 트리거·규칙·피드백·루프
마이크로 인터랙션은 좋아요 하트, 당겨서 새로 고침, 스위치 하나처럼 한 가지 일만 하는 작은 상호작용이다. NN/g는 이를 트리거와 피드백의 짝으로 정의한다. 트리거는 사용자의 동작일 수도 있고(누르기, 제스처, 음성) 정해 둔 조건을 만난 시스템일 수도 있다(알람 시각). 피드백은 그 트리거에 대한 좁고 구체적인 반응이고, 보통 트리거 가까이에 작게 나타난다. NN/g는 이 개념을 정리한 사람으로 댄 새퍼(Dan Saffer)의 2014년 책 『Microinteractions』를 든다. 이 노트는 그 책의 틀을 따라 트리거와 피드백 사이에 "무엇이 일어나는가"(규칙)를, 뒤에 "다시 쓰거나 시간이 지나면 어떻게 되는가"(루프와 모드)를 더해 네 부분으로 본다.
- 트리거: 무엇이 시작하는가. 사용자가 시작하는 것이라면 눈에 보여야 하고, 늘 같은 자리에 있어야 한다.
- 규칙: 시작되면 무엇이 일어나고, 무엇은 할 수 없는가. 시스템 설정이 이미 다크라면 스위치는 어떻게 보이는가 같은 경계 조건이 여기 들어간다.
- 피드백: 규칙이 실행된 것을 어떻게 보이는가. 필요 이상으로 화려하지 않게, 정보가 되는 만큼만.
- 루프와 모드: 두 번째, 백 번째에는 어떻게 되는가. 처음에는 알려 주던 안내를 익숙해지면 줄이고, "자동" 같은 모드가 있다면 언제 바뀌는지 정한다.
NN/g가 든 예 가운데 쇼핑몰의 하트는 누르면 짧게 뛰고, 다시 누르면 되돌아가 실행 취소 역할까지 한다. 작은 움직임을 어떻게 만드는지는 모션과 전환 노트에 있다.
토스트·스낵바·모달·시트 고르기
메시지를 띄울 자리는 성격에 따라 고른다. 질문은 셋이다. 특정 입력에 붙은 문제인가? 사용자가 답해야 다음으로 갈 수 있는가? 고를 것이 여러 개인가? NN/g는 알림을 사용자의 행동이 필요한 것과 필요 없는 것(수동적인 것)으로 나누고, 몇 초 뒤 사라지는 작은 토스트는 수동적인 알림에는 맞지만 오류 메시지에는 나쁜 선택이라고 한다.
| 표면 | 막는가 | 쓰는 곳 | 주의 |
|---|---|---|---|
| 인라인 메시지 | 아니요 | 한 필드·한 카드의 문제 | 원인 바로 옆에. 오류의 기본 자리 |
| 토스트·스낵바 | 아니요 | "보관했습니다" 같은 결과, 실행 취소 하나 | 저절로 사라지므로 꼭 읽어야 할 오류에 쓰지 않는다 |
| 모달 대화상자 | 예 | 되돌릴 수 없는 일의 경고, 진행에 꼭 필요한 입력 | 목표와 무관한 모달(광고, 설문)은 짜증과 불신을 부른다 |
| 바텀 시트 | 모달형은 예, 비모달형은 아니요 | 공유 대상·정렬 기준처럼 고를 것이 여러 개 | 뒤로 가기로 닫히게, 닫기 버튼도 두고, 시트 위에 시트를 쌓지 않는다 |
안드로이드의 Material 스낵바는 화면 아래쪽에 짧게 떠서 사용자를 방해하지 않고, 실행 취소나 다시 시도 같은 동작을 하나 붙일 수 있으며, 새 스낵바가 뜨면 이전 것은 사라져 한 번에 하나만 보인다. 문서는 스낵바가 동작한 맥락 안에 뜨므로 시스템 토스트보다 피드백에 더 맞다고 권한다. 모달과 바텀 시트의 지침은 NN/g의 각 글에 있다.
제스처: 보이지 않는 조작
밀기, 길게 누르기, 핀치 같은 제스처는 익숙한 사람에게는 빠른 지름길이지만 화면에 신호가 없다. 해 보기 전에는 있는지조차 모른다. 그래서 제스처는 같은 일을 하는 보이는 수단 위에 얹는 지름길로 쓴다.
- 접근성 기준: WCAG 2.5.1(포인터 제스처, A)은 두 손가락 핀치나 정해진 경로로 긋는 동작이 필요한 기능을 한 손가락의 단순한 누르기로도 할 수 있어야 한다고 요구한다(W3C 설명). 2.2에서 더해진 2.5.7(끌기 동작, AA)은 끌어서 하는 일도 누르기만으로 할 수 있게 하라는 것이다.
- 운영체제 제스처와 겹치지 않게: 화면 가장자리에서 미는 동작은 뒤로 가기나 홈 같은 시스템 제스처와 부딪힌다. 앱 제스처는 가장자리에서 떨어뜨린다.
- 되돌릴 수 있게: 밀어서 지우기처럼 결과가 큰 제스처는 실수로 일어나기 쉽다. 위의 실행 취소를 붙인다.
흔한 오해
- "확인 대화상자를 많이 띄울수록 안전하다" — 자주 물으면 읽지 않고 누르게 된다. 심각한 동작에만 묻고, 나머지는 실행 취소로 지킨다.
- "포커스 링은 디자인을 해치니 지운다" — 키보드 사용자의 커서를 지우는 것과 같다.
:focus-visible로 키보드일 때만 또렷하게 그린다. - "스피너만 띄우면 기다림은 해결된다" — 10초가 넘으면 진행률과 남은 시간이 필요하고, 1초 미만에는 스피너가 오히려 깜빡임이 된다.
- "비활성 버튼은 실수를 막아 준다" — 왜 못 누르는지 모르면 막힌 느낌만 준다. 이유를 옆에 적거나, 누를 수 있게 두고 빠진 것을 알린다.
- "오류는 토스트로 알리면 된다" — 저절로 사라지는 메시지는 놓치기 쉽다. 오류는 원인 옆에 남겨 둔다.
- "제스처가 많을수록 세련된 앱이다" — 보이지 않는 조작은 배울 수 없다. 늘 보이는 대체 수단을 함께 둔다.
핵심 자료
요지는 직접 정리한 한 줄이다.
| 자료 | 한 줄 요지 |
|---|---|
| NN/g — Microinteractions in User Experience | 마이크로 인터랙션을 트리거와 피드백의 짝으로 정의하고 예를 모았다. |
| NN/g — Progress Indicators | 1초 넘으면 진행 표시, 2~10초는 반복 애니메이션, 10초 이상은 진행률. |
| NN/g — Skeleton Screens 101 | 스켈레톤은 화면 전체, 스피너는 한 부분, 10초 넘으면 진행률. |
| MDN — :focus-visible | 포커스 표시를 지우지 말고 키보드일 때만 보이게 다시 그리는 법. |
| NN/g — Confirmation Dialogs | 확인은 심각한 동작에만, 결과를 이름으로 적고, 되돌리기가 먼저다. |
| NN/g — Error-Message Guidelines | 가까이, 사람 말로, 고치는 법과 함께, 입력은 지우지 않고. |
| NN/g — Modal & Nonmodal Dialogs | 모달은 막아야 할 때만. 목표와 무관한 모달은 신뢰를 깎는다. |
| React — useOptimistic | 요청 중에만 낙관적 값을 보이고, 실패하면 원래 값으로 돌아간다. |
- Microinteractions in User Experience — NN/g
- Button States: Communicate Interaction — NN/g
- :focus-visible — MDN Web Docs
- Progress Indicators Make a Slow System Less Insufferable — NN/g
- Skeleton Screens 101 — NN/g
- useOptimistic — React
- Designing Empty States in Complex Applications — NN/g
- Error-Message Guidelines — NN/g
- Confirmation Dialogs Can Prevent User Errors (If Not Overused) — NN/g
- Modal & Nonmodal Dialogs — NN/g
- Indicators, Validations, and Notifications — NN/g
- Bottom Sheets: Definition and UX Guidelines — NN/g
- Snackbar — Material Components for Android
- Button — GOV.UK Design System
- Understanding SC 2.5.1 Pointer Gestures — W3C