[nextJS] μ±λ₯ μ΅μ ν νκΈ°(lighthouse, λΉλ μ©λ κ°μ )
πͺλ©μΈνμ΄μ§ μ΅μ ννκΈ°
νμ¬ μ§ν μ€μΈ νλ‘μ νΈ λ©μΈνμ΄μ§ μμ μ νκ³ λμ μ±λ₯ κ²μ¬λ₯Ό νλ μ΄λ―Έμ§κ° λ§μμ κ·Έλ°μ§ μ’μ§ μμ μ μλ₯Ό λ°κ³ μμλ€.
LCPμμ μ’μ§ μμ μ μλ₯Ό λ°κ³ μμκ³ , TBTμ SI μμ λΉ λ₯Έ μλλ μλμλ€. μ΄μ λν΄ κ°μ μ μν μ΅μ ν μμ μ μ§νν΄ λ³΄μλ€.
lighthouse μΈ‘μ νλͺ©
FCP(First Contentful Paint): λΈλΌμ°μ κ° μ²« λ²μ§Έ DOMμ μ½ν μΈ λ₯Ό λ λλ§ νλ λ° κ±Έλ¦¬λ μκ°.
LCP(Largest Contenful Paint): λ·°ν¬νΈμμ κ°μ₯ ν° μ½ν μΈ μμκ° νλ©΄μ λ λλ§ λ λκΉμ§ 걸리λ μκ°
TBT(Total Blocking Time): μΉ νμ΄μ§κ° μ¬μ©μμ μ λ ₯μ μλ΅νμ§ λͺ»νλλ‘ μ°¨λ¨λ μκ°
SI(Speed Index): μ½ν μΈ κ° μκ°μ μΌλ‘ νμλλ λ°κΉμ§ 걸리λ μκ°
Cumulative Layout Shift: λλ¦° λ‘λ©, λΉλκΈ° λμ, λμ DOM λ³κ²½ λ±μΌλ‘ λ μ΄μμμ΄ λ³νλ μκ°
1. Dynamic import μ μ©νκΈ°
μ 체 νμ΄μ§μ μν₯μ λ―ΈμΉλ _app.tsxλΆν° μ΅μ νλ₯Ό μ§ννμλ€.
νμ¬ _appμμλ λ―Έλ‘κ·ΈμΈ μ λ‘κ·ΈμΈμ΄ νμν νμ΄μ§μ μ κ·Όνλ κ²½μ° μ΄λ¦¬λ λ‘κ·ΈμΈ λͺ¨λ¬ μ»΄ν¬λνΈμ λ‘λ© μ λνλλ λ‘λ© μ»΄ν¬λνΈλ₯Ό import νκ³ μλ μνμλ€. ν΄λΉ μ»΄ν¬λνΈλ€μ 첫 λ λλ§μ νμν μμκ° μλκΈ° λλ¬Έμ dynamic importλ₯Ό μ μ©νμλ€.
Dynamic Importλ?
ES2020μ λ¬Έλ²μΌλ‘ λͺ¨λμ λΉλ νμμ΄ μλ λ°νμμ λΆλ¬μ€λλ‘ νλ€. λ°λΌμ λ²λ€ νμΌμ λΆλ¦¬νλ©΄μ 첫 λ λλ§μ μ±λ₯μ κ°μ ν μ μλ€. κ·Έλμ λ³΄ν΅ μ²« λ λλ§μ 보μ¬μ§μ§ μλ μ»΄ν¬λνΈ(λͺ¨λ¬ λ±)μ μ μ©ν μ μλ€.
κΈ°μ‘΄μλ promiseλ₯Ό μ¬μ©νμ¬ μ²΄μ΄λμ ν΅ν΄ μ½λλ₯Ό μμ±ν΄μΌ νμ§λ§ nextμμλ dynamic importλ₯Ό μν next/dynamic λͺ¨λμ μ§μνμ¬ κ°νΈνκ² μ¬μ©ν μ μλ€.
- dynamic import μ μ© μ
//μ΄μ
import LoginModal from '@/components/auth/LoginModal';
import { LoadingSpinner } from '@/components/common/LoadingSpinner';
- dynamic import μ μ© ν
//μ΄ν
import dynamic from 'next/dynamic';
const LoginModal = dynamic(() => import('@/components/auth/LoginModal'));
const LoadingSpinner = dynamic(
() => import('@/components/common/LoadingSpinner'),
);
λν λ©μΈνμ΄μ§μ ν΄λΉνλ page/index.tsxμμ νλ¨μ μμΉνμ¬ λ·°ν¬νΈμ λ°λ‘ λ€μ΄μ€μ§ μλ μ»΄ν¬λνΈλ₯Ό dynamic import νμλ€.
const RecordFeedList = dynamic(
() => import('@/components/main/mainRecordFeed/RecordFeedList'),
);
μ΄λ¬ν μμ μ ν΅ν΄ λ²λ€ μ¬μ΄μ¦λ₯Ό μ€μΌ μ μμλ€!
λ©μΈ νμ΄μ§λ μ©λμ΄ μ λ°μΌλ‘ μ€μκ³ _appκ³Ό λ€λ₯Έ νμ΄μ§λ€μ κ²½μ° First Load JS μκ°μ΄ κ°μ λ κ²μ λ³Ό μ μμλ€.
2. μ΄λ―Έμ§ lazy loading
μ±λ₯ μ΅μ νλ₯Ό μν΄ λ·°ν¬νΈμ λ€μ΄μ€λ μ΄λ―Έμ§λ§ λΆλ¬μ¬ μ μλλ‘ lazy loadingμ μ€μ ν μ μλ€.
img νκ·Έμμ lazy loading μμ±μ μ 곡νκΈ° λλ¬Έμ next/Imageμμλ μ μ©μ΄ κ°λ₯νλ€. νμ§λ§ λ°λ‘ loading μ€μ μ νμ§ μμ κ²½μ° κΈ°λ³Έ κ°μΌλ‘ lazyκ° λ€μ΄κ°κΈ° λλ¬Έμ λ°λ‘ μ§μ ν΄μ£Όμ§ μμλ€.
λ°λλ‘ λ·°ν¬νΈμ λ°λ‘ λ€μ΄μ€λ λ©μΈ μ΄λ―Έμ§μΈ κ²½μ° lazy loadingμ νλ©΄ μ€μΊλμμ μ΄λ―Έμ§λ₯Ό μ¨κΈ°κΈ° λλ¬Έμ λΈλΌμ°μ μμ μ΄λ―Έμ§λ₯Ό λ¦κ² μμ²νκΈ° λλ€. κ·Έλ κ² λλ©΄ LCPκ° λ¦μ΄μ§κΈ° λλ¬Έμ λ·°ν¬νΈμμ λ°λ‘ 보μ΄λ μ΄λ―Έμ§μ κ²½μ° next/Imageμ priority μ€μ μ ν΅ν΄ lazy loadingμ ν΄μ νκ³ μ°μ μμλ₯Ό λμΌ μ μλ€.
μ°Έκ³ )
next/Imageμ priority
trueμΈ κ²½μ° μ΄λ―Έμ§κ° λμ μ°μ μμλ‘ κ°μ£Όλμ΄ λ―Έλ¦¬ λ‘λ©λλ©° μ§μ°λ‘λ©μ΄ μλμΌλ‘ λΉνμ±νλλ€. λ·°ν¬νΈ μ¬μ΄μ¦μ λ°λΌ LCPμ ν΄λΉνλ μ΄λ―Έμ§κ° λ€λ₯Ό μ μμΌλ―λ‘ μ¬λ¬ μ΄λ―Έμ§μ μ μ©ν μ μλλ‘ κ³ λ €λμ΄μΌ νλ€.
<Image
src='/images/main/main-banner.svg'
width={390}
height={274}
alt='λ©μΈ μλ΄ λ°°λ'
className='w-full'
priority
/>
3. μ¬μ©νμ§ μλ ν°νΈ μ κ±°νκΈ°
κΈ°λ³Έ ν°νΈλ Noto_Sans_KRλ‘ next/font/googleμμ κ°μ Έμ¬ μ μμ§λ§ λ€λ₯Έ νΉμ ν°νΈλ next/font/localμμ κ°μ ΈμμΌ νλ€. κ·Έλ¬λ ν΄λΉ ν°νΈλ₯Ό woff2 νμΌλ‘ μ¬μ©νμμλ λΆκ΅¬νκ³ μ©λμ΄ μκ°λ³΄λ€ 컀μ λ°μμ€λ μκ°μ΄ κΈΈμλ€. νμ§λ§ μ¬μ©νλ νμ΄μ§λ λ§μ§ μμκΈ° λλ¬Έμ κΈ°μ‘΄μ _appμμ μ μμΌλ‘ import νλ ν°νΈλ₯Ό μ¬μ©νλ μ»΄ν¬λνΈμμ import νλλ‘ μμ νμλ€.
κ·Έλ¬λ μ»΄ν¬λνΈλ§λ€ ν°νΈ μ€μ νλ©΄ μ€λ³΅ μ½λκ° μκΈ°κ³ μ΄νμ μ μ§λ³΄μλ μ΄λ €μΈ κ² κ°μμ νλμ layout μ»΄ν¬λνΈλ₯Ό λ§λ€μ΄μ κ΄λ¦¬νκΈ°λ‘ νμλ€.
- λ μ΄μμ μ»΄ν¬λνΈ μμ±
import localFont from 'next/font/local';
import { ReactNode } from 'react';
const prettyNight = localFont({
src: '../../public/fonts/Cafe24Oneprettynight-v2.0.woff2',
weight: '400',
variable: '--prettyNight',
});
function PrettyNightFontLayout({ children }: { children: ReactNode }) {
return <div className={`${prettyNight.variable} h-full`}>{children}</div>;
}
export default PrettyNightFontLayout;
- λ μ΄μμ μ»΄ν¬λνΈ μ μ©
OnboardingPage.getLayout = function getLayout(page: ReactElement) {
return <PrettyNightFontLayout>{page}</PrettyNightFontLayout>;
};
μ΄ μμ μ ν΅ν΄ ν΄λΉ ν°νΈλ₯Ό μ¬μ©νμ§ μλ νμ΄μ§μμλ λ‘λνμ§ μμμΌλ‘μ¨ LCP μλλ₯Ό κ°μ ν μ μμλ€.
μ΅μ ν ν μ±λ₯ κ²μ¬
TBTμ SIκ° λΉ λ¦μΌλ‘ μ±λ₯μ΄ ν₯μλμκ³ LCPλ μ΅μ μ μλμ§λ§ μ½ 5μ΄ μ λ μ€μΌ μ μμλ€!!
100μ κΉμ§ μλμ§λ§ μ μλ₯Ό μ΄λ μ λ μμ μ μΈ λ²μκΉμ§ μ¬λ¦΄ μ μμ΄μ λ§€μ° κΈ°μλ€!! ππ
κ³μν΄μ λ μ΅μ νν μ μλ λ°©λ²μ΄ μλμ§ κ³΅λΆν΄ λ΄μΌκ² λ€πͺ