Skip to content

필사 모드: 프론트엔드 아키텍처 완전 가이드 — React Server Components·Signals·Islands·Meta Frameworks·Bundlers를 2025년 기준으로 한 번에 정리

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

프롤로그 — "프론트엔드는 왜 또 바뀌었나"

2018년: "React 쓰세요"

2021년: "Next.js 쓰세요"

2023년: "Server Components? Astro? Remix?"

2025년: "Signals? RSC? Islands? Turbopack vs Rspack vs Vite?"

프론트엔드는 **5년마다 혁명**이 반복된다. 2025년의 핵심 단어 6개:

1. **RSC (React Server Components)** — 서버에서 렌더, 번들 0 KB 추가

2. **Signals** — 세밀한 반응성, Virtual DOM 우회

3. **Islands** — 대부분 정적, 필요한 부분만 hydration

4. **Meta Framework** — Next, Remix, Nuxt, SvelteKit

5. **Edge Runtime** — Vercel, Cloudflare에서 V8 isolate 실행

6. **Rust-based Bundler** — Turbopack(Vercel), Rspack(바이트댄스), Biome

클라우드 네이티브(Ep 15)가 인프라를 만들었다면, 프론트엔드는 **그 위에서 사용자를 만난다**.

이 글은 Season 2 Ep 16 — **프론트엔드 아키텍처**.

RSC가 왜 혁명인지, Signals가 왜 Virtual DOM을 우회하는지, Astro Islands의 실전, Meta Framework 4대 비교, State Management 2025 판, Rust Bundler 전쟁까지.

1부 — 프론트엔드 렌더링 모델 6가지

6가지 모델

| 모델 | 설명 | 대표 |

|---|---|---|

| **SPA** | 클라이언트 렌더링 | CRA, Vite + React |

| **MPA** | 서버 페이지 반환 | PHP, Rails |

| **SSR** | 서버 렌더 + Hydration | Next Pages, Nuxt 2 |

| **SSG** | 빌드 시 정적 생성 | Astro, Gatsby, Hugo |

| **ISR** | 정적 + 주기적 재생성 | Next, Astro |

| **Streaming SSR + RSC** | 서버 컴포넌트 + 스트리밍 | Next App Router |

Core Web Vitals로 보는 차이

| 지표 | SPA | SSR | SSG | RSC Streaming |

|---|---|---|---|---|

| FCP (First Contentful Paint) | 느림 | 보통 | 빠름 | 매우 빠름 |

| LCP | 느림 | 보통 | 빠름 | 빠름 |

| TTI (Time to Interactive) | 느림 | 보통 | 빠름 | 빠름 |

| TBT (Total Blocking Time) | 높음 | 보통 | 낮음 | 낮음 |

| SEO | 나쁨 | 좋음 | 최고 | 좋음 |

| 번들 크기 | 크다 | 크다 | 작다 | **최소** |

**2025 표준**: 콘텐츠 사이트는 **Astro/Next RSC**, 앱은 **Next App Router / Remix**.

2부 — React Server Components 이해하기

RSC는 무엇인가

전통 React: 모든 컴포넌트가 클라이언트 번들에 포함

RSC: 일부 컴포넌트를 "서버 전용"으로 표시

→ 서버에서 렌더, 결과만 전송

→ 클라이언트 번들에 포함 X

**효과**:

- 번들 크기 **30-50% 감소**

- DB/파일 접근 컴포넌트 가능

- 큰 라이브러리(marked, shiki 등) 서버만 사용

Server Component vs Client Component

// app/blog/page.tsx — 기본 Server Component

export default async function BlogPage() {

const posts = await db.post.findMany(); // 서버에서 직접 DB 접근

return (

{posts.map(p => <Article key={p.id} post={p} />)}

);

}

// app/Article.tsx — 여전히 Server Component

export function Article({ post }) {

return <article>{post.content}</article>;

}

// app/Comments.tsx — Client Component ('use client' 지시어)

'use client';

export function Comments({ postId }) {

const [text, setText] = useState('');

return <input value={text} onChange={e => setText(e.target.value)} />;

}

Server Component 규칙

**할 수 있는 것**:

- async/await (최상위)

- DB/파일시스템 접근

- 서버 환경 변수

- Node 라이브러리

**할 수 없는 것**:

- `useState`, `useEffect`, 이벤트 핸들러

- 브라우저 API (window, document)

- Context 소비는 가능하지만 Provider는 Client에서

"Pass-through" 패턴

// Server Component

export default function Page() {

return (

);

}

**핵심**: Client Component가 Server Component를 import 할 수 없지만, **children으로 받을 수 있음**.

Streaming SSR + Suspense

export default function Page() {

return (

);

}

**효과**: 느린 부분이 빠른 부분을 막지 않음. 사용자는 LCP에 해당하는 콘텐츠 먼저 봄.

RSC의 한계 2025

- **생태계 미성숙**: 많은 라이브러리가 `use client`를 요구

- **Next App Router 한정**: Remix는 Loader 모델, 다른 방식

- **디버깅 어려움**: 서버/클라이언트 경계 헷갈림

- **Monorepo/패키지 호환성**: UI 라이브러리 author 고생

3부 — Signals — Virtual DOM의 대안

Virtual DOM의 비용

React 렌더링:

1. setState → 컴포넌트 함수 재실행

2. 새 VDOM 트리 생성

3. 이전 트리와 diff

4. 변경된 DOM 노드만 업데이트

문제: 컴포넌트 전체가 재실행됨 → 큰 컴포넌트에서 낭비

Signal 원리

// SolidJS

function Counter() {

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

createEffect(() => console.log('count =', count()));

return (

);

}

**핵심**: `count()`를 읽는 **정확한 DOM 노드**만 업데이트. 컴포넌트 재실행 X.

Signal 프레임워크 비교

| 프레임워크 | Signal API | 특징 |

|---|---|---|

| **SolidJS** | `createSignal`, `createEffect` | JSX but no VDOM, 2020년부터 |

| **Svelte 5 Runes** | `$state`, `$derived`, `$effect` | 컴파일러 최적화, 2024 |

| **Angular 18+** | `signal()`, `computed()`, `effect()` | Google 공식 채택, 2024 |

| **Vue 3 (Refs)** | `ref()`, `computed()` | 오래된 Signal의 선배 |

| **Preact Signals** | `signal()`, `computed()` | React와 호환 가능 |

React의 대응: `use()`와 자체 상태

React는 Signal을 직접 도입하지 않고 **RSC + React Compiler(Forget)**로 대응.

- **React Compiler**: 컴포넌트 자동 memoization

- **Activity API** (실험): 보이지 않는 컴포넌트 상태 유지

언제 Signal을 고를까

**적합**:

- 빈번한 상태 업데이트 (게임, 실시간 대시보드)

- 성능 중요한 UI

- 팀이 새 패러다임 학습 가능

**부적합**:

- 기존 React 생태계 활용

- 대규모 팀 (학습 곡선)

4부 — Islands Architecture

개념

전통 SPA: 전체 페이지가 JS 앱 → 큰 번들, hydration 오래

Islands: 정적 HTML + 필요한 부분만 "섬"처럼 hydration

**장점**: 적은 JS, 빠른 LCP/TTI

**단점**: 섬 간 상태 공유 복잡

Astro 예시

// astro.build/page.astro (서버 실행)

const posts = await db.post.findMany();

{posts.map(p => <article>{p.title}</article>)}

**directive**:

- `client:load` — 즉시

- `client:visible` — IntersectionObserver

- `client:idle` — `requestIdleCallback`

- `client:media="(max-width: 500px)"` — 미디어 쿼리

- (지시어 없음) — 완전 정적

Fresh (Deno), Qwik, Marko

- **Fresh**: Deno의 Astro 아날로그, Preact 기반

- **Qwik**: "Resumable" — hydration 없이 이벤트 시점에 JS 로드

- **Marko**: eBay 제작, Islands 전 조상 (2014)

Islands 2025 현실

**Astro**: 블로그, 문서, 마케팅 사이트 지배

**Qwik**: 실험적이지만 흥미로운 아이디어(Resumability)

**Fresh**: Deno 생태계에서 유지

5부 — Meta Framework 4대 비교 2025

Next.js 15 — 가장 큰 생태계

강점:

- App Router + RSC + Streaming

- Vercel 배포 1-click

- Image, Font, Analytics 내장

- Turbopack (Rust bundler) 개발 모드

- Partial Prerendering (실험) — 정적 shell + 동적 hole

약점:

- 복잡성 (App Router 학습 곡선)

- Vercel 락인 (일부 기능)

- 버전마다 breaking change

Remix (React Router 7로 병합, 2024)

강점:

- Web standards 중심 (Request/Response, FormData)

- Nested routing + Loaders/Actions

- Progressive Enhancement 철학

- React Router 팀 운영 → 라우팅 강력

- 어디든 배포 (Cloudflare, Node, Deno)

약점:

- RSC 지원 늦음 (2025 초)

- 생태계 Next 대비 작음

- Shopify 인수 후 React Router 7 통합

SvelteKit 2 — Svelte 5 Runes 시대

강점:

- Svelte 5 Runes → 최고 성능

- 번들 크기 작음

- SSR/SSG/SPA 모두 지원

- 학습 곡선 낮음

약점:

- 생태계 작음

- 대기업 채택 적음

- Runes 전환으로 혼란 (2024)

Nuxt 3 — Vue 생태계의 Next

강점:

- Vue 3 + Composition API

- Nitro (범용 서버, 여러 런타임 지원)

- 자동 import, auto-route

- 좋은 DX

약점:

- React 대비 생태계 작음

- 국제적 채택 Next 대비 낮음

선택 가이드 2025

팀이 React 숙련 + Vercel 인프라 → Next

Web standards 선호 + Cloudflare → Remix(React Router 7)

성능/번들 크기 우선 + 학습 의지 → SvelteKit

Vue 숙련 + 범용 배포 → Nuxt

콘텐츠 사이트 (블로그/문서) → Astro (React/Vue/Svelte 모두 지원)

6부 — State Management 2025

상태의 4가지 종류

1. **Server State** (서버에서 가져옴) — TanStack Query, SWR

2. **URL State** (쿼리, path) — useSearchParams, nuqs

3. **Form State** — React Hook Form, TanStack Form

4. **Client State** (클라이언트만) — Zustand, Jotai, Redux

TanStack Query — 서버 상태의 표준

function Posts() {

const { data, isLoading, error } = useQuery({

queryKey: ['posts'],

queryFn: () => fetch('/api/posts').then(r => r.json()),

staleTime: 5 * 60 * 1000, // 5분

});

if (isLoading) return <Spinner />;

if (error) return <Error />;

return data.map(p => <Post key={p.id} />);

}

**왜 필수**: 캐싱, 중복 제거, 백그라운드 갱신, optimistic update, pagination, infinite scroll. 전부 기본 제공.

Zustand — Client 상태 승자

const useStore = create((set) => ({

count: 0,

increment: () => set((state) => ({ count: state.count + 1 })),

reset: () => set({ count: 0 }),

}));

function Counter() {

const { count, increment } = useStore();

return <button onClick={increment}>{count}</button>;

}

**장점**: Redux 대비 보일러플레이트 90% 적음, TypeScript 친화적, 4KB.

Jotai — Atomic State

const countAtom = atom(0);

const doubledAtom = atom((get) => get(countAtom) * 2);

function Counter() {

const [count, setCount] = useAtom(countAtom);

const [doubled] = useAtom(doubledAtom);

return <div>{count} / {doubled}</div>;

}

**특징**: Recoil 영감, 작은 atom 조합, dependency tracking 자동.

Redux — 이제 선택? Toolkit으로 완전히 다름

const counterSlice = createSlice({

name: 'counter',

initialState: { count: 0 },

reducers: {

increment: (state) => { state.count += 1; }, // Immer로 mutation OK

},

});

const store = configureStore({ reducer: { counter: counterSlice.reducer } });

**2025 현실**: Redux는 **대규모 엔터프라이즈**에서 여전히 현역. 하지만 **신규 프로젝트는 Zustand/Jotai 선호**.

URL State — nuqs 2025

function Search() {

const [query, setQuery] = useQueryState('q'); // ?q=...

return <input value={query ?? ''} onChange={e => setQuery(e.target.value)} />;

}

**장점**: URL이 Single Source of Truth. 뒤로가기, 공유 가능.

7부 — Form과 Validation 2025

React Hook Form + Zod

const schema = z.object({

email: z.string().email(),

age: z.number().min(18),

});

type FormData = z.infer<typeof schema>;

function SignupForm() {

const { register, handleSubmit, formState: { errors } } = useForm<FormData>({

resolver: zodResolver(schema),

});

return (

{errors.email && <span>{errors.email.message}</span>}

);

}

**2025 조합**:

- **React Hook Form** (unmount 없이 빠름)

- **Zod** (스키마 + 타입)

- **Server Action** (Next) 또는 **Mutation** (TanStack Query)

TanStack Form — 새로운 플레이어 (2024)

headless, framework-agnostic. React Hook Form 대체 가능.

Server Action (Next) — Form의 미래

// actions.ts

'use server';

const schema = z.object({ email: z.string().email() });

export async function subscribe(formData: FormData) {

const parsed = schema.safeParse({ email: formData.get('email') });

if (!parsed.success) return { error: parsed.error.message };

await db.subscriber.create({ data: parsed.data });

return { success: true };

}

// page.tsx

export default function Page() {

return (

);

}

**장점**: No API route, progressive enhancement, 타입 안전.

8부 — Bundler 전쟁 2025

역사

2015: Webpack 지배

2018: Parcel, Rollup 공존

2020: esbuild (Go, 10-100x 빠름)

2020: Vite (esbuild dev + Rollup prod)

2022: Turbopack (Vercel, Rust)

2023: Rspack (ByteDance, Rust, Webpack 호환)

2024: Rolldown (Rollup의 Rust 리라이트)

2025 주요 선수

| Bundler | 언어 | 특징 | 사용처 |

|---|---|---|---|

| **Vite 5** | esbuild + Rollup | 개발 매우 빠름, 생태계 대세 | SvelteKit, Nuxt, Remix |

| **Turbopack** | Rust | Next.js 통합, dev 모드 안정화 | Next.js 15+ |

| **Rspack** | Rust | Webpack 호환 API | Modern.js, ByteDance 사내 |

| **esbuild** | Go | 10x 빠름, 간단 | 라이브러리, 툴링 |

| **Rolldown** | Rust | Rollup 대체 (2025 rc) | Vite의 미래 |

| **Bun** | Zig | 올인원 (bundler + runtime + pm) | 독립 사용 |

Vite가 왜 이겼나

1. Dev: esbuild로 ESM dev server → 즉시 업데이트

2. Build: Rollup으로 프로덕션 번들 (tree-shaking 최고)

3. Plugin 생태계: Rollup 플러그인 호환

4. Framework-agnostic: React, Vue, Svelte, Solid 모두

**2025 체감**: webpack 기반 프로젝트를 Vite로 옮기면 **dev start 30초 → 1초**.

Turbopack vs Rspack

Turbopack: Vercel이 Next.js용으로 최적화, 2025년 prod 모드 stable

Rspack: Webpack 플러그인/로더 그대로 → 마이그레이션 쉬움

선택: Next면 Turbopack, 기존 Webpack 프로젝트면 Rspack

9부 — CSS 전략 2025

3대 방식

1. **Utility CSS (Tailwind)** — 2025 지배적

2. **CSS-in-JS (Emotion, Styled Components)** — 쇠퇴

3. **CSS Modules** — 꾸준, RSC 호환

Tailwind CSS v4 (2024)

**v4 변경**: Rust 기반 엔진 (Oxide), 설정 CSS 파일로 이동, import 한 번.

CSS-in-JS의 몰락

**2024-2025 변화**:

- **Emotion/Styled Components**: RSC 비호환 → 사용률 감소

- **Vanilla Extract**: zero-runtime CSS-in-JS, RSC 호환

- **Panda CSS**: build-time 생성, Chakra 팀 신규 프로젝트

CSS Modules 재부상

// Button.module.css

.button { background: blue; padding: 0.5rem 1rem; }

// Button.tsx

export function Button() {

return <button className={styles.button}>Click</button>;

}

**장점**: zero runtime, RSC 호환, 표준. Next/Remix 기본 지원.

shadcn/ui — 컴포넌트의 새 패턴

npx shadcn@latest add button card dialog

→ components/ui/ 에 코드가 복사됨 (npm install 아님)

**철학**: 패키지 X, 코드 복사. 자유롭게 수정. Radix UI + Tailwind.

**2025 현실**: React UI의 **사실상 표준**. Toolpad, Clerk, Vercel 모두 채택.

10부 — 프론트엔드 테스팅 2025

레이어별 도구

| 레이어 | 도구 | 비율 |

|---|---|---|

| Unit | Vitest (Jest 대체), Bun test | 60% |

| Component | Testing Library + Vitest, Storybook Test | 20% |

| E2E | Playwright (Cypress 대체 중) | 15% |

| Visual | Chromatic, Percy, Playwright | 5% |

Vitest — Jest 킬러

describe('Button', () => {

it('renders', () => {

const { getByRole } = render(<Button>Click</Button>);

expect(getByRole('button')).toHaveTextContent('Click');

});

});

**왜 Jest 대체**: Vite 기반 → 빠름, ESM 네이티브, TypeScript 지원 좋음.

Playwright — E2E의 승자

test('user can sign up', async ({ page }) => {

await page.goto('/signup');

await page.getByLabel('Email').fill('user@example.com');

await page.getByLabel('Password').fill('password');

await page.getByRole('button', { name: 'Sign up' }).click();

await expect(page.getByText('Welcome')).toBeVisible();

});

**장점**: Cypress 대비 빠름, 멀티 브라우저 (Chromium, Firefox, WebKit), parallel 기본.

11부 — 접근성(a11y) 2025

필수 체크리스트 10개

- [ ] 모든 이미지에 alt 텍스트

- [ ] 폼 label 연결 (`<label htmlFor="">`)

- [ ] 키보드 네비게이션 가능 (Tab, Enter, Escape)

- [ ] 포커스 가시성 (focus ring)

- [ ] ARIA roles 올바르게 (버튼은 button, 링크는 a)

- [ ] 색 대비 WCAG AA (4.5:1)

- [ ] 스크린 리더 테스트 (VoiceOver, NVDA)

- [ ] 모션 줄이기 존중 (`prefers-reduced-motion`)

- [ ] 언어 명시 (`<html lang="ko">`)

- [ ] 동적 콘텐츠 알림 (`aria-live`)

도구

- **axe DevTools** (브라우저 확장)

- **Lighthouse** (Chrome DevTools)

- **React Axe** (개발 시 자동 감지)

- **ESLint plugin jsx-a11y**

Radix UI / shadcn/ui

접근성 기본 내장. 직접 구현 금지 — 라이브러리 활용.

12부 — 성능 최적화 12가지

Core Web Vitals 맞추기

1. **이미지 최적화**: WebP/AVIF, `<Image>` 컴포넌트, lazy loading

2. **폰트**: `font-display: swap`, `<link rel="preload">`

3. **CSS 최적화**: Critical CSS inline, 나머지 defer

4. **JS 분할**: `import()` dynamic, route-based splitting

5. **Prefetch**: `<Link prefetch>`, rel="prefetch"

6. **CDN**: 정적 자산 CDN, Edge Functions

7. **Compression**: Brotli (20% 작음 vs gzip)

8. **React Compiler**: 자동 memoization

9. **Server Components**: 번들 감소

10. **Streaming SSR**: TTFB 단축

11. **Database**: N+1 해결, 인덱스

12. **Monitoring**: Vercel Analytics, Web Vitals API

Vercel Speed Insights

export default function Layout({ children }) {

return (

{children}

);

}

**측정**: LCP, FID, CLS, INP, TTFB — 실제 사용자 기준.

13부 — 6개월 로드맵

**1개월차**: Next.js 15 App Router 제대로 이해. RSC vs Client Component 경계

**2개월차**: Tailwind CSS v4 + shadcn/ui로 실전 UI. Radix 접근성 활용

**3개월차**: TanStack Query로 서버 상태, Zustand로 클라이언트 상태

**4개월차**: React Hook Form + Zod + Server Action. 폼 처리 패턴

**5개월차**: Playwright E2E, Vitest 단위 테스트. CI 통합

**6개월차**: Signals 학습 (SolidJS 소형 프로젝트). Islands (Astro) 시도

14부 — 체크리스트 12개

- [ ] Next App Router or Remix or Astro 선택 (프로젝트 성격)

- [ ] Server Components 활용 (번들 감소)

- [ ] TanStack Query로 서버 상태 관리

- [ ] React Hook Form + Zod로 폼 처리

- [ ] Tailwind CSS v4 + shadcn/ui

- [ ] Playwright E2E 테스트

- [ ] 이미지는 Next Image / Astro Image

- [ ] Lighthouse 90+ 점수 유지

- [ ] a11y 체크 (axe DevTools)

- [ ] Core Web Vitals 모니터링

- [ ] Vite / Turbopack / Rspack으로 빠른 dev

- [ ] CDN/Edge 배포 (Vercel, Cloudflare)

15부 — 안티패턴 10가지

1. **useEffect로 데이터 fetching** → TanStack Query 써라

2. **모든 컴포넌트 Client Component** → RSC 활용 못함

3. **Redux 보일러플레이트 폭발** → Zustand/Jotai로 이동

4. **CSS-in-JS runtime** → 성능 저하, RSC 비호환

5. **Any prop drilling** → Context or 상태관리

6. **이미지 최적화 안 함** → LCP 박살

7. **Bundle analyzer 안 씀** → 무엇이 큰지 모름

8. **접근성 무시** → 법적 리스크 (유럽, 미국)

9. **Lighthouse 안 봄** → 사용자 경험 나쁨

10. **폼을 직접 useState로** → React Hook Form 써라

마무리 — "프론트엔드는 계속 변하지만 원칙은 같다"

2025년 프론트엔드의 3대 원칙:

1. **서버에서 할 수 있는 건 서버에서** (RSC, Streaming)

2. **보낼 JS는 최소로** (Islands, Signals, Code splitting)

3. **사용자 경험 우선** (Core Web Vitals, a11y)

프레임워크는 바뀌지만:

- HTML/CSS/JS는 여전히 기반

- 접근성은 영원히 중요

- 성능은 비즈니스에 직결

- 테스트 없으면 리팩터 못함

다음 글은 Season 2 Ep 17 — **테스팅 완전 가이드**.

Unit, Integration, E2E, Property-based, Contract Testing, Mutation Testing, Test Doubles, TDD vs BDD, CI 통합까지.

"코드를 신뢰하려면 테스트가 있어야 한다."

다음 글 예고 — "테스팅 완전 가이드: Unit·Integration·E2E·Property·Contract·Mutation"

Season 2 Ep 17은:

- Testing Pyramid vs Trophy

- Test Doubles (Stub/Mock/Fake/Spy)

- Property-based Testing (fast-check, Hypothesis)

- Contract Testing (Pact)

- Mutation Testing (Stryker)

- Snapshot Testing 현명하게

- TDD vs BDD vs TLD

- CI 통합 전략

테스트 없으면 리팩터 없다. 다음 글에서.

현재 단락 (1/397)

2018년: "React 쓰세요"

작성 글자: 0원문 글자: 12,689작성 단락: 0/397