마이크로 인터랙션에 관한 글에서 저는 transform과 opacity가 컴포지터 스레드에서 애니메이션되므로, 아무리 무거운 페이지라도 버튼 애니메이션이 끊겨서는 안 된다고 썼습니다. 이는 여전히 사실입니다 – 다만 그 글이 침묵했던 한 가지 단서가 있습니다. 메인 스레드가 긴 JS 작업으로 막혀 있다면, 애니메이션은 시작할 기회조차 얻지 못할 수 있습니다. 클릭 핸들러 자체가 대기열에서 기다리고 있기 때문입니다. INP는 정확히 이 틈을 측정하는 지표이며, scheduler.yield()는 그 틈을 실제로 메울 수 있는 몇 안 되는 방법 중 하나입니다.
댓글 천 개짜리 목록이 느리게 렌더링되어 react-window를 꺼내 듭니다 – 그런데 부드러운 스크롤을 얻는 대가로 Ctrl+F, 페이지 인쇄, 그리고 스크린 리더 탐색 일부를 잃게 됩니다. 화면 밖 요소들이 DOM에서 물리적으로 사라져버리기 때문입니다. content-visibility는 같은 문제를 다른 방식으로 풉니다 – 요소는 DOM에 그대로 남아 있고, 브라우저는 화면 근처에 오기 전까지 비용이 큰 레이아웃과 페인트 작업만 건너뜁니다. 그리고 놀랍게도, 그 안의 텍스트를 검색할 때는 일시적으로 다시 드러내 줄 수도 있습니다.
접근성 글에서 다룬, display:none으로 감췄지만 접근성 트리에는 여전히 남아 있던 모달은 div로 처음부터 직접 만든 모달에서 비롯된 실수의 한 예였습니다. 진짜 질문은 애초에 왜 그것을 처음부터 만들어야 했는가입니다. 네이티브 <dialog>와 popover 속성이 공짜로 제공하는 것 – 톱 레이어, 포커스 트랩, light dismiss – 이 무엇인지, 그리고 이 두 메커니즘이 서로 헷갈리면 곧바로 접근성이 떨어지는 인터페이스로 이어질 만큼 다른 지점이 어디인지 살펴봅니다.
'좋아요' 버튼은 서버가 응답하기도 전에 즉시 반응하며, 하트가 채워지고 카운터가 올라갑니다. 이는 마이크로 인터랙션 글에서 다룬 속도의 착시가 아니라, 그보다 한 걸음 더 나아간 것입니다. 인터페이스가 아직 존재하지 않는 상태를 보여주며, 그것이 곧 실현될 것이라고 가정하는 것입니다. 이를 안전하게 만드는 법 – 가정이 틀렸을 때 그 거짓말에서 실제로 되돌아 나오는 방법 – 그리고 수동 setState와 catch가 useOptimistic이 제공하는 것보다 왜 열등한 버전인지 살펴봅니다.
'use server'는 API 라우트로의 fetch를 위한 문법적 설탕처럼 보입니다 – 그리고 바로 그 이유로 대부분의 구현이 이를 그냥 함수 호출처럼 다룹니다. 이는 구체적인 결과를 초래하는 실수입니다. 'use server'가 붙은 모든 함수는 네트워크상의 공개 엔드포인트가 되며, 이 메커니즘 위에 지어진 폼은 JavaScript가 로드되기도 전에 작동합니다. 이 지시어 아래에서 실제로 무슨 일이 일어나는지, 그리고 문서만 봐서는 한눈에 보이지 않는 함정이 어디에 있는지 살펴봅니다.