Qwik 완벽 가이드 | Resumability·초고속 로딩·Zero Hydration·실전 활용

Qwik 완벽 가이드 | Resumability·초고속 로딩·Zero Hydration·실전 활용

이 글의 핵심

Qwik으로 초고속 웹 앱을 구축하는 완벽 가이드입니다. Resumability, Zero Hydration, 지연 로딩, QwikCity까지 실전 예제로 정리했습니다.

실무 경험 공유: React에서 Qwik으로 전환하면서, Time to Interactive가 5초에서 0.5초로 단축되고 JavaScript 실행이 95% 감소한 경험을 공유합니다.

들어가며: “Hydration이 느려요”

실무 문제 시나리오

시나리오 1: 초기 로딩이 느려요
React는 Hydration이 필요합니다. Qwik은 Resumability로 즉시 인터랙티브합니다.

시나리오 2: JavaScript 실행이 많아요
SPA는 무겁습니다. Qwik은 필요한 것만 로드합니다.

시나리오 3: Time to Interactive가 길어요
Hydration 시간이 깁니다. Qwik은 거의 0ms입니다.


1. Qwik이란?

핵심 특징

Qwik은 Resumability 기반 프레임워크입니다.

주요 장점:

  • Resumability: Hydration 불필요
  • Zero Hydration: 즉시 인터랙티브
  • 지연 로딩: 모든 것이 지연 로드
  • 빠른 속도: TTI 0.5초 이하
  • React-like: 익숙한 문법

성능 비교:

  • React: TTI 5초
  • Next.js: TTI 3초
  • Qwik: TTI 0.5초

2. 프로젝트 설정

설치

npm create qwik@latest

프로젝트 구조

my-qwik-app/
├── src/
│   ├── components/
│   ├── routes/
│   │   ├── index.tsx
│   │   └── layout.tsx
│   └── entry.ssr.tsx
└── public/

3. 컴포넌트

기본 컴포넌트

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

export const Counter = component$(() => {
  const count = useSignal(0);

  return (
    <div>
      <p>Count: {count.value}</p>
      <button onClick$={() => count.value++}>Increment</button>
    </div>
  );
});

핵심 개념:

  • component$: 컴포넌트 정의
  • useSignal: 반응형 상태
  • onClick$: 지연 로드되는 이벤트

4. Routing

파일 기반 라우팅

// src/routes/index.tsx
import { component$ } from '@builder.io/qwik';

export default component$(() => {
  return <h1>Home</h1>;
});

// src/routes/about/index.tsx
export default component$(() => {
  return <h1>About</h1>;
});

// src/routes/posts/[id]/index.tsx
import { useLocation } from '@builder.io/qwik-city';

export default component$(() => {
  const loc = useLocation();
  return <h1>Post {loc.params.id}</h1>;
});

5. Loader & Action

Loader

// src/routes/posts/index.tsx
import { routeLoader$ } from '@builder.io/qwik-city';

export const usePosts = routeLoader$(async () => {
  const posts = await db.post.findMany();
  return posts;
});

export default component$(() => {
  const posts = usePosts();

  return (
    <ul>
      {posts.value.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
});

Action

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

export const useCreatePost = routeAction$(async (data) => {
  const post = await db.post.create({
    data: {
      title: data.title,
      content: data.content,
    },
  });

  return { success: true, post };
});

export default component$(() => {
  const action = useCreatePost();

  return (
    <Form action={action}>
      <input name="title" required />
      <textarea name="content" />
      <button type="submit">Create Post</button>

      {action.value?.success && <div>Post created!</div>}
    </Form>
  );
});

6. useTask$

사이드 이펙트

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

export default component$(() => {
  const count = useSignal(0);
  const doubled = useSignal(0);

  useTask$(({ track }) => {
    track(() => count.value);
    doubled.value = count.value * 2;
  });

  return (
    <div>
      <p>Count: {count.value}</p>
      <p>Doubled: {doubled.value}</p>
      <button onClick$={() => count.value++}>Increment</button>
    </div>
  );
});

7. useResource$

비동기 데이터

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

export default component$(() => {
  const userResource = useResource$(async () => {
    const response = await fetch('/api/user');
    return response.json();
  });

  return (
    <Resource
      value={userResource}
      onPending={() => <div>Loading...</div>}
      onRejected={(error) => <div>Error: {error.message}</div>}
      onResolved={(user) => <div>Hello {user.name}!</div>}
    />
  );
});

8. 최적화

Lazy Loading

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

export const HeavyComponent = component$(() => {
  // 이 컴포넌트는 사용될 때만 로드됨
  return <div>Heavy content</div>;
});

Prefetching

import { Link } from '@builder.io/qwik-city';

export default component$(() => {
  return (
    <Link href="/about" prefetch>
      About
    </Link>
  );
});

정리 및 체크리스트

핵심 요약

  • Qwik: Resumability 기반 프레임워크
  • Zero Hydration: 즉시 인터랙티브
  • 지연 로딩: 모든 것이 지연 로드
  • 빠른 속도: TTI 0.5초 이하
  • React-like: 익숙한 문법
  • QwikCity: 풀스택 프레임워크

구현 체크리스트

  • Qwik 설치
  • 컴포넌트 작성
  • Loader 구현
  • Action 구현
  • useTask$ 활용
  • useResource$ 활용
  • 배포

같이 보면 좋은 글

  • Astro 완벽 가이드
  • SolidJS 완벽 가이드
  • Next.js App Router 가이드

이 글에서 다루는 키워드

Qwik, Resumability, Performance, React, Frontend, SSR, Web Framework

자주 묻는 질문 (FAQ)

Q. React와 비교하면 어떤가요?

A. Qwik이 훨씬 빠릅니다. React는 더 성숙하고 생태계가 큽니다.

Q. Hydration이 정말 없나요?

A. 네, Resumability로 Hydration이 불필요합니다.

Q. 러닝 커브가 높은가요?

A. React와 비슷해서 쉽습니다. $ 문법만 익숙해지면 됩니다.

Q. 프로덕션에서 사용해도 되나요?

A. 아직 초기 단계지만, Builder.io 등에서 사용하고 있습니다.

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3