Skip to main content
← 블로그

focused task를 더 돋보이게 하고 싶었다

VauDium ·

focus된 태스크를 시각적으로 강조하려고 애니메이션 여섯 개를 만들었다가 전부 지우고, 결국 preference 토글로 착지한 하루의 기록입니다.

focused task를 더 돋보이게 하고 싶었다

이미 있는 것부터

Fecit에선 task를 focus하면 리스트에서 빠져서 상단의 별도 슬롯으로 올라갑니다. 이 슬롯은 이미 꽤 눈에 띕니다. 1.5px PRIMARY300 테두리, PRIMARY400 컬러의 iOS 네이티브 shadow halo, 리스트보다 살짝 다른 배경톤. 시각적 위계는 이미 잡혀 있습니다.

그런데 오늘 문득 “이걸 조금 더 눈에 띄게 할 수 없나?” 하는 생각이 들었습니다. focus한 task가 “지금 내가 집중해야 할 것”이라는 신호를 더 강하게 주고 싶었습니다. 간단한 목표처럼 들렸습니다.

결과적으로는 애니메이션 여섯 개를 만들고 전부 지운 하루가 됐습니다.

시도 1: 회전하는 점선 테두리

가장 먼저 떠오른 건 comet 같은 효과였습니다. task item 둘레를 따라 점 하나가 빙글빙글 도는. Reanimated + react-native-svg로 AnimatedRectstrokeDashoffset을 애니메이션하는 패턴입니다. 첫 프로토타입은 괜찮았습니다. “빙글빙글 도네” 하는 반응이 나왔습니다.

그 다음부터가 문제였습니다. 점선 길이를 조금 더 길게, 간격을 좁히자, 개수를 줄이자, 두께를 얇게, 색깔을 바꾸자, glow를 추가, glow를 빼자… 디테일 튜닝을 반복하다가 “바깥쪽에 헤일로 느낌으로” 라는 방향 전환이 나왔습니다. 점선을 아이템 바깥으로 옮기니 갑자기 안 보였습니다.

여기서 두 시간을 태웠습니다

점선이 바깥으로 안 나옵니다. 색이 문제인가? 두께가? Reanimated가 동작을 안 하는 건가? 빨간색 4px로 바꿔도 안 보였습니다. animatedProps가 react-native-svg 15.x + Reanimated 4.x에서 제대로 동작하지 않는다고 의심하고, requestAnimationFrame + setState 기반 JS 애니메이션으로 아예 접근을 바꿨습니다. 여전히 안 보였습니다.

범인은 LightSwipeableoverflow: "hidden" — 이라고 처음엔 생각했지만, 사실은 한 단계 더 위, focused task를 감싸는 FocusedTaskWrapperoverflow: "hidden"이었습니다. SVG를 리스트 아이템 바깥으로 뺀 뒤에도 상위 wrapper가 모든 걸 잘라내고 있었습니다. 심지어 “바깥 halo가 보여요”라고 제가 읽었던 것은 제 SVG 글로우가 아니라 원래부터 있던 FocusedTaskWrapper의 네이티브 iOS shadow였습니다. 두 시간을 태운 뒤 깨달았습니다.

교훈: 레이아웃 라이브러리를 의심하기 전에 DOM 트리를 거슬러 올라가며 어디서 clip되는지 직접 확인하자. 그리고 기존에 보이던 효과를 “내가 만든 것”으로 착각하지 말자.

구조를 재배치해서 SVG를 상위 wrapper 바깥으로 옮기니 드디어 회전이 보였습니다.

시도 2: 점 하나하나가 glow인 헤일로

“이거 말고, 점 하나하나가 halo처럼 부드러운 glow면서 일렁이게.” 점선 패턴을 유지하되 각 dash를 두꺼운 soft stroke로 바꾸고, 3겹 레이어로 부드러움을 더했습니다. 속도도 1.8초로 천천히.

“너무 명시적으로 보인다, 좀 더 묻혀있게.” 조정. “안쪽은 깔끔하게, 밖으로만 퍼지게.” 조정. “아래쪽 테두리가 잘립니다.” overflow: "hidden"의 유산이 또 나왔습니다. 조정.

시도 3: 등대 빔

한 덩어리의 긴 soft arc가 테두리를 따라 도는 효과. 점선이 아니라 lighthouse beam 같은 느낌. 만들고 보여드렸더니:

“아니 이거 말고, 점 하나하나가 glow여야 해.”

잘못 이해한 겁니다. 되돌림.

시도 4: 숨 쉬는 그림자

“점선 도는 건 포기하자.” 대신 네이티브 shadow의 opacity/radius를 호흡하듯 pulse 시키는 쪽으로. 2초 주기, 0.3↔0.6 opacity, 8px↔14px radius. 조용하고 차분한 호흡.

“있다는 건 알겠지만 열심히 봐야 보여.” 범위를 0.15↔1.0 / 3px↔25px로 극단화. 속도 1.2초. 여전히 “너무 안 뜨니다.” 아래쪽이 slot의 overflow: "hidden"에 잘리는 것도 영향이 있었습니다. 그것까지 해결해도 “안 되겠다, 이거도 포기하자.”

시도 5: 스포트라이트

focused task에 효과를 더하는 대신, 아래 FlatList를 어둡게 만들어 주인공을 돋보이게 하는 방식. opacity 0.55로 dim. 효과는 확실했습니다.

“집중은 되는데 리스트에 추가하거나 관리할 때 안 좋을 것 같애.” 맞는 말이었습니다. 되돌림.

시도 6: 배경 틴트

focused slot의 배경을 NEUTRAL100 대신 PRIMARY50로 살짝 파란 톤. “안 보여.” PRIMARY100으로 올림. “여전히.” 테스트를 위해 빨간색으로. “안 하는 게 낫겠다.”

깨달음

여섯 번의 실패가 알려준 건 단순했습니다. focused slot은 이미 충분히 강조되어 있었습니다.

  • 별도 고정 슬롯 (물리적 위치 차별화)
  • 1.5px PRIMARY300 테두리
  • 네이티브 shadow halo (이미 은은한 glow)
  • 리스트와 분리된 배경톤

여기에 시각 효과를 얹으려 할수록 “너무 미묘해서 안 보임” 또는 “너무 튀어서 거슬림” 사이에서 왔다 갔다 했습니다. 움직이는 요소는 특히 집중을 돕는 게 아니라 방해합니다. 눈이 거기로 계속 끌려가기 때문입니다.

“집중을 돕는 시각 효과”는 애초에 모순일지도 모릅니다. 집중은 조용한 상태입니다. 정적인 위계가 이미 잡혀 있으면 그걸로 충분하고, 거기에 뭘 더하는 건 대부분 장식이 됩니다.

방향 전환: 시각 → 기능

시각으로 해결되지 않는 문제라면 기능으로 가보자. focused task에는 그 task를 작업하는 데 실제로 유용한 정보를 더하자. 예를 들면 경과 시간.

그런데 오늘 아침에 경과 시간을 Live Activity에서도 빼자고 결정한 참이었습니다. 압박감 때문이었죠. 그래서 이번엔 다른 방향, opt-in preference로 가기로 했습니다.

  • 기본값 OFF (압박감 없는 미니멀한 경험이 기본)
  • 세팅에서 “경과 시간 표시” 토글을 ON 하면 리스트 아이템 + Live Activity에 표시
  • duration이 있으면 남은 시간 카운트다운 (overdue 시 빨강 + 음수로 -05:23)
  • duration이 없으면 단순 경과 시간

Fecit의 “Minimal to Maximal” 원칙과도 맞습니다. 기본은 조용하고, 사용자가 원할 때 깊이를 더할 수 있습니다.

돌아보며

시각 효과 여섯 개를 만들었다 지운 후에 남은 건, 결국 preference 토글 하나입니다. 코드 변경 라인은 많지 않습니다. 하지만 하루 종일 돌고 돌아서 “더하는 게 아니라 사용자에게 선택권을 주는 것”에 도착한 건 나름대로 의미 있었습니다.

부차적으로 몇 가지 교훈도 남았습니다:

  • Clipping 의심은 DOM 트리를 거슬러 올라가면서 직접 확인. 상위 wrapper들의 overflow 속성을 하나씩 체크.
  • 기존에 뭔가 보였다면 그게 내가 만든 게 맞는지부터 확인. 두 시간 동안 네이티브 shadow를 내 SVG로 착각했습니다.
  • 유저의 “안 보여”는 원인이 여러 가지. 색 대비, 위치, 잘림, 렌더 안 됨… 추측하지 말고 하나씩 검증.
  • 움직이는 효과는 집중에 반대되는 신호일 수 있다. 집중은 시끄러움이 아니라 조용함에 가깝습니다.

내일은 다른 걸 만들어야죠.