๐ Next.js ์ฌํ 8์ฅ: Performance Deep Dive โ Core Web Vitals ์ค์ ํ๋
๐ ๊ฐ์
Core Web Vitals ์ค์ ํ๋ โ LCP, CLS, INP๋ฅผ ๊ฐ์ ํ๋ ๊ตฌ์ฒด์ ์ธ ๊ธฐ๋ฒ์ ๋ค๋ฃน๋๋ค.
๐ ๋ชฉ์ฐจ
- ๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
- ๐ค ์ ์์์ผ ํ๋๊ฐ
- ๐๏ธ ๋น์ ๋ก ๋จผ์ ์ดํดํ๊ธฐ
- ๐ Core Web Vitals โ ์ธ ๊ฐ์ง ์ ์ํ ๐ข
- โก LCP ํ๋ โ ๊ฐ์ฅ ํฐ ์์๋ฅผ ๊ฐ์ฅ ๋น ๋ฅด๊ฒ ๐ก
- ๐ฑ๏ธ INP ํ๋ โ ํด๋ฆญ ๋ฐ์์ 50ms ์ด๋ด๋ก ๐ก
- ๐ CLS ํ๋ โ ๋ ์ด์์ ์ด๋ 0์ผ๋ก ๋ง๋ค๊ธฐ ๐ก
- ๐ฆ ๋ฒ๋ค ์ต์ ํ โ ํ์ํ ๊ฒ๋ง ๋ด๋ ค๋ณด๋ด๊ธฐ ๐ด
- ๐ฅ ์๋ฌ ํด๊ฒฐ ์นดํ๋ก๊ทธ
- ๐ ์ด๋ฒ์ ๋ฐฐ์ด ๋ด์ฉ ์ด์ ๋ฆฌ
- ๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
- ๐ ๋ ์์๋ณด๊ธฐ
๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
โฑ๏ธ ์์ ์ฝ๊ธฐ ์๊ฐ: 18๋ถ (์ ์ฒด) / ํต์ฌ ํํธ๋ง: 9๋ถ
๐บ๏ธ ์ด ๋ฌธ์์ ๋ฐฐ๊ฒฝ ์ธ๊ณ๊ด: '์์๋ค ์ปค๋ฎค๋ํฐ'
- ์์ฒ (์ ์ ): "Lighthouse ์ ์๊ฐ 47์ด์์. ๊ธฐ์ PM์ธ ์์ ์จ๊ฐ 'Performance ์ ์ 90 ์ด์ ์ฌ๋ ค์ผ ํ๋ค'๊ณ ํ๋๋ฐ, ๋ญ ์ด๋ป๊ฒ ๊ฑด๋๋ ค์ผ ํ๋์ง ๋ชจ๋ฅด๊ฒ ์ด์. ์ผ๋จ ์ด๋ฏธ์ง ์์ถํ๋ฉด ๋๋์?"
- ์ํธ(๋ฆฌ๋): "์ด๋ฏธ์ง ์์ถ์ ์์์ด์์. Core Web Vitals ์ธ ๊ฐ์ง(LCP, INP, CLS)๋ฅผ ๊ฐ๊ฐ ์ง๋จํ๊ณ , ์์ธ์ ๋ง๋ ์ต์ ํ๋ฅผ ํด์ผ ํด์. Lighthouse๊ฐ ๊ฐ ์งํ์ ์ ์๋ฅผ ์๋ ค์ฃผ๋๊น ๋ฎ์ ๊ฒ๋ถํฐ ์์๋๋ก ๊ณต๋ตํด๋ด์."
๐บ๏ธ ์ด ๋ฌธ์์ ํ๋ฆ
Core Web Vitals ๊ฐ๋
โ LCP ์ต์ ํ โ INP ์ต์ ํ โ CLS ์ต์ ํ โ ๋ฒ๋ค ์ต์ ํ
๐ฏ ์ด ๋ฌธ์๋ฅผ ๋ค ์ฝ์ผ๋ฉด ํ ์ ์๋ ๊ฒ
- Lighthouse ๊ฒฐ๊ณผ์์ LCP, INP, CLS ์ ์๋ฅผ ๋ณด๊ณ ๊ฐ๊ฐ์ ์์ธ์ ํ์ ํ ์ ์๋ค
- ๊ฐ ์งํ๋ณ ์ต์ ํ ๊ธฐ๋ฒ์ ์ค์ ์ฝ๋๋ก ์ ์ฉํ ์ ์๋ค
-
next/dynamic์ผ๋ก ์ฝ๋ ์คํ๋ฆฌํ ์ ์ ์ฉํด ์ด๊ธฐ ๋ฒ๋ค ํฌ๊ธฐ๋ฅผ ์ค์ผ ์ ์๋ค
๐ค ์ ์์์ผ ํ๋๊ฐ
์ฑ๋ฅ์ ์ธก์ ๊ฐ๋ฅํ ๋น์ฆ๋์ค ์งํ์ผ. ๊ฐ์ด ์๋์ผ:
| ์งํ ๊ฐ์ | ๋น์ฆ๋์ค ์ํฅ |
|---|---|
| LCP 1์ด ๊ฐ์ | ์ดํ๋ฅ 35% ๊ฐ์ |
| INP 200ms ์ดํ ๋ฌ์ฑ | ์ ํ์จ 25% ์ฆ๊ฐ |
| CLS 0.1 ์ดํ ๋ฌ์ฑ | ์๋ชป๋ ํด๋ฆญ์ผ๋ก ์ธํ ์ดํ ๊ฐ์ |
๊ตฌ๊ธ์ Core Web Vitals๋ฅผ ๊ฒ์ ๋ญํน ์ ํธ๋ก ์ฌ์ฉํด. ๋๋ฆฐ ์ฌ์ดํธ๋ ๊ฒ์ ๊ฒฐ๊ณผ์์ ๋ฐ๋ฆฐ๋ค๋ ๋ป์ด์ผ.
๐๏ธ ๋น์ ๋ก ๋จผ์ ์ดํดํ๊ธฐ
๐ง 5์ด์๊ฒ ์ค๋ช ํ๋ค๋ฉด?
์์์ ์๋น์ค ํ์ง์ ์ธ ๊ฐ์ง๋ก ํ๊ฐํด๋ด:
- LCP (์ฒซ ์์์ด ๋์ค๋ ์๋): ์ฃผ๋ฌธํ๊ณ ์ฒซ ์์๊น์ง 2.5์ด ์ด๋ด
- INP (์ข ์ ์ ๋ฐ์ ์๋): ์ ๋ค๋ฉด 200ms ์ด๋ด ๋๋ง์ถค
- CLS (์์ฐจ๋ฆผ ์์ ์ฑ): ์์ ๊ฐ์ ธ๋ค ๋์ ๋ ๋ค๋ฅธ ๊ทธ๋ฆ์ด ๋ฐ๋ ค๋์ง ์์
์ธ ๊ฐ์ง ๋ค ์ข์์ผ "์ข์ ์์์ "์ด์ผ. ์์์ด ๋ง์์ด๋(๊ธฐ๋ฅ) ์๋น์ค๊ฐ ๋์๋ฉด ์๋์ด ์ ์(์ดํ๋ฅ ์ฆ๊ฐ).
๐ Core Web Vitals โ ์ธ ๊ฐ์ง ์ ์ํ ๐ข
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- LCP, INP, CLS๊ฐ ๊ฐ๊ฐ ๋ฌด์์ ์ธก์ ํ๋์ง ์ค๋ช ํ ์ ์๋ค
- ๊ฐ ์งํ์ Good/Needs Improvement/Poor ๊ธฐ์ค๊ฐ์ ์๋ค
| ์งํ | ์๋ฏธ | Good | Poor |
|---|---|---|---|
| LCP | Largest Contentful Paint โ ๋ทฐํฌํธ์ ๊ฐ์ฅ ํฐ ์์ ๋ ๋ ์๋ฃ | โค 2.5s | > 4s |
| INP | Interaction to Next Paint โ ํด๋ฆญยทํค ์ ๋ ฅ ํ ์๊ฐ์ ์๋ต | โค 200ms | > 500ms |
| CLS | Cumulative Layout Shift โ ๋ ์ด์์์ด ์์์น ์๊ฒ ์ด๋ํ ์ด๋ | โค 0.1 | > 0.25 |
์ง๋จ ๋๊ตฌ:
- Lighthouse (Chrome DevTools) โ ๋ก์ปฌ ์๋ฎฌ๋ ์ด์
- Chrome DevTools Performance ํญ โ ์ค์ ๋ ๋๋ง ํ์๋ผ์ธ
- Vercel Speed Insights โ ์ค์ ์ฌ์ฉ์ ๋ฐ์ดํฐ(RUM)
๐ก ํ ์ค๋ก ๊ธฐ์ตํ๊ธฐ
LCP๋ "์ฒซ์ธ์", INP๋ "๋ฐ์์ฑ", CLS๋ "์์ ๊ฐ". ์ธ ๊ฐ์ง ๋ชจ๋ ์ฌ์ฉ์ ์ฒด๊ฐ์ ์ง๊ฒฐ๋ผ.
โก LCP ํ๋ โ ๊ฐ์ฅ ํฐ ์์๋ฅผ ๊ฐ์ฅ ๋น ๋ฅด๊ฒ ๐ก
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- LCP ์์๋ฅผ ์๋ณํ๊ณ
priority,preload๋ก ์ฐ์ ๋ก๋ํ ์ ์๋ค- ๋ ๋๋ง ์ ๋ต์ผ๋ก LCP๋ฅผ ๊ฐ์ ํ๋ ๋ฐฉ๋ฒ์ ์๋ค
LCP๋ฅผ ๋๋ฆฌ๊ฒ ๋ง๋๋ ์ฃผ๋ฒ:
- ํ์ด๋ก ์ด๋ฏธ์ง์
priority์์ โ Lazy Loading์ผ๋ก ๋ฆ๊ฒ ๋ก๋ - TTFB(์๋ฒ ์๋ต) ๋๋ฆผ โ Dynamic Rendering์ธ๋ฐ DB ์ฟผ๋ฆฌ๊ฐ ๋๋ฆผ
- ํฐํธ ๋ก๋ ์ง์ฐ โ Google Fonts ์ธ๋ถ ์์ฒญ
// โ
LCP ์ด๋ฏธ์ง ์ต์ ํ ์ฒดํฌ๋ฆฌ์คํธ
// 1. priority ์ถ๊ฐ (preload ์ฒ๋ฆฌ)
<Image
src="/hero.jpg"
alt="ํ์ด๋ก ๋ฐฐ๋"
width={1200}
height={630}
priority // โ ์ ๋ ๋น ๋จ๋ฆฌ๋ฉด ์ ๋จ
sizes="(max-width: 768px) 100vw, 1200px"
/>
// 2. fetchPriority="high" (Next.js 16+)
<Image
src="/hero.jpg"
fetchPriority="high" // ๋ธ๋ผ์ฐ์ ์ ๋ช
์์ ์ผ๋ก ์ฐ์ ์์ ํํธ
// ...
/>์๋ฒ ์๋ต(TTFB) ๊ฐ์ :
// โ
DB ์ฟผ๋ฆฌ๊ฐ LCP๋ฅผ ๋ฆ์ถ๋ ๊ฒฝ์ฐ โ ISR ๋๋ PPR๋ก ์ ํ
// Dynamic Rendering โ ISR๋ก ๋ฐ๊ฟ TTFB ๊ฐ์
export const revalidate = 60 // 60์ด๋ง๋ค ์ฌ์์ฑ
// ๋๋ PPR๋ก ์ ์ ์
ธ์ ๋จผ์ ๋ณด๋ด๊ณ DB ๋ฐ์ดํฐ๋ ์คํธ๋ฆฌ๋ฐ
export const experimental_ppr = true
// Suspense๋ก DB ์กฐํํ๋ ๋ถ๋ถ๋ง ๊ฐ์ธ๊ธฐ๐ฑ๏ธ INP ํ๋ โ ํด๋ฆญ ๋ฐ์์ 50ms ์ด๋ด๋ก ๐ก
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- INP๊ฐ ๋์ ์์ธ์ ํ์ ํ๊ณ Long Task๋ฅผ ๋ถํ ํ๋ ๋ฐฉ๋ฒ์ ์๋ค
next/dynamic์ผ๋ก ํด๋ผ์ด์ธํธ JS๋ฅผ ์ง์ฐ ๋ก๋ํด ํ์ด๋๋ ์ด์ ๋น์ฉ์ ์ค์ผ ์ ์๋ค
INP๋ ๋ฉ์ธ ์ค๋ ๋๊ฐ ๋ธ๋กํน๋๋ฉด ๋๋น ์ ธ. JS๊ฐ ์คํ ์ค์ด๋ฉด ํด๋ฆญ์ ๋ฐ์์ ๋ชปํ๊ฑฐ๋ .
INP๋ฅผ ๋์๊ฒ ๋ง๋๋ ์ฃผ๋ฒ:
- ํฐ JS ๋ฒ๋ค โ Hydration์ ์ค๋ ๊ฑธ๋ฆผ
- Long Task (50ms ์ด์ ์คํ๋๋ JS)
- ์ด๋ฒคํธ ํธ๋ค๋ฌ์์ ๋ฌด๊ฑฐ์ด ๋๊ธฐ ์ฐ์ฐ
// โ
1. next/dynamic์ผ๋ก ์ฝ๋ ์คํ๋ฆฌํ
โ ์ด๊ธฐ ๋ฒ๋ค์์ ์ ์ธ
import dynamic from 'next/dynamic'
// ์๋ํฐ, ์ฐจํธ ๊ฐ์ ๋ฌด๊ฑฐ์ด ์ปดํฌ๋ํธ๋ ์ง์ฐ ๋ก๋
const RichTextEditor = dynamic(
() => import('@/components/features/posts/RichTextEditor'),
{
loading: () => <div>์๋ํฐ ๋ก๋ฉ ์ค...</div>,
ssr: false, // SSR ์ ์ธ (๋ธ๋ผ์ฐ์ API ์์กด ์ปดํฌ๋ํธ)
}
)
// โ
2. ์๋ฒ ์ปดํฌ๋ํธ๋ก ์ ํ โ ํด๋ผ์ด์ธํธ ๋ฒ๋ค์์ ๋ก์ง ์ ๊ฑฐ
// ํด๋ผ์ด์ธํธ์์ ๋ฐ์ดํฐ ๊ฐ๊ณตํ๋ ๋ก์ง์ ์๋ฒ ์ปดํฌ๋ํธ๋ก ์ด๋
// โ Hydration ๋น์ฉ โ โ INP โ
// โ
3. scheduler.yield()๋ก Long Task ๋ถํ
async function processLargeList(items: string[]) {
for (const item of items) {
await processItem(item)
// ๋ฉ์ธ ์ค๋ ๋์ ์ ์ ์๋ณด โ ํด๋ฆญ ์ด๋ฒคํธ๊ฐ ๋ผ์ด๋ค ์ ์๊ฒ ํจ
if (typeof scheduler !== 'undefined') {
await scheduler.yield()
}
}
}๐ CLS ํ๋ โ ๋ ์ด์์ ์ด๋ 0์ผ๋ก ๋ง๋ค๊ธฐ ๐ก
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- CLS๊ฐ ๋ฐ์ํ๋ ์์ธ 3๊ฐ์ง๋ฅผ ์๊ณ ๊ฐ๊ฐ์ ํด๊ฒฐ์ฑ ์ ์ ์ฉํ ์ ์๋ค
CLS ๋ฐ์ ์์ธ๊ณผ ํด๊ฒฐ์ฑ :
์์ธ 1: ์ด๋ฏธ์ง/์์ ํฌ๊ธฐ ๋ฏธ์ง์
// โ ํฌ๊ธฐ ์๋ ์ด๋ฏธ์ง โ ๋ก๋ ์ ๊ณต๊ฐ ์์ โ ๋ก๋ ํ ๋ ์ด์์ ์ด๋
<img src="/photo.jpg" alt="..." />
// โ
next/image๋ก ๋ฏธ๋ฆฌ ๊ณต๊ฐ ์์ฝ
<Image src="/photo.jpg" alt="..." width={800} height={400} />์์ธ 2: ๋์ ์ผ๋ก ์ฝ์ ๋๋ ์ฝํ ์ธ (๊ด๊ณ , ๋ฐฐ๋)
// โ ๋ฐฐ๋๊ฐ ๋์ค์ ์ฝ์
๋์ด ์๋ ์ฝํ
์ธ ๊ฐ ๋ฐ๋ฆผ
<div> {/* ๋์ค์ ๋ฐฐ๋ ์ฝ์
*/} </div>
<main>๊ฒ์๊ธ ๋ชฉ๋ก</main>
// โ
๋ฐฐ๋ ์์ญ ๋์ด๋ฅผ ๋ฏธ๋ฆฌ ์์ฝ
<div style={{ minHeight: '90px' }}> {/* ๊ด๊ณ ๋ค์ด์ฌ ๊ณต๊ฐ ์์ฝ */} </div>
<main>๊ฒ์๊ธ ๋ชฉ๋ก</main>์์ธ 3: ํฐํธ ๊ต์ฒด(FOUT) โ ํฐํธ ๋ก๋ ์ ํ ๊ธ์ ํฌ๊ธฐ ์ฐจ์ด
// โ
next/font๋ก FOUT ๋ฐฉ์ง + size-adjust๋ก ํฌ๊ธฐ ๋ง์ถค
import { Inter } from 'next/font/google'
const inter = Inter({
subsets: ['latin'],
display: 'swap', // FOUT ํ์ฉํ์ง๋ง layout shift ์ต์ํ
adjustFontFallback: true, // ์์คํ
ํฐํธ์ ํฌ๊ธฐ ์๋ ๋ง์ถค โ CLS ๊ฐ์
})๐ฆ ๋ฒ๋ค ์ต์ ํ โ ํ์ํ ๊ฒ๋ง ๋ด๋ ค๋ณด๋ด๊ธฐ ๐ด
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
@next/bundle-analyzer๋ก ๋ฒ๋ค ๊ตฌ์ฑ์ ์๊ฐํํ ์ ์๋ค- Tree shaking์ด ์ ๋๋ ํจํค์ง๋ฅผ ์ฌ๋ฐ๋ฅด๊ฒ importํ๋ ๋ฐฉ๋ฒ์ ์๋ค
# ๋ฒ๋ค ๋ถ์๊ธฐ ์ค์น
npm install @next/bundle-analyzer// next.config.ts
import withBundleAnalyzer from '@next/bundle-analyzer'
const bundleAnalyzer = withBundleAnalyzer({
enabled: process.env.ANALYZE === 'true',
})
export default bundleAnalyzer({
// ...next config
})# ๋ฒ๋ค ๋ถ์ ์คํ โ ๋ธ๋ผ์ฐ์ ์์ treemap ์๊ฐํ
ANALYZE=true npm run buildํํ ๋ฒ๋ค ์ต์ ํ ํจํด:
// โ lodash ์ ์ฒด import โ ๋ฒ๋ค์ ๋ค ๋ค์ด๊ฐ (70kb+)
import _ from 'lodash'
const result = _.merge(obj1, obj2)
// โ
ํ์ํ ํจ์๋ง import โ Tree shaking
import merge from 'lodash/merge'
const result = merge(obj1, obj2)
// โ
๋๋ ES ๋ชจ๋ ๋ฒ์ ์ฌ์ฉ
import { merge } from 'lodash-es'// โ moment.js โ locale ํ์ผ๊น์ง ๋ค ๋ค์ด๊ฐ (300kb+)
import moment from 'moment'
// โ
date-fns ๋๋ dayjs โ Tree shaking ์๋ฒฝ ์ง์
import { format } from 'date-fns/format'
import { ko } from 'date-fns/locale/ko'next/dynamic์ผ๋ก ํด๋ผ์ด์ธํธ ๋ฒ๋ค ๋ถ๋ฆฌ:
// ๋ฌด๊ฑฐ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ง์ฐ ๋ก๋ โ ์ด๊ธฐ ๋ฒ๋ค ํฌ๊ธฐ ๊ฐ์
const ChartComponent = dynamic(() => import('@/components/features/Chart'), {
ssr: false, // ์๋ฒ์์ ๋ ๋ ์ ํจ
loading: () => <ChartSkeleton />,
})๐ฅ ์๋ฌ ํด๊ฒฐ ์นดํ๋ก๊ทธ
โ Lighthouse LCP๊ฐ ์ด๋ฏธ์ง์ธ๋ฐ ์ ์๊ฐ ๋ฎ์
์ฒดํฌ๋ฆฌ์คํธ:
<Image>์ปดํฌ๋ํธ ์ฌ์ฉ ์ค์ธ๊ฐ?- LCP ์ด๋ฏธ์ง์
priority์์ฑ ์๋๊ฐ? sizes์์ฑ์ด ์ ํํ๊ฐ?- ์ด๋ฏธ์ง๊ฐ CDN์ ์๋๊ฐ (์๋ต ์๋)?
โ window is not defined โ SSR ์ค๋ฅ
์์ธ: ํด๋ผ์ด์ธํธ ์ ์ฉ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ SSR์์ ์คํ.
ํด๊ฒฐ์ฑ :
const HeavyLibrary = dynamic(() => import('@/components/HeavyLibrary'), {
ssr: false, // SSR ๋นํ์ฑํ
})๐ ์ด๋ฒ์ ๋ฐฐ์ด ๋ด์ฉ ์ด์ ๋ฆฌ
๐ ์งํ๋ณ ์ต์ ํ ์์ฝ
| ์งํ | ์์ธ | ํด๊ฒฐ์ฑ |
|---|---|---|
| LCP ๋๋ฆผ | ํ์ด๋ก ์ด๋ฏธ์ง lazy load | priority + sizes |
| LCP ๋๋ฆผ | TTFB ๋๋ฆผ | ISR ๋๋ PPR ์ ํ |
| INP ๋์จ | ํฐ JS ๋ฒ๋ค | next/dynamic ์ฝ๋ ์คํ๋ฆฌํ
|
| INP ๋์จ | Long Task | scheduler.yield() |
| CLS ๋ฐ์ | ์ด๋ฏธ์ง ํฌ๊ธฐ ๋ฏธ์ง์ | width/height ๋ช
์ |
| CLS ๋ฐ์ | ํฐํธ ๊ต์ฒด | next/font + adjustFontFallback |
โ ๏ธ ์ ๋ ํ์ง ๋ง ๊ฒ
| ์ํฉ | โ ๋์ ์ | โ ์ข์ ์ |
|---|---|---|
| ๋ชจ๋ ์ด๋ฏธ์ง์ priority | ์ ๋ถ ์ฆ์ ๋ก๋ โ ๊ฒฝํฉ | LCP ์ด๋ฏธ์ง ํ๋๋ง |
| lodash ์ ์ฒด import | import _ from 'lodash' | import merge from 'lodash/merge' |
| ๋ฒ๋ค ํ์ธ ์ ํจ | ๊ฐ์ผ๋ก ์ต์ ํ | Bundle Analyzer๋ก ์๊ฐํ ํ ๊ฒฐ์ |
๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
Q1. INP(Interaction to Next Paint) ์ ์๋ฅผ ๊ฐ์ ํ๊ธฐ ์ํ ๊ฐ์ฅ ํจ๊ณผ์ ์ธ ๋ฐฉ๋ฒ์?
- A) ํ์ด๋ก ์ด๋ฏธ์ง์
priority์์ฑ ์ถ๊ฐ - B) ์ด๋ฏธ์ง์
width/height์์ฑ ์ถ๊ฐ - C) ๋ฌด๊ฑฐ์ด ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ๋ฅผ
next/dynamic์ผ๋ก ์ง์ฐ ๋ก๋ - D) ํฐํธ๋ฅผ
next/font๋ก ๊ต์ฒด
โ ์ ๋ต: C
ํด์ค: INP๋ ํด๋ฆญ ํ JS ์๋ต์ด ๋๋ฆฐ ๋ฌธ์ ์ผ. ์ด๊ธฐ ๋ฒ๋ค์ ์ค์ด๋ฉด Hydration์ด ๋นจ๋ฆฌ ๋๋๊ณ ํด๋ฆญ ์๋ต์ด ๋นจ๋ผ์ ธ. A, B๋ LCP/CLS ๊ด๋ จ, D๋ CLS ๊ด๋ จ.
๐ ํต์ฌ ๊ธฐ์ต๋ฒ: INP = JS ๋ฌธ์ = ๋ฒ๋ค ์ค์ด๊ธฐ.
Q2. ์๋ ์ค CLS๋ฅผ ๋ฐ์์ํค๋ ์์ธ์ด ์๋ ๊ฒ์?
- A)
<img>ํ๊ทธ์width/height์์ - B) ํ์ด์ง ๋ก๋ ํ ๊ด๊ณ ๋ฐฐ๋๊ฐ ๋์ ์ผ๋ก ์ฝ์ ๋จ
- C)
next/font๋์ Google Fonts<link>ํ๊ทธ ์ฌ์ฉ - D)
<Image>์ปดํฌ๋ํธ์priority์์ฑ ์์
โ ์ ๋ต: D
ํด์ค:
priority์์ผ๋ฉด LCP๊ฐ ๋๋น ์ง์ง๋ง, CLS์๋ ๋ฌด๊ดํด. CLS๋ ๋ ์ด์์์ด ์์์น ์๊ฒ ์ด๋ํ๋ ๊ฑฐ์ผ.๐ ํต์ฌ ๊ธฐ์ต๋ฒ: CLS = "๋ญ๊ฐ๊ฐ ๊ฐ์๊ธฐ ํ์ด๋์์ ๋ด์ฉ์ด ๋ฐ๋ฆฌ๋ ๊ฒ."
Q3. ์น๊ตฌ์๊ฒ ์ค๋ช ํ๋ค๋ฉด?
Core Web Vitals์ ์ธ ์งํ๋ฅผ ์์์ ์๋น์ค์ ๋น๋์ด ์ค๋ช ํด๋ด.
์์ ๋ต๋ณ:
"LCP๋ ์ฃผ๋ฌธ ํ ์ฒซ ์์์ด ๋์ค๋ ์๋์ผ. 2.5์ด ์ด๋ด๋ฉด Good. INP๋ ์ ๋ค์์ ๋ ์ข ์ ์์ด ๋๋ง์ถคํ๋ ์๋์ผ. 200ms ์ด๋ด๋ฉด Good. CLS๋ ์์ ๊ฐ์ ธ๋ค ๋์ ๋ ๋ค๋ฅธ ๊ทธ๋ฆ์ด ๋ฐ๋ฆฌ๋ ์ ๋ฐ๋ฆฌ๋์ผ. ๊ทธ๋ฆ์ด ๊ฐ์๊ธฐ ์ด๋ํ๋ฉด 0.25์ ์ด์์ด๋ผ Poor. ์ธ ๊ฐ ๋ค ์ข์์ผ '๋ณ์ 5์ ์ง๋ฆฌ ์๋น'์ด์ผ."
๐ฃ ์์ฒ ์ด์ ํด๊ทผ ์ผ๊ธฐ
์ค๋์ ์ ๋ง ๋ฅ์คํธ์ ์ฑ๋ฅ์ ๊ทนํ์ผ๋ก ๋์ด์ฌ๋ฆฌ๋ 'Performance Deep Dive' ๋ฅผ ๋ฐฐ์ฐ๋ฉด์ ์ง๋ฆฟํ ์พ๊ฐ์ ๋๋ ๋ ์ด์ผ! ๊ทธ๋์ ๋จ์ํ "๋ด ์ปดํจํฐ์์ ๋นจ๋ผ์" ๋ผ๊ณ ๋ง ์๊ฐํ๋๋ฐ, Lighthouse ์งํ๋ฅผ ๋ณด๋ฉด์ LCP, INP, CLS๋ฅผ ํ๋ํ๋ ๊ฐ์ ํด ๋๊ฐ๋ ๊ณผ์ ์ด ๋ง์น ๊ฒ์ ํ์คํธ๋ฅผ ๊นจ๋ ๊ฒ ๊ฐ์์ด.
๐ก ์ค๋์ ๊ตํ: "์ฑ๋ฅ ์ต์ ํ๋ ๊ฐ์ด ์๋๋ผ ์งํ๋ก ๋งํด์ผ ํ๋ค.
next/image๋ก ์ฉ๋์ ์ค์ด๊ณ ,next/dynamic์ผ๋ก ๋ฒ๋ค์ ์ชผ๊ฐ๋ฉฐ,scheduler.yield()๋ก ์ฌ์ฉ์ ์๋ต์ฑ์ ๋๊น์ง ์ฌ์ํ์!"
์ํธ ๋ฆฌ๋ ๋์ด ์์์ ์๋น์ค ๋น์ ๋ฅผ ๋ค์ด ์ค๋ช ํด ์ฃผ์ค ๋, ๋๊ธฐ ์๊ฐ(LCP) ๋ฟ๋ง ์๋๋ผ ์ข ์ ์์ ์๋ต ์๋(INP)์ ๊ทธ๋ฆ์ ์์ ์ฑ(CLS)๊น์ง ์ฑ๊ฒจ์ผ ์ง์ ํ ๋ช ํ ์๋น์ด๋ผ๋ ๊ฑธ ๊นจ๋ฌ์์ด. ์ค๋ ๋๋ฌด ๋ชฐ์ ํด์ ์ต์ ํํ๋๋ ๋จธ๋ฆฌ๊ฐ ํฝํฝ ๋์๊ฐ๋ค. ํด๊ทผ๊ธธ์ ๋ด๊ฐ ์ข์ํ๋ ๋จ๊ณจ ์๋น์ ๋ค๋ฌ์ '์ฑ๋ฅ ์ข์' ๋ง์๋ ์ ๋ ํ ๋ผ ํด์ผ์ง! ๋ด์ผ์ ๋ '๋น๋ณด๋ค ๋น ๋ฅธ' ์๋น์ค๋ฅผ ๋ง๋๋ ๊ฐ๋ฐ์๊ฐ ๋ ๊ฑฐ์ผ! ๐ฃ