요약
조시 코모가 Interop 2027 투표에서 고른 CSS 신기능 10가지로 CSS의 앞날을 미리 봅니다.
개발자 조시 코모가 쓴 글입니다. 브라우저 업체들이 함께 지원 우선순위를 정하는 Interop 프로젝트의 2027년 후보 가운데 자신이 투표한 10가지를 골라 소개했습니다.
왜 중요한가
- Interop에 오른 기능은 대개 1년 안에 주요 브라우저 모두에서 쓸 수 있게 됩니다. 2023년에 오른 서브그리드는 테스트 통과율이 12%에서 91%로 뛰었습니다.
- 모질라가 만든 순위 사이트의 개발자 투표가 선정에 반영되므로, 개발자도 우선순위에 목소리를 낼 수 있습니다.
- 옛 자료로 학습한 AI는 새 기능을 잘 쓰지 못하므로, 최신 CSS를 아는 일이 오히려 더 중요해졌다고 봤습니다.
핵심 내용
@function으로 재사용할 CSS 함수를 직접 정의할 수 있으며, 지금은 Chromium 계열 브라우저만 지원합니다.corner-shape는 모서리를 squircle, bevel, notch 같은 모양으로 바꾸고, 테두리와 그림자도 그 모양을 따릅니다.interpolate-size와calc-size()를 쓰면height: auto까지 애니메이션할 수 있어 아코디언 구현이 쉬워집니다.reading-flow는 탭 이동 순서를 문서 구조가 아니라 화면에 보이는 배치 순서에 맞춥니다.- 격자 사이에 선을 긋는 간격 장식, 고정된 헤더 상태를 알아내는 스크롤 상태 컨테이너 쿼리도 꼽았습니다.
HN 반응
- 프레임워크 없이 순수 HTML·CSS·JS만으로도 충분해졌다는 공감이 많았고, 아코디언이나 읽기 진행 막대도 이제 자바스크립트 없이 만든다는 경험담이 나왔습니다.
- 반응성(데이터가 바뀌면 화면을 자동으로 고치는 기능)에는 여전히 프레임워크가 필요하다는 반론과, 사이트에 맞춘 작은 코드로 충분하다는 주장이 맞섰습니다.
저는 가능한 한 일부러 바닐라 HTML/CSS/JS만 쓰려고 하는 사람인데, 프레임워크 안에서만 작업해 온 사람들에게 요즘 이게 얼마나 좋아졌는지 설명하기가 정말 어렵습니다. 물론 작업에 맞는 도구를 쓰는 게 맞지만, 쓰다 보면 즐거워집니다!
맞아요!
저도 몇 년째 그렇게 해 왔는데, 약간 IQ 종형 곡선 밈 같아요. 왼쪽 끝과 오른쪽 끝에서는 "바닐라 HTML/CSS/JS면 충분하다"고 하거든요.
진지하게 묻는 건데, 반응성(reactivity)이나 상태 관리에서도 그게 정말 맞는 말인가요? 바닐라로도 기술적으로야 뭐든 가능하겠지만, 제가 마지막으로 확인했을 때는 자기가 뭘 하는지 제대로 알지 못하면 잘 지원되지 않는 영역이었던 것 같거든요.
저는 요즘 개인 웹사이트와 프로젝트를 만들 때, 최신 CSS만 쓰고 스타일시트 구조를 조금 고민하고 시맨틱 HTML을 쓰려고 하면서 div 수프는 만들지 않습니다. 그렇게 하면 온라인이나 직장에서 보는 대부분의 사이트보다 훨씬 반응이 빠릅니다. 제대로 쓰고 적절히 신경 쓰기만 하면 꽤 잘 돌아갑니다.
반응형(responsiveness)은 한동안 문제없었어요. 스마트폰 혁명이 벌써 거의 20년 전이니 놀랄 일도 아니죠.
상위 댓글이 물은 건 반응성(reactivity)이었어요. 그건 여전히 어떤 형태로든 프레임워크가 필요합니다. 이미 만들어진 것이든 가볍게 직접 짠 것이든요.
이 맥락에서 반응성(reactivity)이 뭔가요? 웹사이트가 얼마나 빨리 응답하느냐요? 제 경험으로는 빠른 백엔드에 서버 사이드 렌더링 HTML을 쓰고 동적인 부분을 조금 얹는 쪽이 실제로 SPA보다 훨씬 빠릅니다. 처음 Gmail을 쓴 지 20년이 지났는데, 그때의 Gmail은 빨랐고 요즘 SPA의 95%는 느려요.
"상태 관리"는 "자바스크립트가 데이터를 변수에 어떤 식으로든 저장해 두고 필요할 때마다 갱신한다"를 거창하게 부르는 말입니다.
"반응성"은 "그 변수가 바뀌면 DOM을 즉시 갱신하는 범용 코드가 이미 있다"는 뜻입니다. 변경 사항마다 직접 해당 코드를 따로 짜는 방식과 대비되죠.
안타깝게도 React에서 "DOM을 자동으로 갱신한다"는 보통 이런 뜻입니다. 데이터를 갱신하면 React가 메모리에 DOM 전체를 새 사본으로 다시 만들고, diff 검사기를 돌려 실제 DOM과 무엇이 달라졌는지 알아낸 다음, 실제 DOM에서 변경 사항을 하나하나 적용합니다. React(또는 가상 DOM 중심의 다른 대안들)를 아무리 작은 웹 프로젝트에도 쓰다 보니, 과정이 "데이터를 갱신하고, DOM을 직접 갱신한다" 정도로 단순해야 할 수많은 작은 사이트가 이 모든 일을 하고 있습니다.
결국 경험이 부족한 개발자들이 눈앞의 일에 어떤 도구가 최선인지 따져 보지 못하고, 모든 웹 프로젝트에 똑같이 망치질만 하는 문제입니다.
프레임워크를 더 나은 걸로 바꾸기만 하면 되잖아요. Vapor 모드의 Vue, Svelte, Solid, Preact 같은 거요. 이런 것들은 VDOM이 아예 없거나 아주 빠르고 최소한의 VDOM만 씁니다.
시그널은 이벤트 핸들러를 직접 짜는 것만큼 빠르면서 훨씬 덜 깨지고 덜 장황해요. React는 아주 인기가 많고 아주 느려서, 모든 JS 프레임워크에 대한 사람들의 인상을 혼자 좌우하고 있을 뿐입니다.
저도 예전엔 React에 푹 빠져 있었는데, Svelte를 써 본 뒤로는 돌아간 적이 없어요.
반응성은 기반 데이터가 갱신될 때마다 DOM을 갱신하는 것입니다. 모든 JS 프레임워크가 존재하는 가장 기본적인 이유 중 하나죠. 이를 네이티브로 가져오자는 TC39 Signals 제안이 있지만, 지금은 위에서 말한 대로 프레임워크를 쓰거나 직접 하나 만드는 수밖에 없습니다. 요즘은 Proxy를 쓰면 놀랄 만큼 쉽습니다.
DOM을 반응형으로 갱신하는 건 예전부터 늘 쉬웠습니다. 저는 HTML5가 표준화되기도 전에 바닐라 JS로 그렇게 했어요.
프레임워크가 하는 일은 이 개념을 한 단계 더 밀어붙여 SPA(싱글 페이지 애플리케이션)를 만드는 건데, 솔직히 저는 그게 사라져야 할 안티패턴이라고 생각합니다.
문제는 주로 소유권과 출처(provenance), 그리고 모든 DOM 접근이 느리고 내부적으로 동기화 코드가 필요하다는 점입니다.
노드를 만지거나 템플릿을 쓰는 코드는 누구나 짤 수 있지만, 코드가 수십만 줄인 프로젝트에서 이를 일관되게 하기는 어렵습니다. 성능도 필요하고, 모두가 제일 싫어하는 막내인 접근성까지 챙겨야 한다면 더 그렇죠.
물론 프레임워크를 써도 이 중 일부는 해결되지 않지만, 상당히 도움이 됩니다.
네, 프레임워크가 규모가 커지는 프로젝트를 위한 표준 인터페이스를 제공하려는 것이라는 점은 분명하죠. 하지만 지금 말씀하신 건 처음에 논쟁하던 것과는 다른 문제입니다.
저는 일종의 옵저버 패턴을 써서 상태 변화에 리스너를 등록하는 식으로 해 왔습니다.
상태가 갱신될 때마다 모든 리스너가 갱신된 상태를 받아 호출되고, 새 상태를 바탕으로 UI 등을 갱신할 수 있습니다.
프로파일링해서 프레임워크와 비교해 본 적은 없지만, 제가 만든 비교적 단순한 UI에는 충분하다고 느낍니다.
어떤 면에서는 모든 프레임워크가 거기서 출발하죠.
대부분의 웹 앱과 웹사이트에는 상태 변수가 그렇게 많지 않습니다(데이터 양은 많을 수 있지만요). 그래서 React가 개발 속도를 조금 높여 줄 수는 있어도, 그 이득은 뭔가를 빌드하고 배포 파이프라인 전체를 돌봐야 하는 비용에 다 먹힙니다.
HTMX나 Alpine 같은 라이브러리는 예전 방식의 명령형 패턴에 필요한 보일러플레이트 대부분을 처리해 줄 수 있습니다. 그리고 Rails, Phoenix, Laravel 같은 백엔드 프레임워크에도 각자 데이터 갱신 구현이 있습니다.
제가 추천 버튼을 누르면 숫자가 0에서 1로 바뀝니다. 페이지의 다른 곳에는 추천을 받았다는 배너가 표시되고요.
자바스크립트로 그 두 div를 직접 지정해서 doc.getElementById(counter).innerHtml이나 document.createElement 같은 걸 쓸 수 있습니다.
단순한 경우에는 그럭저럭 통합니다. 하지만 금세 복잡해져요. 반응성이란 카운터가 카운트의 변화를 감지하고 있다가 카운트가 바뀔 때마다 스스로를 갱신하는 것입니다.
그래서 그 두 div를 직접 지정하지 않고, div를 갱신하는 전용 지점을 하나 두고 그 두 div를 갱신해야 하는 다른 모든 곳에서 그 지점을 호출하는 겁니다.
물론 상황을 단순화한 얘기고, 사이트에 이런 상황이 점점 더 많아지면 어느 시점에는 중재자(mediator) 패턴으로 넘어갈 수도 있습니다. 이건 시그널을 내보내고 그 시그널을 듣는 것과 꽤 비슷합니다.
이 경우라면 저는 "추천을 받았다" 배너를 display:none 클래스를 붙여 DOM에 미리 넣어 두겠습니다.
저는 카운터와 배너의 코드가 같은 자리에 있다는 게 큰 장점이라고 봅니다. thisCounter++; thisBanner.className = "shown"; 이렇게요.
... 그런데 DOM을 쓰는 바닐라 JS에는 이미 이벤트 리스너가 있잖아요...?
그래도 리스너를 설정하고 해제해야 하고, 서로 떨어진 곳들의 화면 표시도 갱신해야 합니다.
갱신할 게 몇 개 안 될 때는 잘 되지만, 금방 감당이 안 됩니다.
바닐라 JS도 내장 메서드만으로 반응성을 아주 잘 처리합니다.
저도 직장에서보다 훨씬 효율적으로 만듭니다.
그런데 실용성은 없어요.
컴퓨팅 자원은 충분하고 거의 공짜입니다.
그냥 예술 프로젝트일 뿐이죠.
종형 곡선 밈의 오른쪽 끝에는 자기가 뭘 하는지 제대로 안다는 전제가 암묵적으로 깔려 있죠 :-)
아니요, 다른 댓글들이 뭐라고 하든 그렇지 않습니다. 물론 가능은 하지만 그러다 보면 결국 React나 다른 라이브러리를 재현하게 됩니다. JS에 네이티브 시그널이 들어올 날이 언젠가 오겠지만, 지금은 순수 JS만으로는 복잡한 완성형 앱을 만들기에 충분하지 않습니다.
> 그러다 보면 결국 React나 다른 라이브러리를 재현하게 됩니다
늘 나오는 단골 대답이죠. 대개 다른 사람이 하는 말을 읽기만 했지, 스스로 의견을 형성할 만큼 경험하지는 못한 사람들이 읊습니다.
거대하고 비대하고 느리며, 모든 사람에게 모든 것이 되려고 최대한 범용으로 만든 프레임워크를 결코 다시 만들게 되지 않습니다. 결국 만들게 되는 건 그 사이트의 필요에 딱 맞는, 비교적 작고 빠른 코드입니다. 크기, 속도, 단순함의 차이는 대개 엄청나게 큽니다.
두 가지 모두 틀렸습니다. 저는 바닐라 JS만 쓰는 회사들에서 일했고, 그 회사들이 결국 만들게 된 프레임워크의 일부를 직접 만들었습니다. 그건 절대 "그 사이트의 필요에 딱 맞는, 비교적 작고 빠른 코드"가 아니었고, React로 바꾸고 나니 사이트 로딩 속도가 여러 부분에서 실제로 좋아졌습니다. 다음엔 그 회사가 그냥 형편없었던 거라며 "진정한 스코틀랜드인은" 식 논리로 나오시겠죠. 하지만 다른 회사에서도 충분히 많은 사례를 봤기 때문에, React나 비슷한 프레임워크를 쓰는 게 그냥 옳은 선택이라고 봅니다.
리더십이 기존 반응형 프레임워크를 쓰고 싶어 하지 않았던 대규모 ERP 같은 백오피스 프로젝트에서, 저는 jQuery 위에 그걸 결국 다시 만들었습니다(프로젝트 초기에는 IE를 아직 지원해야 했거든요).
서로 다른 종류의 여러 항목을 관리하는 동적 폼, 대시보드, 오류 상태, i18n 같은 걸 추가하기 시작하면 결국 자체 제작 프레임워크가 생기거나 스파게티 코드가 잔뜩 생깁니다.
lit.dev 써 보셨나요? OpenJS 재단(옛 jQuery 재단과 Node.js 재단)에 속해 있습니다.
동의합니다. 설명하기 어려운 이유 중 하나는 많은 사람이 flexbox가 막 나왔을 때의 CSS를 기준으로 CSS에 대한 생각을 굳혔기 때문이라고 봅니다. 요즘 아코디언은 <details> 몇 개면 되고, 읽기 진행 표시줄은 animation-timeline: scroll()을 쓴 고정 위치 div 하나면 됩니다. 둘 다 JS가 필요 없습니다.
(솔직히 밝히자면 제가 이 주제로 책을 썼기 때문에 중립적이진 않습니다: https://theosoti.com/you-dont-need-js/ )