원문: https://www.jonoalderson.com/conjecture/its-time-for-modern-css-to-kill-the-spa/

네이티브 CSS 트랜지션은 클라이언트 사이드 라우팅의 가장 강력한 근거를 조용히 무너뜨렸습니다. 그런데도 사람들은 성능 좋은 웹사이트 대신 끔찍한 앱을 계속 만들고 있습니다.
앱처럼 보여야 한다는 착각
"앱처럼 느껴지게 만들어 주세요."
기획 단계 어딘가에서 누군가 이 말을 꺼냅니다. CMO일 수도, 디지털 리드일 수도, 브랜드 매니저일 수도 있습니다. 그리고 이 한마디로 아키텍처가 결정됩니다. SPA로 가겠다고요. 아마 리액트겠죠. 뷰일 수도 있습니다. 거의 확실히 Vercel이나 Netlify에 배포될 것이고, 헤드리스 CMS와 GraphQL API도 곁들여질 겁니다.
하지만 이 결정은 사실 아키텍처에 관한 것이 아니었습니다. 성능이나 확장성, 콘텐츠 관리에 관한 것도 아니었습니다. 그저 _인터렉션_에 관한 것이었습니다. 사이트를 이리저리 클릭할 때 어떤 느낌인지에 관한 것이었습니다.
가정은 단순했습니다. 매끄러운 내비게이션을 구현하려면 앱을 만들어야 한다는 것이었습니다.
이 가정은 이제 낡았습니다.
SPA의 거짓 약속
SPA가 기본 선택지가 된 이유는 더 _뛰어나서_가 아닙니다. 한동안, 부드러운 느낌을 줄 수 있는 유일한 방법이었기 때문입니다. 페이지 사이에 화면이 하얗게 번쩍이거나 스크롤 위치가 뒤틀리지 않는 경험을 제공하는 유일한 방법이었습니다.
하지만 현실은 조금 다릅니다. 대부분의 SPA는 약속한 매끄러움을 실제로 제공하지 못합니다.
실제로 얻게 되는 것은 이렇습니다.
- 페이지 전환이 매끄러워 보이지만, 사실 두 개의 로딩 상태 사이를 페이드하는 것에 불과함
- 깨진 스크롤 복원
- 일관되지 않은 포커스 동작
- 스크립트가 컴포넌트를 리하이드레이트하는 동안 지연되는 내비게이션
- 레이아웃 시프트, 콘텐츠가 뜬금없이 나타나거나, 전체 페이지 스켈레톤
- 효과에 비해 터무니없는 성능 저하
이론적인 이야기가 아닙니다. Next.js, Gatsby, Nuxt로 만든 대부분의 사이트를 보세요. 네이티브 내비게이션을 흉내 내기 위해 킬로바이트 단위(종종 메가바이트 단위)의 자바스크립트를 전송하고 있습니다. 라우팅 로직, 하이드레이션 코드, 로딩 스피너 등 전부 브라우저가 이미 네이티브로 할 줄 알았던 것을 짜깁기하려는 것뿐입니다.
매끄러움 대신 시뮬레이션을 얻게 됩니다. 빠르고 안정적이며 SEO 친화적인 경험 대신, 우리가 버린 네이티브 동작을 재현하려는 무거운 자바스크립트 기계를 얻게 됩니다.
우리는 "빠르게 느껴지게" 만들려고 산더미 같은 JS를 쌓아 올리면서, 모든 것을 더 느리게 만들고 있었습니다.
여담으로 이 주제를 JavaScript broke the web에서 더 깊이 다루었습니다. JS 중심 개발에 대한 집착이 어떻게 웹의 기반을 적극적으로 잠식하는지 정리한 글입니다.
웹은 성장했습니다
우리가 자바스크립트로 내비게이션을 재발명하느라 바쁜 동안, 플랫폼은 그 사이 조용히 문제를 해결했습니다.
모던 브라우저, 구체적으로 크롬이나 에지 같은 크로미움 기반 브라우저는 이제 네이티브 선언적 페이지 트랜지션을 지원합니다. View Transitions API를 사용하면 자바스크립트 한 줄 없이도 두 문서 사이, 전체 페이지 내비게이션까지 애니메이션 처리할 수 있습니다.
네, 정말입니다.
여기서 말하는 "모던 CSS"란 View Transitions, Speculation Rules, 그리고 내비게이션, 인터렉션, 레이아웃을 처음부터 처리하도록 설계된 네이티브 브라우저 기능으로 돌아가는 것을 통칭하는 표현입니다. 이런 기능들은 자바스크립트로 브라우저를 다시 작성하지 않고도 풍부하고 매끄러운 경험을 구축할 수 있게 해줍니다.
여담 — CSS는 선언적이고, 탄력적이며, 표현력이 풍부하고, 확장 가능하며, 점점 더 직관적입니다. 평범한 HTML을 작성할 줄 아는 사람이라면 누구나 다룰 수 있습니다. 이런 구조적 명료함은 Why semantic HTML still matters에서 주장한 내용을 뒷받침합니다. 깔끔하고 의미 있는 마크업이 성능, 유지보수성, 기계 가독성의 기반이라는 것입니다.
이제 다음이 가능합니다.
- 페이지 간 페이드 전환
- 공유 요소 애니메이션 (예: 섬네일 → 상품 상세)
- 헤더나 내비게이션 바 같은 지속 요소 유지
- 실제 URL, 실제 페이지 로드, JS 라우팅 꼼수 없이 이 모든 것을 실현
구체적으로 살펴보겠습니다.
🔄 기본 크로스 페이지 페이드 트랜지션
CSS 몇 줄만으로 페이지 간 부드러운 시각적 전환을 만들 수 있습니다.
현재 페이지와 도착 페이지 양쪽에 다음을 추가합니다.
@view-transition {
navigation: auto;
}
::view-transition-old(root),
::view-transition-new(root) {
animation: fade 0.3s ease both;
}
@keyframes fade {
from { opacity: 0; }
to { opacity: 1; }
}
이게 전부입니다. 브라우저가 트랜지션을 처리합니다. 클라이언트 사이드 라우팅도, 하이드레이션도, 로딩 스피너도 필요 없습니다.
🔁 공유 요소 트랜지션
섬네일 이미지를 다음 페이지의 전체 크기 상품 이미지로 애니메이션하고 싶다면?
자바스크립트가 필요 없습니다. 양쪽 페이지의 요소에 같은 view-transition-name을 지정하기만 하면 됩니다.
상품 목록 페이지에서는 이렇게 합니다.
<a href="/product/red-shoes">
<img src="/images/red-shoes-thumb.jpg" style="view-transition-name: product-image;" />
</a>
상품 상세 페이지에서는 이렇게 합니다.
<img src="/images/red-shoes-large.jpg" style="view-transition-name: product-image;" />
브라우저가 내비게이션 사이의 요소를 매칭하고 애니메이션합니다. 위치, 스케일, 불투명도, 레이아웃까지 모두 CSS로 애니메이션할 수 있습니다.
🤖 JS 기반 트랜지션이 필요하다면?
페이지 내에서 트랜지션을 수동으로 트리거할 수도 있습니다.
document.startViewTransition(() => {
document.body.classList.toggle('dark-mode');
});
탭 토글이나 테마 전환 같은 경우에 완벽합니다. 프레임워크나 하이드레이션 레이어가 필요 없습니다.
🔮 Speculation Rules: JS 없는 즉각적인 내비게이션
View Transitions는 매끄러움을 만들어 줍니다. 그럼 빠르게 하려면 어떻게 할까요?
바로 Speculation Rules가 등장할 차례입니다. 사용자가 링크에 마우스를 올리거나 터치하는 것처럼 사용자 행동을 기반으로, 클릭하기 전에 브라우저가 전체 페이지를 미리 로드하거나 미리 렌더링할 수 있게 해줍니다.
<script type="speculationrules">
{
"prerender": [
{
"where": {
"selector_matches": "a"
}
}
]
}
</script>
결과는 어떨까요? 즉각적인 내비게이션입니다. 대기도 없고, 로딩도 없고, 스피너도 없습니다.
⚠️ 주의 사항
Speculation Rules는 성능 배율기입니다. 가벼운 사이트에서는 모든 것이 즉각적으로 느껴지게 만들어 줍니다. 하지만 페이지가 느리거나 비대하거나 JS가 무겁다면, 추측이 그 비용을 앞당겨 지불하게 할 뿐입니다.
사이트가 비대하다면, 추측은 여전히 추측할 것이며 그 대가는 사용자가 치릅니다.
이는 CPU, 네트워크 대역폭, 모바일 배터리가 낭비된다는 뜻입니다. 종종 사용자가 방문조차 하지 않을 페이지를 위해서요.
신중하게 사용해야 합니다. 빠른 사이트에서는 마법입니다. 느린 사이트에서는 함정입니다.
브라우저는 돕고 싶어합니다. 허락한다면
모던 브라우저는 어느 때보다 똑똑합니다. 속도, 반응성, 효율성을 개선할 방법을 끊임없이 찾고 있습니다. 단, 우리가 허락해야 합니다.
가장 명확한 예시 중 하나가 Back/Forward Cache(bfcache)입니다. 사용자가 뒤로가기나 앞으로가기를 할 때 전체 페이지의 스냅샷을 찍어 즉시 복원할 수 있게 해줍니다.
사실상 공짜 성능 향상입니다. 하지만 "잘 작동하는" 페이지에서만 가능합니다. 제멋대로인 자바스크립트도 없고, 가로채진 내비게이션도 없고, 생명주기의 혼란도 없어야 합니다. 깔끔한 선언적 아키텍처만 있으면 됩니다. HTML과 CSS만으로요.
당연히 잘 구조화된 멀티 페이지 사이트와 아름답게 어울립니다. 하지만 대부분의 SPA에서는 아예 사용할 수 없습니다. SPA를 정의하는 바로 그 디자인 패턴들 — 가로채진 라우팅, 클라이언트 사이드 렌더링, 복잡한 상태 관리 — 이 bfcache가 의존하는 전제를 깨뜨리기 때문입니다.
이것은 훨씬 더 큰 주제의 축소판입니다. 브라우저는 단순함과 회복탄력성에 보상하는 방향으로 진화하고 있습니다. 우리가 처음부터 받아들였어야 할 그런 웹을 위해 만들어지고 있습니다. 그리고 SPA는 점점 더 이질적인 존재가 되고 있습니다.
📊 SPA vs MPA: 성능 현실 점검
평균적인 Next.js 마케팅 사이트
- JS 번들: 1 – 3MB
- TTI: ~3.5 – 5초 (하이드레이션 전략에 따라 다름)
- 라우트 전환: 시뮬레이션
- SEO: 복잡하고 취약
- 스크롤/앵커 동작: 불안정
모던 MPA + View Transitions + Speculation Rules
- JS 번들: 0KB (선택적 향상만)
- TTI: ~1초
- 라우트 전환: 실제, 네이티브
- SEO: 간단
- 스크롤/포커스/히스토리: 브라우저 기본값, 완벽
모던 CSS는 SPA의 동작을 대체하는 것에 그치지 않습니다. 능가합니다.
웹사이트를 앱처럼 만들지 마세요
대부분의 웹사이트는 앱이 아닙니다.
공유 상태가 필요 없습니다. 클라이언트 사이드 라우팅이 필요 없습니다. 모든 화면에 인터랙티브 컴포넌트가 필요하지 않습니다. 하지만 어느 순간부터 우리는 이 구분을 그만두었습니다.
이제 우리는 실시간 협업 UI를 위해 설계된 스택으로 이커머스 스토어, 문서 포털, 마케팅 사이트, 블로그를 만들고 있습니다. 미친 짓입니다.
콘텐츠 블록 여섯 개와 문의 폼 하나 있는 홈페이지에 하이드레이션, 서스펜스 바운더리, 렌더링 전략이 필요하지 않습니다.
빠른 마크업, 깔끔한 URL, 그리고 어쩌면 — 어쩌면 — 위에 얹는 약간의 인터랙티비티가 필요할 뿐입니다.
그런데도 모든 프로젝트에서 이런 일이 벌어집니다.
- 이해관계자가 "앱처럼 느껴지게 해주세요"라고 말합니다.
- 개발팀이 Next.js나 Nuxt를 꺼냅니다.
- 라우팅이 클라이언트 사이드로 넘어갑니다.
- 성능이 곤두박질칩니다.
- 이제 엣지 함수, 스트리밍, ISR, 로딩 전략, 디버깅 계획이 필요해집니다.
- 그런데도 어째서인지... 일반 링크 클릭과 CSS 애니메이션보다 여전히 느립니다.
프레임워크를 반대하자는 것이 아닙니다. 의도적으로 선택하자는 것입니다.
리액트를 쓰고 싶다면 쓰세요. Tailwind든, Vite든, 뭐든 좋습니다. 다만 필요하지 않다면 브라우저에 다 보내지 마세요.
사이트는 사이트답게 만드세요. HTML을 사용하세요. 내비게이션을 사용하세요. 플랫폼을 사용하세요.
더 빠르고, 더 단순하고, 모두에게 더 좋습니다.
우리가 가진 웹을 위해 만드세요
SPA는 일시적인 한계에 대한 영리한 해결책이었습니다. 하지만 그 한계는 더 이상 존재하지 않습니다.
이제 우리에게는 이런 것들이 있습니다.
- 실제 페이지 간 네이티브 선언적 트랜지션
- Speculation Rules를 통한 즉각적인 프리렌더링 내비게이션
- 우아한 점진적 저하(graceful degradation)
- 깔끔한 마크업, 빠른 로드, 실제 URL
- 도와주고 싶어하는 플랫폼 — 허락한다면
여전히 "매끄러움"을 위해 사이트를 SPA로 만들고 있다면, 브라우저가 이미 해결한 문제를 풀고 있는 것입니다. 그리고 그 대가를 복잡성, 성능, 유지보수성으로 치르고 있는 것입니다.
모던 서버 렌더링을 사용하세요. 실제 페이지를 사용하세요. CSS로 애니메이션하세요. 의도를 가지고 프리로드하세요. 자바스크립트를 줄이세요.
2025년답게 만드세요. 2018년 Gatsby 데모에 갇힌 것처럼 만들지 마세요.
더 빠른 사이트, 더 행복한 사용자, 그리고 더 적은 후회를 얻게 될 것입니다.
'Web Frontend Developer' 카테고리의 다른 글
| [번역] CSS in 2026: 프런트엔드 개발을 바꾸는 새로운 기능들 (0) | 2026.03.27 |
|---|---|
| [번역] 디자인 엔지니어란 무엇인가? (0) | 2026.03.11 |
| [번역] 토스트 컴포넌트 만들기 (0) | 2026.02.11 |
| 프론트엔드 테스트는 꼭 필요할까? (1) | 2026.01.02 |
| [번역] package.json을 관리하는 방법 (1) | 2025.12.10 |