블로그

React의 Optimistic UI: 의도적으로 거짓말하는 인터페이스

'좋아요' 버튼은 서버가 응답하기도 전에 즉시 반응하며, 하트가 채워지고 카운터가 올라갑니다. 이는 마이크로 인터랙션 글에서 다룬 속도의 착시가 아니라, 그보다 한 걸음 더 나아간 것입니다. 인터페이스가 아직 존재하지 않는 상태를 보여주며, 그것이 곧 실현될 것이라고 가정하는 것입니다. 이를 안전하게 만드는 법 – 가정이 틀렸을 때 그 거짓말에서 실제로 되돌아 나오는 방법 – 그리고 수동 setState와 catch가 useOptimistic이 제공하는 것보다 왜 열등한 버전인지 살펴봅니다.

CSS @layer: 캐스케이드 레이어가 특이성 전쟁을 끝내는 방법

유틸리티 클래스 .mt-4는 나중에 추가되었으니 논리적으로는 이겨야 할 것 같은데도 .card .card__header .card__title 규칙에 집니다. 이는 우연이 아니라 특이성이 설계된 대로 정확히 작동하고 있는 것입니다. @layer는 특이성과 함께가 아니라 그보다 앞서 작동하는, 충돌 해결의 완전히 새로운 축을 도입합니다 – 그리고 어찌나 직관에 어긋나는지, 웹상의 일부 문서조차 잘못 설명하는 함정이 하나 있습니다.

Next.js Server Actions: JavaScript가 로드되기도 전에 작동하는 폼

'use server'는 API 라우트로의 fetch를 위한 문법적 설탕처럼 보입니다 – 그리고 바로 그 이유로 대부분의 구현이 이를 그냥 함수 호출처럼 다룹니다. 이는 구체적인 결과를 초래하는 실수입니다. 'use server'가 붙은 모든 함수는 네트워크상의 공개 엔드포인트가 되며, 이 메커니즘 위에 지어진 폼은 JavaScript가 로드되기도 전에 작동합니다. 이 지시어 아래에서 실제로 무슨 일이 일어나는지, 그리고 문서만 봐서는 한눈에 보이지 않는 함정이 어디에 있는지 살펴봅니다.

CSS :has() — 안에서 무슨 일이 일어나는지 아는 부모

폼 필드는 내부의 input이 유효하지 않을 때 빨간색으로 강조되어야 합니다. 간단해 보이는 일입니다 – 다만 CSS는 25년 동안 요소에게 그 내부에서 무슨 일이 일어나고 있는지 물어볼 방법이 없었고, 오직 그 반대 방향만 가능했습니다. :has()가 이 방향을 어떻게 뒤집는지, 일반적인 결합자와 어떻게 다른지, 그리고 이 새로운 힘이 실제로 성능에 어떤 대가를 치르게 하는지 살펴봅니다.

컨테이너 쿼리: 뷰포트가 아니라 자신의 컨테이너를 아는 컴포넌트

같은 상품 카드가 그리드에서는 근사하게 보이지만, 미디어 쿼리를 정성껏 짜놓았음에도 좁은 사이드바에서는 무너집니다. 문제는 코드에 있는 게 아니라 미디어 쿼리가 던지는 질문 자체에 있습니다 – 화면의 너비를 묻는 것이지, 컴포넌트가 실제로 받은 공간을 묻는 게 아니라는 것이죠. 컨테이너 쿼리가 이 질문을 원래 있어야 할 자리로 옮기는 방법과, 요소가 '쿼리 컨테이너'가 될 때 내부에서 실제로 무슨 일이 벌어지는지 살펴봅니다.

스크린 리더를 위한 접근 가능한 컴포넌트 만들기

올바른 HTML은 시작점일 뿐이다. 접근성 트리가 실제로 어떻게 동작하는지, ARIA가 생각보다 훨씬 자주 오히려 해가 되는 이유는 무엇인지 살펴보고, 실제로 동작하는 아코디언과 라이브 리전, Next.js 포커스 처리를 함께 만들어본다.