Files
2nd/10_Wiki/Topics/Coding/Frontend_SolidJS_Qwik.md
T
2026-05-09 22:47:42 +09:00

8.8 KiB

id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
id title category status source_trust_level verification_status created_at updated_at tags tech_stack applied_in aliases
frontend-solidjs-qwik SolidJS / Qwik — Reactive 후속 framework Coding draft B conceptual 2026-05-09 2026-05-09
frontend
solidjs
qwik
vibe-coding
language applicable_to
TS
Frontend
SolidJS
Qwik
Svelte 5
fine-grained reactivity
resumability
signals

SolidJS / Qwik

React 의 후속. SolidJS = signals (fine-grained), Qwik = resumability (0 hydration). React 지식 transferable + 빠름.

📖 핵심 개념

  • Signals: fine-grained reactivity (vs React 의 re-render).
  • Resumability (Qwik): 0 hydration cost.
  • Compile-time: Svelte / Solid 가 build 시 optimize.
  • React API like: 학습 비용 작음.

💻 코드 패턴

SolidJS — Signals

import { createSignal, createEffect } from 'solid-js';

function Counter() {
  const [count, setCount] = createSignal(0);
  
  createEffect(() => {
    console.log('count:', count());  // call as function
  });
  
  return <button onClick={() => setCount(c => c + 1)}>{count()}</button>;
}

→ React 비슷 but count() (call). Re-render 없음 — DOM 만 업데이트.

Solid — derived (vs useMemo)

const [first, setFirst] = createSignal('Alice');
const [last, setLast] = createSignal('Smith');

const fullName = createMemo(() => `${first()} ${last()}`);

// Or just:
const fullName = () => `${first()} ${last()}`;  // function call

return <p>{fullName()}</p>;

→ Signals 가 trigger 때만 re-compute.

Solid — Stores (object)

import { createStore } from 'solid-js/store';

const [user, setUser] = createStore({ name: 'Alice', age: 30 });

setUser('age', 31);  // immutable-style update
setUser({ name: 'Bob', age: 25 });

return <p>{user.name}, {user.age}</p>;

Solid — Show / For (vs JSX)

import { Show, For, Switch, Match } from 'solid-js';

<Show when={loggedIn()} fallback={<Login />}>
  <Dashboard />
</Show>

<For each={items()}>
  {(item) => <li>{item.name}</li>}
</For>

<Switch>
  <Match when={status() === 'loading'}>Loading...</Match>
  <Match when={status() === 'error'}>Error</Match>
  <Match when={status() === 'success'}>Done</Match>
</Switch>

→ React 의 {condition && ...} 보다 명시.

SolidStart (Next-like)

// routes/users/[id].tsx
import { createAsync, useParams } from '@solidjs/router';

export default function UserPage() {
  const params = useParams();
  const user = createAsync(() => fetchUser(params.id));
  
  return (
    <Show when={user()} fallback={<Loading />}>
      <h1>{user()!.name}</h1>
    </Show>
  );
}

// Server function
'use server';
async function fetchUser(id: string) {
  return db.user.findUnique({ where: { id } });
}

Qwik — Resumability

import { component$, useSignal } from '@builder.io/qwik';

export default component$(() => {
  const count = useSignal(0);
  
  return (
    <button onClick$={() => count.value++}>
      {count.value}
    </button>
  );
});

$ = lazy boundary. JS 가 사용자 click 까지 download 안 됨.

Qwik 의 magic

Server: HTML + serialized state.
Client: 0 JS until 사용자 interacts.
Click: 그 handler만 download + execute.

→ Massive site = first paint 즉시.

Qwik City (Next-like)

// routes/users/[id]/index.tsx
import { component$, useSignal } from '@builder.io/qwik';
import { routeLoader$ } from '@builder.io/qwik-city';

export const useUserData = routeLoader$(async ({ params }) => {
  return await db.user.findUnique({ where: { id: params.id } });
});

export default component$(() => {
  const user = useUserData();
  return <h1>{user.value.name}</h1>;
});

Solid vs React (성능)

React:    re-render entire component tree
Solid:    update only specific DOM nodes (signals)

큰 list 의 1 item 변경:
React: 전체 list virtual DOM diff
Solid: 그 1 item DOM 만 update

→ Solid 가 5-10x 빠름 자주.

Bundle size

React + ReactDOM: ~45 KB (gzip)
Solid:            ~7 KB
Preact:           ~3 KB
Svelte:           ~3 KB (compile)
Qwik:             5 KB initial (lazy 더 download)

→ Mobile / slow network = 큰 차이.

Svelte 5 (Runes)

<script>
  let count = $state(0);
  let doubled = $derived(count * 2);
  
  $effect(() => {
    console.log('count:', count);
  });
</script>

<button onclick={() => count++}>{count}</button>
<p>Doubled: {doubled}</p>

→ Compile-time. 작은 bundle.

Migration React → Solid (점진)

1. Solid 가 같은 mental model (component, props, state).
2. Hook 이름 다름 — useState → createSignal.
3. setState set 함수 — 같음.
4. JSX 같음.
5. 학습 1-2 day.

React → Qwik

Qwik 가 React 비슷 + $ boundary.
큰 차이: serializable state.

Suspense (data loading)

// Solid
import { Suspense, ErrorBoundary } from 'solid-js';

<ErrorBoundary fallback={<Error />}>
  <Suspense fallback={<Loading />}>
    <UserList />
  </Suspense>
</ErrorBoundary>
// Qwik
<Resource
  value={users}
  onPending={() => <Loading />}
  onResolved={(users) => <UserList users={users} />}
/>

Routing

Solid: @solidjs/router (file-based + dynamic)
Qwik:  @builder.io/qwik-city (file-based)
React: TanStack Router / Next App Router

State management

Solid:
- Signals (built-in)
- Stores (object)

Qwik:
- useSignal / useStore

→ External state (Redux 등) 보통 안 필요.

Form

// Solid
import { createSignal } from 'solid-js';

function Form() {
  const [email, setEmail] = createSignal('');
  
  return (
    <form onSubmit={(e) => { e.preventDefault(); submit(email()); }}>
      <input value={email()} onInput={(e) => setEmail(e.currentTarget.value)} />
    </form>
  );
}

Server actions (Qwik)

import { routeAction$, Form } from '@builder.io/qwik-city';

export const useCreateUser = routeAction$(async (data) => {
  return db.user.create({ data });
});

export default component$(() => {
  const action = useCreateUser();
  return (
    <Form action={action}>
      <input name="email" />
      <button>Submit</button>
    </Form>
  );
});

CSS / styling

// Solid + Tailwind
<div class="p-4 rounded">...</div>

// Solid + CSS module
import styles from './Card.module.css';
<div class={styles.card}>...</div>

Animation (Solid Motion)

import { Motion, Presence } from 'solid-motionone';

<Presence>
  <Show when={visible()}>
    <Motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}>
      Content
    </Motion.div>
  </Show>
</Presence>

Test

import { render } from '@solidjs/testing-library';
import { Counter } from './Counter';

test('increments', () => {
  const { getByRole } = render(() => <Counter />);
  const button = getByRole('button');
  expect(button).toHaveTextContent('0');
  button.click();
  expect(button).toHaveTextContent('1');
});

Production usage

SolidJS:
- Codeium (AI), Builder.io
- 작지만 성장

Qwik:
- Builder.io
- 새로움

Svelte:
- Bloomberg, NYTimes, Apple
- 큰 ecosystem

→ React 가 dominant — but alternative 가치.

Why migrate?

React 가 "충분히 빠름" 인 경우:
- 작은 / medium app
- 익숙한 팀

다른 framework 가치:
- Massive content site (Qwik)
- Performance critical (Solid)
- 작은 bundle (Svelte)
- 학습 / 호기심

Deno / Bun 호환

모두 Node + Deno + Bun OK.
SolidStart / Qwik City = Vite 기반 — modern.

Server / SSR

SolidStart:  SSR + 점진 hydration
Qwik City:   resumability (0 hydration)
SvelteKit:   SSR + 점진 hydration

→ Qwik 의 resumability = 가장 modern.

Adopt hesitation

- 작은 community → Stack Overflow 답 적음
- 일부 lib X (React 보다)
- Hire 어려움 (사람 React 더 많음)
- 점차 변경 — but learning curve

Trial 권장

1. 작은 side project — Solid / Qwik 시도
2. Marketing site — Astro + Solid island
3. 적합 발견 시 main project 도

→ 점진. Risk 작음.

🤔 의사결정 기준

상황 추천
Performance critical SolidJS
큰 content + 작은 interaction Qwik
작은 bundle + 단순 Svelte 5
일반 / 큰 ecosystem React
Migration React Solid (가장 비슷)
New project (호기심) Solid 또는 Qwik

안티패턴

  • Solid signal 가 React state 같은 가정: rendering 다름.
  • Qwik $ 잊음: lazy boundary 안 됨.
  • 모든 거 signal: 의미 없음. local state.
  • Hire 무 plan: 몇 명 만 알 = bus factor.
  • Big rewrite: 점진 migration 더 안전.
  • React lib 가정: 다른 ecosystem.

🤖 LLM 활용 힌트

  • React 알면 Solid 쉬움 (1-2 day).
  • Signal = fine-grained reactivity.
  • Qwik = resumability (0 hydration).
  • Svelte = compile-time.

🔗 관련 문서