필드를 누르면 떠오르는 작은 질문
가이드를 '모드'에서 꺼내 보면서 — 별 하나, 토글 하나, 그리고 입력창 두 개가 만들어 낸 깜빡임 버그.
필드를 누르면 떠오르는 작은 질문
Fecit엔 Guide Mode가 있습니다. 별 모양의 fecit 아이콘이 말풍선으로 “무엇을 바라나요?”, “극복해야 할 난관은?” 같은 질문을 던지며 태스크를 한 칸씩 채워 나가게 돕는 대화형 흐름이죠. 마음에 드는 기능인데, 한 가지 한계가 있었습니다 — 모드라는 점.
가이드를 받으려면 먼저 가이드 모드에 들어가야 합니다. 그런데 사람들이 도움을 가장 필요로 하는 순간은, 빈 expectation 칸을 열고 커서를 깜빡이며 “여기 뭘 써야 하지” 하고 멈출 때입니다. 그 순간은 보통 모드 바깥에 있습니다.
그래서 가이드를 모드에서 꺼냈습니다. 가이드 모드가 아니어도, 필드를 포커스하면 그 필드의 질문이 같은 자리(상단 바)에 떠오릅니다. 작성하던 그 맥락에서, 묻지 않아도 답이 먼저 와 있는 셈입니다.
모드에서 꺼낸다는 것
가이드 모드는 능동적입니다. 사용자가 켜고, 단계를 따라가고, 끕니다. 힌트는 수동적이어야 합니다 — 가만히 있다가, 필요한 순간 옆에 와 있고, 시선을 옮기면 사라지는.
그래서 트리거를 포커스로 잡았습니다. target/expectation/obstacle/stakes/result/retrospect, 그리고 제목과 작전(description) — 질문이 있는 필드를 포커스하면 해당 질문이 뜨고, blur하면 닫힙니다. 별도 진입도, 다음/이전 버튼도 없습니다. 그냥 거기 있습니다.
새로 만들지 않기
처음 떠오른 건 “힌트 바를 새로 만들자”였습니다. 그런데 가이드 모드의 바(GuideBar)가 이미 같은 자리에, 같은 모양으로 있었습니다. 아이콘 + 말풍선 + (가이드 모드일 땐) 단계 표시와 네비게이션.
새로 만드는 대신 GuideBar에 hint 모드를 추가했습니다. hint일 땐 단계 표시와 네비게이션을 숨기고, 별 아이콘과 질문 말풍선만 남깁니다. 위치·색·애니메이션이 전부 가이드 모드와 동일하게 떨어집니다 — 같은 성격의 두 표면이 같은 모양이어야 한다는 결의 연장선입니다.
기본은 켜짐, 단 끌 수 있게
도움이 되는 기능도 어떤 사람에겐 노이즈입니다. 필드 이름만 봐도 뭘 쓸지 아는 사용자에게 매번 질문이 뜨는 건 거슬릴 수 있죠.
showFieldHints라는 preference 필드를 새로 만들었습니다. 기본은 켜짐 — 이게 디폴트 경험이어야 한다고 봤습니다. 특히 처음 쓰는 사람에게요. 모바일·데스크톱 설정 양쪽에 토글을 넣었고, 서버에 동기화되니 한쪽에서 끄면 다른 쪽도 따라옵니다.
값이 비어 있으면(null) 켜진 것으로 취급합니다. 마이그레이션 없이 모두에게 기본 ON.
별은 이미 있었다
힌트 바의 아이콘으로 별을 골랐습니다. fecit의 별은 앱 곳곳에서 이미 살아 움직입니다 — 태스크를 완료하면 통통 튀고, 가끔 반짝이고, 메모를 만들면 한 바퀴 돕니다. 그 애니메이션 엔진이 AnimatedStar입니다.
처음엔 힌트 별의 등장 애니메이션을 직접 짰습니다. 그런데 어딘가 어색했습니다. 살짝 움직이다 끊기는 느낌. 원인은 초기값이었습니다 — 별을 꽉 찬 크기로 띄워 놓고, 효과가 0으로 줄였다가 다시 키우니 “나왔다 끊겼다” 처럼 보였던 거죠.
여기서 한 발 물러섰습니다. 왜 직접 짜고 있지? 앱의 모든 별이 쓰는 엔진이 이미 있는데. AnimatedStar에 햅틱도 회전도 없는 순수 scale pulse(pop) 트리거를 하나 추가하고, 힌트 별을 그걸로 교체했습니다. 질문이 바뀔 때마다 컴포넌트가 다시 마운트되며 pop이 재생됩니다. 손으로 짠 애니메이션은 지웠습니다.
교훈은 단순합니다 — 같은 종류의 동작이 이미 앱에 있다면, 새로 짜기 전에 그걸 먼저 본다.
조금 더 즐겁게
질문이 매번 똑같으면 캐릭터가 죽습니다. 그래서 필드마다 질문을 두세 개씩 풀(guideHints)로 만들어 두고, 포커스할 때마다 그 중 하나를 뽑습니다. target이라면 “지금 어디쯤 와 있나요?” / “출발점은 어디인가요?” / “현재 상황은 어떤가요?” 중에서.
여기서 작은 함정 하나. 렌더마다 랜덤을 뽑으면 타이핑할 때마다 질문이 바뀌어 깜빡입니다. 그래서 질문은 포커스한 필드가 바뀔 때만 새로 뽑도록 묶었습니다(useMemo). 같은 필드를 다시 누르면 새 문구, 같은 필드에 머무는 동안엔 고정.
가이드 모드의 질문은 건드리지 않았습니다. 모드는 구조화된 흐름이라 일관된 문구가 낫고, 변주는 힌트 쪽에만 줬습니다.
생성 화면에선 닫지 않고도
힌트 바는 상단을 덮습니다. 생성 화면에선 그 자리에 “만들기” 버튼이 있어서, 힌트가 떠 있으면 버튼이 가려집니다. 만들려면 힌트를 먼저 닫아야 하는 작은 마찰.
그래서 생성 화면의 힌트 바에는 우측에 + 버튼을 달았습니다. 닫지 않고도 거기서 바로 태스크를 만들 수 있습니다. 상세/편집 화면엔 “만들기”가 없으니 이 버튼도 안 나옵니다 — 같은 컴포넌트지만 맥락에 따라 달라집니다.
입력창 두 개, 이벤트 순서 두 개
만들고 보니 한쪽으로만 깜빡이는 버그가 있었습니다.
- 제목 → 작전으로 포커스를 옮기면: 말풍선만 바뀝니다. (정상)
- 작전 → 제목으로 옮기면: 바가 사라졌다 다시 나타납니다. (버그)
같은 “필드 전환”인데 한 방향만 깜빡이는 게 이상했습니다. 따라가 보니 범인은 이벤트 순서였습니다.
제목은 chip을 품은 contenteditable이고, 작전은 리치 텍스트 에디터입니다. 입력창 종류가 다르니 blur와 focus가 발생하는 순서도 달랐습니다. 한쪽은 focus(다음)이 blur(이전)보다 먼저 와서 힌트 필드가 곧장 갈아끼워지고, 다른 쪽은 blur이 먼저 와서 잠깐 “아무것도 없음” 상태를 거친 뒤 다시 채워졌습니다. 그 찰나의 빈 상태에서 바가 unmount → remount 됐던 거죠.
해법은 흔한 패턴입니다. blur 시 즉시 닫지 않고, 다음 틱에 닫기. 그 사이 다른 필드가 포커스되면 닫기 예약을 취소합니다. 포커스가 진짜로 아무 데도 가지 않을 때만 닫힙니다. blur와 focus가 어떤 순서로 오든, 그 틈을 한 틱이 메웁니다.
여기에 더해, chip 입력창은 타이핑 도중에도 focus 이벤트를 다시 쏘는 경우가 있었습니다. 닫은 힌트가 그 때문에 되살아나지 않도록, 같은 필드의 재포커스 이벤트는 무시하는 가드도 함께 넣었습니다.
그 외 잔손질
- 데스크톱에서 말풍선과 꼬리가 떨어져 있었습니다. 컨테이너의 gap이 꼬리까지 밀어낸 탓. gap을 떼고 단계 표시에만 따로 간격을 줬습니다.
- 생성 모달의 헤더 높이가 힌트 바와 어긋났습니다. 헤더 높이를 힌트 바(44px)에 맞췄습니다.
- 상세 화면에선 제목 자동 포커스를 껐습니다. 진입하자마자 힌트가 튀어나오지 않게. 생성 화면은 반대로 바로 쓰기 시작하는 흐름이라 자동 포커스를 그대로 뒀습니다 — 거기선 첫 질문이 바로 떠도 자연스럽습니다.
모드가 아니라 곁
큰 변화는 한 줄입니다 — 가이드를 모드에서 꺼내 곁에 두기. 켜고 끄는 것이 아니라, 필요한 순간 옆에 있다가 시선을 옮기면 물러나는.
별 하나가 통통 튀며 질문을 건네는 데까지, 결정이 꽤 많았습니다. 모드냐 상시냐, 새로 짜냐 재사용하냐, 기본을 켜냐 끄냐, 그리고 입력창 두 개가 만든 한 방향짜리 깜빡임까지. 작은 표면 하나에 이만큼의 결정이 들어가는 게, 신기하기도 하고 익숙하기도 합니다.
가장 좋은 가이드는 “도움말”을 누르게 하지 않습니다. 그냥 거기 있다가, 필요할 때 한 줄 묻고, 답을 쓰기 시작하면 조용히 물러납니다.