JavaScript와 Next.js를 활용한 SSR 최적화로 페이지 로딩 속도 60% 개선한 경험 — 5개월간의 도전과 성과 분석
긴 글, 핵심만 먼저 — 이 글을 3줄로 정리해드려요.
웹 애플리케이션의 성능은 사용자 경험에 큰 영향을 미친다. 특히 페이지 로딩 속도는 사용자의 이탈률에 직접적인 영향을 미치기 때문에, 이를 개선하는 것은 개발자가 반드시 고려해야 할 과제다. 이번 글에서는 JavaScript와 Next.js를 활용하여 서버 사이드 렌더링(SSR) 최적화를 통해 페이지 로딩 속도를 60% 개선한 경험을 공유한다.
SSR의 필요성과 도전
SSR은 클라이언트가 아닌 서버에서 HTML을 미리 렌더링하여 클라이언트에 전달하는 방식이다. 이를 통해 SEO 최적화와 초기 로딩 속도 개선을 기대할 수 있다. 하지만 SSR은 서버의 부담을 증가시키고, 클라이언트 측에서 추가적인 JavaScript를 처리해야 하므로 적절한 트레이드오프가 필요하다. 초기 도입 단계에서 성능 저하를 경험하기도 했다. 특히, 요청이 많을 경우 서버의 CPU 사용량이 급증하며, 응답 시간이 늘어나는 문제를 겪었다.
성능 모니터링과 문제 발견
SSR 최적화를 위한 첫 단계는 성능 모니터링이다. Next.js에는 next build와 next start 명령어를 통해 애플리케이션을 배포할 수 있지만, 실제 운영 환경에서의 성능을 측정하는 것이 중요하다. Google Lighthouse와 같은 도구를 사용하여 페이지 성능을 정기적으로 모니터링하였다. 이 과정에서 불필요한 렌더링이 발생하고 있다는 것을 발견했다. 예를 들어, 특정 컴포넌트가 매번 서버에서 렌더링되고 있었고, 이로 인해 응답 속도가 느려지고 있었다.
// 불필요한 서버 렌더링 예시
export async function getServerSideProps() {
const data = await fetchDataFromAPI();
return { props: { data } };
}
이 코드에서 fetchDataFromAPI()가 매번 호출되면서 성능 저하를 발생시켰다. 이를 해결하기 위해 캐싱 전략을 도입하였다.
캐싱 전략과 리팩토링
캐싱은 SSR 최적화에서 중요한 역할을 한다. Redis와 같은 인메모리 데이터 저장소를 활용하여 API 응답을 캐싱하기로 결정했다. 이를 통해 동일한 데이터 요청이 반복될 경우, 서버가 불필요하게 데이터를 처리하지 않도록 하였다. 다음은 캐싱을 위한 간단한 예시 코드이다.
import Redis from 'ioredis';
const redis = new Redis();
export async function getServerSideProps() {
const cacheKey = 'apiData';
let data = await redis.get(cacheKey);
if (!data) {
data = await fetchDataFromAPI();
redis.(cacheKey, .(data));
} {
data = .(data);
}
{ : { data } };
}