'좋아요' 버튼은 서버가 응답하기도 전에 즉시 반응하며, 하트가 채워지고 카운터가 올라갑니다. 이는 마이크로 인터랙션 글에서 다룬 속도의 착시가 아니라, 그보다 한 걸음 더 나아간 것입니다. 인터페이스가 아직 존재하지 않는 상태를 보여주며, 그것이 곧 실현될 것이라고 가정하는 것입니다. 이를 안전하게 만드는 법 – 가정이 틀렸을 때 그 거짓말에서 실제로 되돌아 나오는 방법 – 그리고 수동 setState와 catch가 useOptimistic이 제공하는 것보다 왜 열등한 버전인지 살펴봅니다.
유틸리티 클래스 .mt-4는 나중에 추가되었으니 논리적으로는 이겨야 할 것 같은데도 .card .card__header .card__title 규칙에 집니다. 이는 우연이 아니라 특이성이 설계된 대로 정확히 작동하고 있는 것입니다. @layer는 특이성과 함께가 아니라 그보다 앞서 작동하는, 충돌 해결의 완전히 새로운 축을 도입합니다 – 그리고 어찌나 직관에 어긋나는지, 웹상의 일부 문서조차 잘못 설명하는 함정이 하나 있습니다.
'use server'는 API 라우트로의 fetch를 위한 문법적 설탕처럼 보입니다 – 그리고 바로 그 이유로 대부분의 구현이 이를 그냥 함수 호출처럼 다룹니다. 이는 구체적인 결과를 초래하는 실수입니다. 'use server'가 붙은 모든 함수는 네트워크상의 공개 엔드포인트가 되며, 이 메커니즘 위에 지어진 폼은 JavaScript가 로드되기도 전에 작동합니다. 이 지시어 아래에서 실제로 무슨 일이 일어나는지, 그리고 문서만 봐서는 한눈에 보이지 않는 함정이 어디에 있는지 살펴봅니다.
폼 필드는 내부의 input이 유효하지 않을 때 빨간색으로 강조되어야 합니다. 간단해 보이는 일입니다 – 다만 CSS는 25년 동안 요소에게 그 내부에서 무슨 일이 일어나고 있는지 물어볼 방법이 없었고, 오직 그 반대 방향만 가능했습니다. :has()가 이 방향을 어떻게 뒤집는지, 일반적인 결합자와 어떻게 다른지, 그리고 이 새로운 힘이 실제로 성능에 어떤 대가를 치르게 하는지 살펴봅니다.
같은 상품 카드가 그리드에서는 근사하게 보이지만, 미디어 쿼리를 정성껏 짜놓았음에도 좁은 사이드바에서는 무너집니다. 문제는 코드에 있는 게 아니라 미디어 쿼리가 던지는 질문 자체에 있습니다 – 화면의 너비를 묻는 것이지, 컴포넌트가 실제로 받은 공간을 묻는 게 아니라는 것이죠. 컨테이너 쿼리가 이 질문을 원래 있어야 할 자리로 옮기는 방법과, 요소가 '쿼리 컨테이너'가 될 때 내부에서 실제로 무슨 일이 벌어지는지 살펴봅니다.