LogoSEO Jing
  • All Posts
  • SEO Jing
  • okayJing
  • KD Team
  • CLAB Coreteam
  • Study

Contact Me

© 2026 SEOJing. All rights reserved.

WebPerformanceSSRNext.jsVite

vinext는 왜 빠를까? — SSR, Vite, Edge, 그리고 Web Vitals까지

2026년 3월 25일·19분 읽기

시작점: vinext는 왜 빠르다고 할까?

vinext는 Cloudflare가 만든 Vite 기반 Next.js 대체 프레임워크다. 빌드 속도 4.4배, 번들 크기 57% 감소를 자랑하고, vinext deploy 한 줄이면 Cloudflare Workers에 배포된다.

그런데 "빠르다"라고만 알고, 왜 빠르고 어떻게 빠른지에 대한 이해가 없었다. Next.js가 빠른 이유가 SSR 때문이라면, SSR은 왜 빠르고 어떻게 빠른 건지. 이 글은 그 질문에서 출발해서 웹 렌더링 성능의 전체 그림을 처음부터 정리한 기록이다.


CSR vs SSR: 왜 SSR이 빠른가

CSR (Client-Side Rendering)

브라우저가 빈 HTML을 받고, JS를 다운로드하고, 실행해서 DOM을 만든다. JS가 실행될 때까지 사용자는 빈 화면을 본다.

서버 → 빈 HTML 전송 → JS 다운로드 → JS 실행 → DOM 생성 → 화면 표시

SSR (Server-Side Rendering)

서버가 미리 HTML을 완성해서 보낸다. 사용자는 JS가 로드되기 전에 이미 콘텐츠를 볼 수 있다.

서버에서 HTML 완성 → 완성된 HTML 전송 → 화면 즉시 표시 → JS 다운로드 → Hydration
지표CSRSSR
FCP (First Contentful Paint)

Post Q&A

오케이징에게 물어보기

vinext는 왜 빠를까? — SSR, Vite, Edge, 그리고 Web Vitals까지 전체를 기준으로 질문과 피드백을 받아요.답을 본 뒤에는 이 내용을 댓글로 달아서 서징에게도 물어볼 수 있어요. 작성자가 직접 볼 수 있어요!

0/500

포스트 목록

/SEOJing
파일 15개, 폴더 1개
Cloudflare Workers에서 fs 모듈이 안 되는 이유와 해결법대표 이미지 자동화 실험 — 검색과 Codex 생성이 같은 경로로 붙었다본문 이미지를 나중에 넣는 게 아니라 — SEOJing 글쓰기 파이프라인에 시각 판단을 넣기모바일 웹에서 가로 모드를 강제하는 5가지 방법 — iOS Safari에서도 동작하는 코드 뷰어 만들기블로그 글을 PPT로 만들기 — DOM 클로닝 기반 프레젠테이션 모드100vh가 100%가 아닌 이유 — 모바일 뷰포트 단위 완전 정리Context로 퀴즈 컴포넌트를 만들다 막혀서 React.Children을 공부하게 된 이야기대표 이미지를 글마다 다시 붙이는 방식 — 사진 검색에서 리소그래프 배경까지글 위에 영상을 붙인다는 것 — SEOJing 요약 쇼츠 파이프라인localStorage 읽기에서 하이드레이션 에러가 터지는 이유 useSyncExternalStore로 해결useEffect cleanup과 의존성 배열 — 실전 버그 사례로 이해하는 생애주기vinext + GitHub Actions로 Cloudflare Workers 배포하기vinext 오픈소스 기여기: 한국어 slug가 RSC에서 이슈를 일으킨 이유RSC 환경에서 WebAssembly가 차단되는 이유 — Shiki에서 rehype-prism-plus로vinext는 왜 빠를까? — SSR, Vite, Edge, 그리고 Web Vitals까지

같은 섹션의 대표 이미지

39 posts · latest first
본문 이미지를 나중에 넣는 게 아니라 — SEOJing 글쓰기 파이프라인에 시각 판단을 넣기 글의 대표 이미지
SEO Jing26. 06. 22.

본문 이미지를 나중에 넣는 게 아니라 — SEOJing 글쓰기.

SEOJing에서 새 글을 쓸 때 대표 이미지와 본문 이미지를 빼먹지 않도록, 블로그 맵과 글쓰기 파이프라인 안에 시각 판단 단계를 넣은 과정을 정리했다.

26. 06. 22.SEOJing
느림 (JS 실행 후)
빠름 (HTML 도착 즉시)
TTI (Time to Interactive)JS 실행 후Hydration 완료 후
SEO불리 (빈 HTML)유리 (완성된 HTML)

SSR이 "빠르다"는 건 정확히는 사용자가 콘텐츠를 처음 보는 시점(FCP)이 빠르다는 뜻이다. 인터랙션 가능 시점(TTI)은 Hydration이 끝나야 하므로 CSR과 비슷하거나 오히려 느릴 수도 있다.


Hydration: 정적 HTML에 생명 불어넣기

서버가 보낸 정적 HTML에 JS 이벤트 핸들러를 붙여서 인터랙티브하게 만드는 과정이다.

정적 HTML (클릭해도 반응 없음)
        ↓ Hydration
동적 HTML (클릭, 입력 등 가능)

핵심은 서버가 만든 HTML과 클라이언트 JS가 1:1로 매칭되어야 한다는 것이다. 서버에서 안녕을 보냈는데 클라이언트 JS가 반가워를 기대하면 불일치가 생겨서 Hydration Error가 발생한다.


Next.js의 렌더링 전략들

Next.js는 SSR만 하는 게 아니라 여러 렌더링 전략을 페이지별로 조합한다.

  • SSG (Static Site Generation): 빌드 시점에 HTML 생성. CDN에서 바로 서빙하므로 가장 빠르다.
  • SSR: 요청마다 서버에서 HTML 생성.
  • ISR (Incremental Static Regeneration): SSG인데 일정 시간마다 재생성.
  • Streaming SSR: HTML을 한번에 보내지 않고 준비된 부분부터 스트리밍.

빌드 도구: webpack이 느린 이유, Vite가 빠른 이유

webpack (Next.js 기본)

webpack은 모든 모듈을 하나의 의존성 그래프로 묶어서 처리한다. 프로젝트가 커지면 이 그래프가 거대해지고, 빌드 시간이 급격히 늘어난다. 개발 서버도 시작할 때 전체 앱을 번들링하기 때문에 느리다.

모든 파일 읽기 → 의존성 그래프 구축 → 변환 → 번들링 → 최적화 → 출력

Vite가 빠른 이유 3가지

1. 개발 시 번들링을 안 한다. 브라우저의 네이티브 ES Modules를 활용한다. import문을 브라우저가 직접 해석하므로, 요청이 온 파일만 그때그때 변환한다.

[webpack] 모든 모듈 → 번들링 → 하나의 파일 → 브라우저
[Vite]    브라우저가 import 요청 → 해당 파일만 변환 → 브라우저로 전송

2. esbuild로 사전 번들링. node_modules의 라이브러리만 esbuild(Go로 작성, JS 대비 10-100배 빠름)로 미리 번들링해둔다.

3. 프로덕션 빌드에 Rollup 사용. Rollup은 ES Modules 기반으로 설계되어 tree-shaking(사용하지 않는 코드 제거)이 webpack보다 효율적이다.

webpack (Next.js)Vite
Dev 서버 시작전체 번들링 (느림)번들링 없음 (빠름)
HMR (코드 수정 반영)모듈 체인 재빌드수정된 파일만 변환
사전 번들링 도구JS 기반esbuild (Go, 10-100x)
프로덕션 번들webpack

Node.js Runtime vs Edge Runtime

Node.js (일반 서버)

서버가 특정 지역(예: 미국 동부)에 있다. 전 세계 어디서든 접근은 가능하지만, 물리적 거리만큼 네트워크 지연이 생긴다.

한국 사용자   → 미국 서버 (왕복 ~200ms) → 응답 느림
미국 사용자   → 미국 서버 (왕복 ~20ms)  → 응답 빠름

Edge Runtime

전 세계 CDN 엣지 노드(수백 개 지점)에서 코드가 실행된다. 사용자와 가까운 곳에서 실행하니까 네트워크 지연이 줄어든다.

한국 사용자   → 한국 엣지 노드   (~20ms) → 빠름
미국 사용자   → 미국 엣지 노드   (~20ms) → 빠름
브라질 사용자 → 브라질 엣지 노드 (~20ms) → 빠름

엣지 노드는 개인 PC가 아니라, Cloudflare나 AWS 같은 기업이 전 세계에 설치해둔 데이터센터(서버)다. Cloudflare는 300개 이상 도시에 데이터센터를 두고 있다.

무겁다 vs 가볍다

Edge에서는 가벼운 V8 Isolate 런타임을 쓴다. V8 엔진만 있어서 JS/WASM만 실행 가능하고, 시작 시간이 약 5ms다. Node.js는 V8 위에 파일시스템, 네트워크 등 OS 기능을 붙인 것이라 무겁지만, 뭐든 할 수 있다.

Node.jsEdge (V8 Isolate)
기능전부 가능 (fs, net 등)JS/WASM만
DB 직접 연결가능불가 (HTTP API로 우회)
시작 속도~200ms+~5ms
배포 위치특정 리전

Vercel은 두 런타임을 모두 제공하고, 페이지별로 선택할 수 있다.

ts
export const runtime = "nodejs"; // 기본값
export const runtime = "edge"; // Edge Runtime

vinext가 빠른 이유 정리

지금까지의 내용을 합치면 vinext의 구조가 보인다.
Next.js:  Next.js API  →  webpack/Turbopack  →  Node.js 서버 (특정 리전)
vinext:   Next.js API  →  Vite (esbuild+Rollup)  →  Cloudflare Workers (전 세계 Edge)
  1. 빌드 도구: webpack 대신 Vite를 쓰므로 빌드가 4.4배 빠르다.
  2. 번들 크기: Rollup의 tree-shaking이 더 공격적이어서 번들이 57% 감소한다.
  3. 런타임: Node.js 서버 대신 Edge에서 실행하므로 사용자와 가까운 곳에서 응답한다.

Web Vitals: "빠르다"를 숫자로 말하기

Google이 정한 사용자 체감 성능의 3가지 핵심 지표다. 실제로 검색 순위(SEO)에도 영향을 준다.

LCP (Largest Contentful Paint)

가장 큰 콘텐츠가 화면에 나타나는 시간. 메인 이미지, 큰 텍스트 블록, 비디오 썸네일 등이 해당한다.

페이지 요청
  │  0.5s  헤더, 네비게이션 표시
  │  1.2s  메인 이미지 표시 ← 이게 LCP
  │  1.8s  나머지 로딩

좋음: ≤ 2.5s / 나쁨: > 4s

각 렌더링 방식의 LCP 영향: CSR은 JS 실행 후에야 표시되므로 느리고, SSG는 미리 만든 HTML을 CDN에서 서빙하므로 가장 빠르다.

INP (Interaction to Next Paint)

사용자가 클릭/탭/키 입력 후 화면이 반응하기까지의 시간. FID를 대체하여 2024년 3월부터 Core Web Vitals 지표가 됐다.

사용자가 버튼 클릭
  │  JS 이벤트 핸들러 실행
  │  상태 업데이트
  │  DOM 변경
  │  화면 다시 그림 ← 여기까지가 INP

좋음: ≤ 200ms / 나쁨: > 500ms

SSR/CSR보다 클라이언트 JS 코드의 효율성과 관련이 크다. Hydration이 무거우면 JS 메인 스레드가 막혀서 INP가 나빠진다.

CLS (Cumulative Layout Shift)

페이지 로딩 중 레이아웃이 얼마나 흔들리는가. 광고가 갑자기 끼어들어서 읽던 텍스트가 밀리는 경험이 대표적이다.

좋음: ≤ 0.1 / 나쁨: > 0.25

흔한 원인: 이미지 크기 미지정, 폰트 로딩 후 글자 크기 변경, 동적 콘텐츠 삽입. 해결: width/height 명시, font-display: swap + 비슷한 크기의 fallback 폰트, Skeleton으로 공간 미리 확보.

지표측정하는 것좋음 기준개선하는 기술
LCP콘텐츠 표시 속도≤ 2.5sSSR, SSG, Streaming, CDN
INP인터랙션 반응 속도≤ 200msRSC, 코드 스플리팅, 가벼운 Hydration
CLS레이아웃 안정성

RSC (React Server Components)

핵심 질문: 이 컴포넌트가 꼭 브라우저에서 실행돼야 하는가? 대부분의 컴포넌트는 데이터를 받아서 UI를 그리기만 한다. 클릭 이벤트도 없고 상태도 없다. 이런 걸 굳이 브라우저에 JS로 보낼 필요가 없다.

tsx
// Server Component (기본값, 'use client' 없음)
// 서버에서만 실행됨. 브라우저로 JS가 안 감.
async function PostList() {
  const posts = await db.query("SELECT * FROM posts");
  return (
    <ul>
      {posts.map((p) => (
        <li key={p.id}>{p.title}</li>
      ))}
    </ul

Server Component는 JS 번들에 포함되지 않고, Hydration도 필요 없다. 페이지에 컴포넌트가 20개 있을 때 상태/이벤트가 필요한 3개만 Client Component로 두면, 나머지 17개는 JS 번들에서 빠진다.

  • 번들 크기 감소 → LCP 개선
  • Hydration 대상 감소 → INP 개선

SSR과 RSC의 차이

SSR은 "HTML을 어디서 만드는가"이고, RSC는 "컴포넌트가 어디서 실행되는가"이다. SSR은 서버에서 HTML을 만들어 보내지만, JS 번들은 전부 보내고 Hydration도 전부 한다. RSC는 Server Component를 JS 번들에서 아예 제외하고, Hydration 자체가 필요 없다. 둘은 경쟁이 아니라 같이 쓰이는 관계다.


Streaming SSR: 준비된 것부터 보내기

일반 SSR은 페이지 전체가 렌더링될 때까지 기다린 후 한번에 보낸다. DB 조회가 3초 걸리면, 사용자는 3초 동안 아무것도 못 본다.

Streaming SSR은 준비된 부분부터 쪼개서 보낸다. 느린 부분을 기다리지 않고 빠른 부분부터 보여줄 수 있다.

tsx
<Layout>
  {" "}
  {/* 즉시 전송 */}
  <Header /> {/* 즉시 전송 */}
  <Suspense fallback={<CommentSkeleton />}>
    <Comments /> {/* DB 조회 끝나면 전송. 그 전엔 Skeleton 표시 */}
  </Suspense>
</Layout

Suspense는 React에서 "아직 준비 안 됐으면 대신 이걸 보여줘"를 선언하는 컴포넌트다. Comments가 데이터를 가져오는 동안 fallback에 넣은 Skeleton을 보여주고, 준비되면 자동으로 교체한다.

Skeleton은 로딩 중일 때 보여주는 빈 뼈대 UI다. 실제 콘텐츠와 비슷한 모양의 회색 박스를 보여줘서 "콘텐츠가 곧 나오겠구나"라고 예측할 수 있게 한다. 빈 화면이나 스피너보다 체감 로딩이 빠르게 느껴지고, 공간을 미리 잡아놓으므로 CLS도 줄어든다.


CDN과 캐싱

CDN (Content Delivery Network)

전 세계에 서버(엣지 노드)를 두고 콘텐츠를 복사해놓는 것이다. Edge Runtime이 "코드를 엣지에서 실행"하는 거라면, CDN은 "이미 만들어진 파일을 캐싱해서 전달"하는 거다.

1. 첫 요청: 한국 사용자 → 한국 엣지 노드 (없음) → 미국 원본 서버 → 응답 + 엣지에 복사본 저장
2. 다음 요청: 한국 사용자 → 한국 엣지 노드 (있음!) → 바로 응답 (20ms)

Cache-Control 헤더

서버가 "이 응답을 얼마나 캐싱해도 되는지" 브라우저와 CDN에게 알려주는 방법이다. Next.js 같은 프레임워크가 렌더링 방식에 맞게 알아서 설정해주므로, 대부분은 커스텀 헤더를 쓸 필요가 없다.

# 정적 파일 (JS, CSS, 이미지) - 파일명에 해시가 있어서 변경 시 URL이 바뀜
Cache-Control: public, max-age=31536000, immutable  → 1년간 캐싱

# HTML 페이지 - 항상 최신인지 확인
Cache-Control: public, max-age=0, must-revalidate  → 매번 확인

# 개인 데이터 (마이페이지 등)
Cache-Control: private, no-store  → CDN 캐싱 금지

stale-while-revalidate

가장 중요한 캐싱 패턴이다. 캐시가 좀 오래됐어도 일단 보여주고, 뒤에서 조용히 업데이트한다. 사용자는 항상 즉시 응답을 받는다.

Cache-Control: public, max-age=60, stale-while-revalidate=3600

0~60초:      캐시 그대로 반환 (신선함)
60~3660초:   캐시 그대로 반환 + 백그라운드에서 새 버전 가져옴
3660초 후:   캐시 만료, 새로 요청

ISR (Incremental Static Regeneration)

SSG는 빌드 시점에 HTML을 만들어 CDN에 올리므로 가장 빠르다. 하지만 데이터가 바뀌면 전체를 다시 빌드해야 한다. 페이지가 1000개면 1000개 전부.

ISR은 이 문제를 해결한다. stale-while-revalidate 패턴을 렌더링에 적용한 것이다.

tsx
// Next.js에서 ISR 설정
export const revalidate = 60; // 60초
빌드: HTML 생성 → CDN에 올림

0~60초:  CDN 캐시 그대로 반환 (SSG처럼 빠름)
60초 후: 다음 요청이 오면
         1. 일단 기존 캐시 반환 (사용자는 기다림 없음)
         2. 원본 서버가 해당 페이지 HTML만 새로 생성
         3. 새 HTML로 CDN 캐시 교체
         4. 다음 요청부터 새 HTML 서빙

핵심은 1000페이지 전부가 아니라 요청이 온 그 페이지 1개만 재생성한다는 것이다. 요청이 안 오면 재생성도 안 한다. 그래서 "Incremental(점진적)"이다.

"옛날 걸" 받는 사람은 재생성을 트리거한 딱 한 명이고, 그 사람도 정상적인 페이지를 본다. 데이터가 60초 전 버전일 뿐이다. 블로그 글이나 상품 설명처럼 실시간성이 필요 없는 콘텐츠에 적합하다.

속도데이터 신선함서버 부하
SSG가장 빠름빌드 시점 고정없음
SSR느림항상 최신매 요청마다
ISRSSG와 동일

PPR (Partial Prerendering)

ISR의 한계: 페이지 전체를 정적 또는 동적으로 취급한다. 상품 페이지에서 이름/설명/이미지는 거의 안 바뀌지만, 가격/재고는 실시간으로 바뀐다. ISR로는 페이지 전체를 60초마다 재생성해야 한다.

PPR은 한 페이지 안에서 정적/동적을 컴포넌트 단위로 분리한다.

tsx
export default function ProductPage() {
  return (
    <div>
      {/* 정적: 빌드 시 생성, CDN 캐싱 */}
      <ProductName />
      <ProductImage />
      <Description />

      {/* 동적: 요청 시 Streaming SSR */}
      <Suspense fallback={<PriceSkeleton />}>
사용자 요청 시:
1. CDN에서 정적 부분 + Skeleton 즉시 전송
2. 서버에서 동적 부분 준비되면 Streaming으로 전송
3. Skeleton이 실제 데이터로 교체

Web Vitals 관점에서: 정적 부분이 CDN에서 바로 오니까 LCP가 SSG급이고, RSC로 Hydration 대상이 줄어서 INP가 가벼워지고, Skeleton으로 공간을 확보해놨으니 CLS도 안정적이다.


전체 진화 흐름

CSR (느림)
 → SSR (LCP 개선, 서버 부하)
   → SSG (빠름, 데이터 고정)
     → ISR (빠름 + 데이터 갱신)
       → PPR (컴포넌트별로 정적/동적 분리) ← 현재 최신

vinext는 이 흐름 위에서, 빌드 도구를 Vite로 바꾸고 런타임을 Edge로 올린 것이다. 렌더링 전략의 진화(SSR → SSG → ISR → PPR)와 빌드/런타임의 진화(webpack → Vite, Node.js → Edge)는 별개의 축이고, vinext는 후자를 개선한 프레임워크다.

SEO Jing26. 06. 22.

글 위에 영상을 붙인다는 것 — SEOJing 요약.

SEOJing 글을 소셜용 영상으로 따로 소비시키는 게 아니라, 포스트 상단 요약과 블로그 유입 장치로 연결하기 위해 summaryVideo frontmatter와 Supertonic3 기반 요약 쇼츠 파이프라인을 붙인 과정을 정리했다.

26. 06. 22.SEOJing
대표 이미지 자동화 실험 — 검색과 Codex 생성이 같은 경로로 붙었다 글의 리소그래프 스타일 대표 이미지 배경
SEO Jing26. 06. 21.

대표 이미지 자동화 실험 — 검색과 Codex 생성이 같은.

SEOJing 블로그에 대표 이미지를 자동으로 붙이는 실험을 실제로 돌려봤다. 검색 기반 cover 삽입과 Codex CLI 기반 정적 SVG 생성이 같은 frontmatter 경로로 연결됐다.

26. 06. 21.SEOJing
대표 이미지를 글마다 다시 붙이는 방식 글의 리소그래프 스타일 대표 이미지 배경
SEO Jing26. 06. 21.

대표 이미지를 글마다 다시 붙이는 방식 — 사진 검색에서.

SEOJing 포스트 목록을 파일 탐색기처럼만 두지 않고, 최신 글부터 실제 사진 기반 리소그래프 배경을 붙이는 실험을 정리합니다. 이미지는 배경만 만들고, 제목과 아이콘은 블로그 UI가 맡는 쪽으로 방향을 바꿨습니다.

26. 06. 21.SEOJing
SEO Jing26. 04. 01.

Day 12 - 테스트 커버리지 개선, 모바일 프레젠테이션 버그 2건.

SEO Jing 개발 열두째 날. code-block 테스트 14개 추가로 커버리지 대폭 개선, 모바일 프레젠테이션에서 FullscreenView 방향 전환 문제와 스크롤 멈춤 버그 수정.

26. 04. 01.SEOJing
SEO Jing26. 04. 01.

useEffect cleanup과 의존성 배열 — 실전 버그.

useEffect 의존성 배열에 불필요한 값이 포함되면 cleanup과 재실행이 뒤엉켜 DOM 상태가 꼬일 수 있다. 프레젠테이션 모드에서 발생한 모바일 스크롤 고착 버그를 통해 원인과 해결 패턴을 정리한다.

26. 04. 01.SEOJing
SEO Jing26. 03. 25.

Day 11 - 프레젠테이션 확대 기능,.

SEO Jing 개발 열한째 날. PC 프레젠테이션 확대/축소 컨트롤 추가, 모바일 orientation 판단 로직 개선, FullscreenView를 독립 컴포넌트로 분리 및 PC 대응.

26. 03. 25.SEOJing
SEO Jing26. 03. 25.

vinext 오픈소스 기여기: 한국어 slug가 RSC에서.

한국어 MDX 블로그를 만들다 vinext 프레임워크의 ByteString 버그를 발견하고, 이슈를 작성하고, PR을 올리기까지의 과정

26. 03. 25.SEOJing
SEO Jing26. 03. 25.

vinext는 왜 빠를까? — SSR, Vite, Edge,.

vinext가 빠른 이유를 이해하기 위해, SSR부터 Hydration, 빌드 도구, Edge Runtime, Web Vitals, RSC, CDN 캐싱, ISR, PPR까지 웹 렌더링 성능의 전체 그림을 정리한다

26. 03. 25.SEOJing
SEO Jing26. 03. 24.

100vh가 100%가 아닌 이유 — 모바일 뷰포트 단위 완전 정리.

모바일 Safari에서 100vh가 화면을 넘치는 이유, vh/svh/lvh/dvh의 차이, JavaScript에서 실제 뷰포트를 구하는 방법, 그리고 전체화면 UI를 만들 때 알아야 할 CSS zoom과 모바일 판정 패턴까지 정리한다.

26. 03. 24.SEOJing
SEO Jing26. 03. 23.

Day 10 - 프레젠테이션 모드 안정화.

SEO Jing 개발 열째 날. 프레젠테이션 모드의 모바일 UX 문제들을 전면 수정. 퀴즈·코드블록·이미지·포스트목록 처리 개선, 롱프레스 UX 및 하단 바 레이아웃 안정화. 모바일 뷰포트·리스트 분할 문제 수정, 채움 비율 보수적으로 조정, 포스트 탐색기 자연 정렬 적용.

26. 03. 23.SEOJing
SEO Jing26. 03. 22.

Day 9 - 프레젠테이션 모드, 코드블럭 개선, 테스팅.

SEO Jing 개발 아홉째 날. 프레젠테이션 기능 추가, 퀴즈 구조 변경, 모바일 반응형, 코드블럭 사용성, 테스팅 도입.

26. 03. 22.SEOJing
SEO Jing26. 03. 22.

모바일 웹에서 가로 모드를 강제하는 5가지 방법 — iOS.

모바일 웹에서 코드 블록을 가로로 넓게 보여주고 싶었다. screen.orientation.lock()은 iOS에서 안 되고, PWA manifest는 브라우저에서 무시된다. 결국 CSS transform으로 가짜 회전을 만들었고, 그 과정에서 엄지 접근성까지 고민하게 됐다.

26. 03. 22.SEOJing
SEO Jing26. 03. 22.

블로그 글을 PPT로 만들기 — DOM 클로닝 기반.

MDX 파일을 수정하지 않고, 렌더된 DOM을 h2 기준으로 자르고 화면 높이에 맞춰 자동 페이지네이션하는 프레젠테이션 모드를 만들었다. 리스트 높이 측정이 왜 틀리는지 디버깅한 과정과, ul/ol을 li 단위로 분할하는 해결책을 정리한다.

26. 03. 22.SEOJing
SEO Jing26. 03. 21.

Day 8 - 아티클 퀴즈와 스터디 자료.

SEO Jing 개발 여덟째 날. 아티클 퀴즈 디자인시스템 구현과 스터디 대면 자료 작성.

26. 03. 21.SEOJing
SEO Jing26. 03. 21.

Context로 퀴즈 컴포넌트를 만들다 막혀서.

MDX 블로그에 퀴즈 컴포넌트를 만들면서, Context 기반 Compound Component로 시작했다가 index 문제에 막혀 React.Children API를 채택하게 된 과정을 정리한다.

26. 03. 21.SEOJing
SEO Jing26. 03. 20.

Day 7 - Front Matter CMS와 관련 게시물.

SEO Jing 개발 일곱째 날. Front Matter CMS 설치와 관련 게시물 이동 탐색기 구현.

26. 03. 20.SEOJing
SEO Jing26. 03. 18.

Day 6 - 스터디 자료 작성과 데스크탑 비율 수정.

SEO Jing 개발 여섯째 날. 데스크탑 비율 수정과 씨랩 스터디 사전 진단 자료 작성.

26. 03. 18.SEOJing
SEO Jing26. 03. 17.

Day 5 - shiki 제거, MDX 모듈화, 그리고.

SEO Jing 개발 다섯째 날. shiki를 rehype-prism-plus로 교체하고, gray-matter 직접 구현, MDX 모듈화, 페이지 내 검색, 테이블 디자인시스템까지.

26. 03. 17.SEOJing
SEO Jing26. 03. 16.

Day 4 - 배포와 CI/CD.

SEO Jing 개발 넷째 날. lint, codecov, Cloudflare 배포, fs 런타임 이슈.

26. 03. 16.SEOJing
SEO Jing26. 03. 16.

Cloudflare Workers에서 fs 모듈이 안 되는 이유와.

배포 후 블로그 포스트가 404를 반환하던 문제부터, gray-matter eval 차단, next-mdx-remote eval 차단까지 — 세 겹으로 터진 이슈를 하나씩 해결한 기록

26. 03. 16.SEOJing
SEO Jing26. 03. 16.

localStorage 읽기에서 하이드레이션 에러가 터지는 이유.

localStorage를 읽는 컴포넌트에서 하이드레이션 불일치가 발생하는 원인과, useState+useEffect가 아닌 useSyncExternalStore가 정답인 이유를 정리한다.

26. 03. 16.SEOJing
SEO Jing26. 03. 16.

vinext + GitHub Actions로.

vinext 프로젝트를 GitHub Actions로 Cloudflare Workers에 자동 배포하는 방법과 실제 겪은 트러블슈팅 기록

26. 03. 16.SEOJing
SEO Jing26. 03. 16.

RSC 환경에서 WebAssembly가 차단되는 이유 —.

코드 하이라이팅에 Shiki를 쓰면 왜 RSC에서 WebAssembly.instantiate() 에러가 터지는지, 그리고 빌드 타임 하이라이팅으로 어떻게 해결했는지 정리한다.

26. 03. 16.SEOJing
SEO Jing26. 03. 15.

엄청난 피드백.

CLI 코드 리뷰에서 받은 피드백과 전체 코드 수정 계획을 정리했다.

26. 03. 15.SEOJing
SEO Jing26. 03. 15.

생각보다 어려웠던 댓글, 완독 로컬스토리지.

localStorage만으로 글 읽기 추적, 스크롤 진행률, 댓글 감지를 구현한 과정을 정리했다.

26. 03. 15.SEOJing
SEO Jing26. 03. 15.

MDX 관련 이슈 노트.

블로그 디테일 페이지에서 MDX를 렌더링하기 위해 검토한 라이브러리들과 최종 선택 과정.

26. 03. 15.SEOJing
SEO Jing26. 03. 15.

Day 3 - MDX 이슈, 반응형, 다크모드.

SEO Jing 개발 셋째 날. MDX 라이브러리 이슈, 반응형, 코드블럭, 댓글, 다크모드, 코드 리뷰.

26. 03. 15.SEOJing
SEO Jing26. 03. 14.

디자인 시스템을 구축할 때 주의할 점.

디자인 시스템 구현 시 파일 구조, 디자인 토큰, 유의 사항을 정리했다.

26. 03. 14.SEOJing
SEO Jing26. 03. 14.

폰트는 왜 메인 페이지에서만 적용이 안되고 있었을까?.

Tailwind v4 환경에서 폰트가 메인 페이지에서만 적용되지 않던 원인과 Hydration Mismatch 이슈를 정리했다.

26. 03. 14.SEOJing
SEO Jing26. 03. 14.

MDX DOM 트리 파싱하기.

MDX 파일의 경로 탐색 로직과 콘텐츠 트리 생성 과정을 정리했다.

26. 03. 14.SEOJing
SEO Jing26. 03. 14.

결국 Node.js 까지 와버렸다.

MDX 파일 구조를 JSON으로 변환하기 위해 Node.js의 fs 모듈을 배워봤다.

26. 03. 14.SEOJing
SEO Jing26. 03. 14.

Day 2 - 블로그 스켈레톤과 MDX 파싱.

SEO Jing 개발 둘째 날. 디자인 시스템 확장, 블로그 스켈레톤, 폰트 이슈 해결.

26. 03. 14.SEOJing
SEO Jing26. 03. 13.

전체적인 플로우.

SEO Jing 프로젝트의 기술 스택 선정과 전체적인 개발 플로우 정리.

26. 03. 13.SEOJing
SEO Jing26. 03. 13.

Storybook으로 디자인 시스템 테스팅하기.

Storybook의 사용법과 디자인 시스템 개발에서의 장점을 정리했다.

26. 03. 13.SEOJing
SEO Jing26. 03. 13.

MDX가 뭘까?.

MDX의 개념과 블로그에서 활용하는 이유를 정리했다.

26. 03. 13.SEOJing
SEO Jing26. 03. 13.

Day 1 - 디자인 컨셉과 디자인 시스템.

SEO Jing 개발 첫째 날. 디자인 컨셉 설정과 디자인 시스템 구축을 시작했다.

26. 03. 13.SEOJing
SEO Jing26. 03. 13.

기술 블로그를 직접 제작하게 된 이유.

SEO Jing을 개발하게 된 이유입니다.

26. 03. 13.SEOJing
SEO Jing26. 03. 13.

왜 자꾸 프로젝트가 중단되는지.

프로젝트가 중단되는 이유에 대한 자기 회고입니다.

26. 03. 13.SEOJing
Rollup (더 나은 tree-shaking)
전 세계 엣지
≤ 0.1
이미지 크기 지정, Skeleton, 폰트 최적화
>
);
}
// Client Component ('use client' 선언)
// 브라우저에서 실행됨. JS 번들에 포함됨.
("use client");
function LikeButton() {
const [liked, setLiked] = useState(false);
return <button onClick={() => setLiked(true)}>좋아요</button>;
}
>
약간의 지연
revalidate 시에만
<Price />
</Suspense>
<Suspense fallback={<StockSkeleton />}>
<StockCount />
</Suspense>
</div>
);
}