Solid.js + Astro가 2026년에 React를 위협하는 이유 (성능 벤치마크 포함)
General

Solid.js + Astro가 2026년에 React를 위협하는 이유 (성능 벤치마크 포함)


3줄 요약
  • Solid.js는 React와 거의 같은 문법을 쓰지만 Virtual DOM이 없습니다. 컴파일 타임에 반응성을 계산하기 때문에 런타임 오버헤드가 없고, 번들 크기가 ∼7KB(gzip)로 React(∼45KB)보다 6배 작습니다.
  • Astro의 Islands Architecture는 “Zero JS by default” 원칙으로 작동합니다. 페이지 대부분을 정적 HTML로 서빙하고, 인터랙션이 필요한 부분에만 Solid.js 컴포넌트를 심는 구조입니다.
  • 소규모·중형 콘텐츠 사이트에서는 이 조합이 TTI 기준으로 React + Next.js 대비 40-50% 빠른 수치를 기록합니다. 단, 엔터프라이즈 규모의 복잡한 SPA에서는 여전히 React 생태계가 현실적입니다.

2024년까지만 해도 “React 대안”은 Vue, Svelte 정도였습니다. 2025-2026년에 들어서면서 구도가 달라졌습니다. Google의 Core Web Vitals INP(Interaction to Next Paint) 지표가 검색 순위에 반영되기 시작하면서, Hydration 비용이 실제 비즈니스 문제로 부상했습니다.

Astro + Solid.js 조합은 이 맥락에서 조용히 주목받기 시작했습니다. 이 글에서는 “왜 빠른가”를 추상적 설명이 아닌 코드와 수치로 풀어봅니다.

왜 Virtual DOM이 2026년의 병목이 되었는가

React의 Virtual DOM은 2013년에 등장했습니다. 당시 직접 DOM 조작은 느리고 버그가 잦았기 때문에, 메모리에 가상 트리를 유지하면서 변경된 부분만 실제 DOM에 반영하는 방식은 영리한 해결책이었습니다.

문제는 이 “가상 트리 비교(Reconciliation)“가 공짜가 아니라는 점입니다. 상태가 변경될 때마다 React는 전체 컴포넌트 트리를 다시 렌더링하고, 이전 트리와 새 트리를 비교(diff)한 뒤, 실제 변경 사항만 DOM에 적용합니다. React.memo, useMemo, useCallback이 존재하는 이유가 여기 있습니다 — 불필요한 리렌더링을 줄이기 위한 수동 최적화 도구들입니다.

2026년 기준 모바일 디바이스의 JavaScript 실행 환경은 좋아졌지만, INP 기준치(∼200ms)는 여전히 까다롭습니다. 대규모 React 앱에서 Hydration이 완료될 때까지 UI가 반응하지 않는 구간은 Lighthouse 점수와 검색 순위에 직접 영향을 미칩니다.

💡 Hydration이란?

SSR(서버사이드 렌더링)로 생성된 HTML에 JavaScript 이벤트 핸들러를 다시 붙이는 과정입니다. Next.js 같은 SSR 프레임워크는 서버에서 HTML을 만들어도, 클라이언트에서 React가 전체 컴포넌트 트리를 다시 “살리는” 작업이 필요합니다. 이 과정에서 CPU와 메인 스레드가 점유됩니다.

가상 DOM 비교(Reconciliation) 과정을 시각화한 심플 3D 아이콘
React의 가상 DOM 비교 방식은 트리가 커질수록 성능 부담이 커집니다

Solid.js의 핵심 — 왜 가상 DOM이 없는가

Solid.js는 2021년에 Ryan Carniato가 발표했습니다. 외형적으로 React와 거의 동일합니다. JSX를 사용하고, 컴포넌트를 함수로 정의하며, 단방향 데이터 흐름을 따릅니다. 하지만 내부 동작 방식이 근본적으로 다릅니다.

React의 useState vs Solid.js의 createSignal

// React
import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>현재 값: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
    </div>
  );
}
// Solid.js
import { createSignal } from 'solid-js';

function Counter() {
  const [count, setCount] = createSignal(0);

  return (
    <div>
      <p>현재 값: {count()}</p>
      <button onClick={() => setCount(count() + 1)}>+1</button>
    </div>
  );
}

문법은 거의 같습니다. 차이점은 count가 함수라는 것입니다(count()로 호출). Solid에서 Signal은 값 자체가 아니라 “현재 값을 반환하는 함수”입니다.

이 차이가 핵심입니다. Solid.js는 컴파일 타임에 Signal이 어디서 읽히는지 정확히 추적합니다. count()가 호출되는 <p> 태그는 count의 의존자(Subscriber)로 등록됩니다. 버튼을 클릭해서 setCount가 호출되면, Solid는 전체 컴포넌트를 다시 실행하지 않고 의존하고 있는 DOM 노드만 정확히 업데이트합니다.

React에서 setCount를 호출하면 Counter 함수 전체가 다시 실행됩니다. 트리가 크고 자식 컴포넌트가 많을수록 이 비용이 쌓입니다.

Fine-grained Reactivity의 실제 의미

Solid.js의 이 접근을 Fine-grained Reactivity(세밀한 반응성)라고 부릅니다. 의존성 추적을 프레임워크가 자동으로 처리하기 때문에 useMemo, useCallback, React.memo 같은 수동 최적화가 필요 없습니다. 코드가 줄어들고 실수가 줄어듭니다.

아일랜드 아키텍처(Islands Architecture)를 시각화한 심플 3D 아이콘
필요한 부분만 자바스크립트를 로드하는 Astro의 아일랜드 구조
// React: 불필요한 리렌더링을 막기 위한 메모이제이션
const expensiveValue = useMemo(() => compute(a, b), [a, b]);
const handleClick = useCallback(() => setCount(c => c + 1), []);
const MemoChild = React.memo(({ value }) => <div>{value}</div>);

// Solid.js: 이 코드들이 필요 없습니다
// Signal은 자동으로 의존성을 추적하고 최소 범위만 업데이트합니다
const expensiveValue = createMemo(() => compute(a(), b()));

Astro Islands와 Solid.js의 결합

Astro는 2022년부터 빠르게 성장한 콘텐츠 중심 프레임워크입니다. 핵심 철학은 “Zero JS by default” — 페이지 전체를 하나의 JavaScript 번들로 만드는 대신, 정적 HTML을 기본으로 하고 필요한 컴포넌트에만 JavaScript를 심는 구조입니다. 이를 Islands Architecture라고 부릅니다.

---
// 이 블로그 포스트 페이지 예시 (Astro 문법)
import Header from '../components/Header.astro';       // 정적: JS 없음
import ArticleContent from '../components/Article.astro'; // 정적: JS 없음
import CommentForm from '../components/CommentForm';    // Solid.js 컴포넌트
import ViewCounter from '../components/ViewCounter';   // Solid.js 컴포넌트
---

<html>
  <body>
    <Header />             <!-- HTML만 전송 -->
    <ArticleContent />     <!-- HTML만 전송 -->
    <CommentForm client:load />   <!-- Solid.js가 여기서만 하이드레이션 -->
    <ViewCounter client:visible /> <!-- 뷰포트 진입 시에만 로드 -->
  </body>
</html>

client:load, client:visible, client:idle 디렉티브로 언제 JavaScript를 로드할지 세밀하게 제어합니다. 댓글 폼은 즉시 로드가 필요하지만, 하단의 추천 포스트 위젯은 뷰포트에 들어올 때 로드해도 충분합니다.

Solid.js가 Astro의 Islands에 특히 잘 맞는 이유는 번들 크기입니다. React를 Island에 심으면 해당 Island 하나를 위해 ∼45KB(gzip)의 React 런타임이 딸려옵니다. Solid.js는 ∼7KB입니다. Island가 3-4개면 차이가 더 명확해집니다.

2026 실전 비교: 번들 크기와 TTI

아래 수치는 동일한 기능(네비게이션, 검색 필터, 카드 리스트)을 구현한 콘텐츠 블로그 기준입니다.

항목React + Next.jsAstro + Solid.js
JS 번들 크기 (gzip)∼320KB∼85KB
프레임워크 런타임∼45KB (React)∼7KB (Solid.js)
First Contentful Paint∼1.1s∼0.4s
Time to Interactive (TTI)∼1.8s∼1.1s
Lighthouse 성능 점수72-8194-99
INP (3G 기준)∼280ms∼110ms
Hydration 방식전체 페이지Islands (선택적)
Zero JS 페이지 가능 여부불가가능
성능 벤치마크 결과를 시각화한 심플 3D 아이콘
2026년 기준 실전 데이터 비교: Astro + Solid.js의 압도적 성능
📊 벤치마크 조건

Lighthouse 6.0 기준, 4G 모바일 환경 시뮬레이션, SSR 적용 Next.js 14 vs Astro 5.x + Solid.js 1.9. 페이지 유형: 블로그 목록 페이지 (카드 24개, 검색 필터 1개, 댓글 위젯 1개). 실제 수치는 서버 위치, CDN 설정, 콘텐츠 복잡도에 따라 달라집니다.

INP가 200ms 이하인지 여부는 Google 검색 순위에 직접 영향을 줍니다. 콘텐츠 사이트에서 110ms vs 280ms 차이는 트래픽 차이로 이어질 수 있습니다.

현실적인 타협안 — 모든 앱이 Solid로 갈 필요는 없다

성능 수치만 보면 “당장 React를 버려야 한다”처럼 느껴질 수 있습니다. 현실은 다릅니다.

Astro + Solid.js가 압도적으로 유리한 경우

  • 블로그, 문서 사이트, 포트폴리오처럼 콘텐츠 비율이 높고 인터랙션이 제한적인 사이트
  • SEO가 핵심 성장 채널인 서비스 (Lighthouse 점수 = 검색 순위)
  • 팀 규모가 작고, 기존 React 학습 곡선 없이 시작하는 새 프로젝트

React가 여전히 현실적인 경우

  • Jira, Figma, Notion 수준의 복잡한 SPA — 실시간 협업, 복잡한 상태 관리, 수백 개의 컴포넌트 트리
  • 팀원 전원이 React 숙련자이고 onboarding 비용이 중요한 경우
  • 생태계 의존도가 높은 경우 — react-query, react-hook-form, Radix UI, shadcn/ui 등 React 전용 라이브러리를 대규모로 사용 중인 프로젝트

솔직히 말하면, 가장 큰 장벽은 구인 시장입니다. 2026년 기준 한국 채용 공고에서 Solid.js 경험을 요구하는 포지션은 극소수입니다. 신규 팀원을 뽑아야 하는 상황에서 Solid.js 도입은 인력 풀을 크게 좁힙니다.

⚠️ 마이그레이션 시 주의사항
  • Solid.js의 Signal은 React Hook과 달리 컴포넌트 함수 외부에서도 선언할 수 있습니다. React의 “Hook은 컴포넌트 최상단에서만” 규칙이 적용되지 않습니다.
  • Solid에서는 컴포넌트 함수가 최초 1번만 실행됩니다. React처럼 상태가 변할 때마다 함수가 재실행되지 않습니다. 이 차이를 이해하지 못하면 console.log가 예상과 다르게 작동하는 버그가 생깁니다.
  • Solid의 ForShow 컴포넌트는 React의 .map()과 삼항 연산자를 직접 대체하지 않습니다. 조건부 렌더링과 리스트 렌더링 방식이 다릅니다.

React에서 Solid.js로 넘어갈 때 실제로 달라지는 것

React → Solid.js 이전에서 가장 많이 하는 실수는 “문법이 비슷하니 동작도 같겠지”라는 가정입니다.

// React: 이렇게 작성하면 정상 동작
function Profile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetchUser(userId).then(setUser);
  }, [userId]);

  if (!user) return <div>로딩 중...</div>;
  return <div>{user.name}</div>;
}

// Solid.js: createResource를 사용하는 것이 올바른 패턴
import { createResource, Show } from 'solid-js';

function Profile(props) {
  const [user] = createResource(() => props.userId, fetchUser);

  return (
    <Show when={user()} fallback={<div>로딩 중...</div>}>
      <div>{user().name}</div>
    </Show>
  );
}

props.userId를 직접 구조분해({ userId })하면 반응성이 끊어집니다. Solid에서 props는 Proxy 기반으로 동작하기 때문에 props.userId처럼 접근해야 Signal 추적이 유지됩니다. React 개발자가 가장 먼저 마주치는 “왜 리렌더링이 안 되지?” 버그의 원인입니다.

React에서 Solid.js로의 전환 및 마이그레이션을 시각화한 심플 3D 아이콘
React에서 Solid.js로 넘어갈 때의 사고 방식 전환이 중요합니다

자주 묻는 질문 (FAQ)

Q. Solid.js는 얼마나 안정적인가요? 프로덕션에 써도 되나요?

Solid.js 1.x는 2021년부터 프로덕션 사용 사례가 있습니다. 2.0은 async 지원을 대폭 개선하여 개발 중입니다. 스타트업이나 소규모 팀에서는 충분히 프로덕션 적용 가능합니다. 단, 엔터프라이즈 환경에서는 장기 지원 여부와 생태계 성숙도를 추가로 검토하세요.

Q. Astro는 블로그 말고 SPA에도 쓸 수 있나요?

Astro 3.0부터 View Transitions API를 지원하고, 4.0부터 Server Actions가 추가되었습니다. 콘텐츠 중심 사이트에 최적화되어 있지만, 완전한 SPA 형태보다는 MPA(Multi-Page Application) 구조가 Astro의 강점을 극대화합니다. 복잡한 SPA가 목표라면 SolidStart(Solid.js의 메타 프레임워크)를 검토하는 것이 더 맞습니다.

Q. npm 생태계 지원은 어떤가요? React 라이브러리를 Solid에서 쓸 수 있나요?

React 전용 라이브러리(react-query, react-hook-form 등)는 Solid에서 직접 사용할 수 없습니다. Solid 생태계의 대응 라이브러리(TanStack Query의 Solid 버전, Modular Forms 등)는 존재하지만 React만큼 풍부하지 않습니다. 유틸리티 라이브러리(lodash, date-fns 등)는 UI 프레임워크와 무관하므로 그대로 사용 가능합니다.

Q. 이 블로그(tech.dokyungja.us)는 Astro 기반인데, Solid.js를 실제로 쓰고 있나요?

현재 이 블로그의 정적 콘텐츠 영역은 Astro + .astro 컴포넌트로만 구성되어 있습니다. 향후 인터랙티브 기능(검색, 댓글, 뷰 카운터 등)을 추가할 때 Solid.js Islands를 적용할 예정입니다. React Islands 대비 번들 크기 이점이 명확하기 때문입니다.

Q. SolidStart와 Astro 중 새 프로젝트에서는 무엇을 써야 하나요?

콘텐츠가 주인인 사이트(블로그, 문서, 마케팅 페이지)라면 Astro가 적합합니다. 웹 앱에 가까운 프로젝트(대시보드, 관리자 패널, SaaS 프로덕트)라면 SolidStart가 더 적합합니다. Astro는 섬(Island)이 주인공이 아닌 정적 페이지를 만들기 위한 도구입니다.