🚒
“홈페이지가 너무 느려요!” FD WEB 최적화 후기 (Next.js)
💡
이 글은 웹 프론트엔드 개발 초보가 작성한 글로, 일부 잘못된 내용이 있을 수 있습니다. 이상하거나 잘못된 부분은 댓글을 통해 알려주세요!
 

우리 홈페이지가 느리다고?

프론트엔드 개발에서 중요한 것 중 하나가 바로 컨텐츠 로딩 속도입니다. 느린 반응 속도는 답답함과 지루함을 유발해 사용자 경험을 크게 해치기 때문입니다.

이 부분에 대해 유명한 법칙 중 하나가 바로 도허티 임계(Doherty Threshold)입니다. 도허티 임계는 상호작용 속도가 0.4초 이하면 생산성이 급격히 높아진다는 내용의 법칙입니다. 0.4초가 지나면 사용자의 주의가 분산되고, 집중하기 어려워진다는 것이지요.

물론 강박을 가지고 0.4초를 지켜야 하는 것은 아니지만, Friday 홈페이지는 너무 느렸습니다. 블로그 기능이 추가된 초창기에는 무려 약 3초의 로딩 시간이 필요했어요.

원인 분석

블로그의 로딩 속도를 개선하기 위해 로딩이 오래 걸리는 원인을 파악해봤어요. 브라우저의 개발자 콘솔을 통해 확인해본 결과, 노션에서 글 목록을 가져오는 API가 많은 시간을 차지했어요. 이미 노션을 활용하기로 정했기 때문에, 새로 백엔드 서버를 파고 DB를 구축하기가 힘들었죠.

그래서 선택한 방법은 바로 “이전에 불러온 글 목록을 일단 보여주자!”입니다. 블로그의 특성상 글 목록이 매우 민감하게 실시간으로 변경되지 않기 때문에, 예전에 불러온 목록을 보여주어도 큰 문제가 없었습니다. 그렇다면 이를 어떻게 구현할 수 있을까요?

 
notion image
 

컨텐츠 렌더링 방식

notion image

React.js를 이용해 프론트엔드 페이지를 만들면 기본적으로 CSR(Client Side Rendering) 방식을 사용해요. CSR은 빌드된 js 파일을 클라이언트에서 직접 불러와 실행시킴으로써 렌더링을 하는 방식이에요. 즉, 클라이언트가 렌더링을 담당하는 것이죠.

 
notion image

이와 반대로 서버에서 웹페이지를 렌더링하는 방식이 바로 SSR(Server Side Rendering)이에요. SSR은 서버에서 렌더링을 진행한 후 클라이언트에게는 완성된 html 파일을 전달해줘요. 이 경우 검색 엔진에서 html의 내용을 확인할 수 있어 CSR보다 검색 노출에 유리해요.

CSR과 SSR의 공통점은 사용자의 요청이 생길 때마다 렌더링을 진행한다는 것입니다. 이 경우 자주 변화하는 컨텐츠를 불러오는 데는 유리하겠지만, 그렇지 않은 경우 사용자가 매번 렌더링 시간을 기다려야 하는 문제가 있죠. 이를 해결할 수 있는 방법이 바로 SSG(Static Site Generation)입니다.

 
notion image

SSG는 빌드 타임에 렌더링을 진행하여 미리 html 파일을 만들어놓아요. 사용자 요청시 렌더링이 필요하지 않기 때문에 매우 빠른 속도를 보여주어요. 다만 빌드 타임 이후의 변경 사항에 대해서는 대응하지 못한다는 단점이 있습니다. 변경될 일이 없는 정적 사이트에 유리해요.

그렇다면 Friday 블로그에는 어떤 방식이 적합할까요? 블로그 글은 추가/수정/삭제될 수 있지만, 그 빈도가 적은 편이에요. 이렇게 변경 빈도가 조금 일어나는 경우 SSR과 SSG의 절충안인 ISR(Incremental Static Rendering)을 사용할 수 있어요.

 
notion image

ISR은 SSG처럼 빌드 타임에 렌더링을 진행하는데요, SSG와 다른 점은 바로 재검증(Revalidation)이에요. ISR에서는 개발자가 재검증 빈도를 결정할 수 있는데요, 사용자의 요청이 들어오고 N초 뒤에 백그라운드에서 정적 생성을 진행해요. 처음 요청한 사용자는 예전 정보를 보겠지만, N초 이후에 들어온 다른 사용자는 새롭게 변경된 페이지를 볼 수 있는 것이죠. 블로그처럼 데이터 변화가 있지만 사용자에게 이전의 정보가 보여져도 상관 없는 사이트에 적합해요. 따라서 Friday 블로그는 ISR을 채택했습니다.

 

Next.js에서 ISR 적용하기

Next.js에서는 getStaticProps()를 이용하여 SSG와 ISR을 적용할 수 있어요. 아래는 예시 코드입니다.

import React from 'react';
import axios from 'axios';

export async function getStaticProps() {
	const result = await axios.get('https://...');

	return {
		props: { apiResult: result.data },
		revalidate: 10, // 10초마다 Revalidate. 이 속성이 없으면 SSG 사용.
	};
}

export default function MainPage({ apiResult }) {
	return <div>{apiResult}</div>;
}
 

체감 로딩 시간 줄이기

ISR을 통해 실제 로딩 시간을 줄였다면 이제는 체감 로딩 시간을 줄여볼까요?

체감 로딩 시간과 관련된 유명한 예시는 바로 엘리베이터 거울이에요. 엘리베이터 이동 시간이 길다는 민원을 거울을 설치해 해결했다는 일화, 다들 알고 계시죠? 프론트엔드도 다양한 기법을 통해 체감 로딩 시간을 줄일 수 있는데요, Friday 웹사이트도 여러 방법으로 이를 실천하고 있답니다!

 

뼈대(Skeleton) 보여주기

뼈대는 사용자에게 “이 자리에 무언가가 로딩되고 있음”을 보여주는 훌륭한 도구인데요, 애니메이션을 함께 활용하면 사용자가 덜 지루해지게 할 수 있습니다.

아래는 css의 maskanimation을 활용한 Friday 블로그의 뼈대입니다.

notion image
 

이미지 블러 업(Blur Up) 사용하기

블러 업(Blur Up)이란 원본 이미지를 불러오기 전에 저화질의 이미지를 불러와 미리 보여주는 방법입니다.

사실 프론트엔드 단에서 이미지를 최적화하는 방법에는 여러가지가 있는데요, 대표적으로 보여줄 크기에 맞는 이미지 보여주기레이지 로딩(Lazy Loading, 화면에 이미지가 보여질 때 로딩) 등이 있습니다.

Friday 웹사이트에 들어가는 대부분의 이미지는 위 방법을 모두 사용하고 있답니다! 아래 스크린샷는 코멘토 이미지를 불러오는 동안 블러 이미지를 보여주는 상황입니다.

notion image
 

사실 이를 빠르게 구현할 수 있었던 건 Next/Image 덕분입니다. Next.js에서는 자체 컴포넌트인 <Image />를 활용하여 이미지 띄울 수 있는데요, 다양한 최적화 기능이 탑재되어 있어 편리하게 사용할 수 있습니다. 이미지 최적화와 관련된 자세한 내용은 🔗 공식 문서🔗 이 글에 적혀 있으니 참고해주세요 :)

import React from 'react';
import Image from 'next/image';
import profile from '../assets/images/profile.png';

export default function ProfileImage() {
	return <Image
		src={profile}
		alt='Profile Image'
		placeholder='blur' // Blur Up 사용
		sizes="100px" // 크기 최적화를 위한 사이즈 지정
	/>;
}
 

이 외에도 Friday 웹사이트는 동영상에 poster 옵션을 주어 로딩 전 이미지가 표시되도록 하는 등의 다양한 방법으로 체감 로딩 시간을 줄이고 있습니다. 여러분은 로딩 시간 개선을 위해 어떤 방법을 사용하셨나요? 댓글로 다양한 의견을 남겨주세요 😁

 
방문자 수