Replies: 1 comment
-
왜 본인 프로젝트에서 사이트 로딩 속도 최적화를 위한 노력이 필요한지 제목에 내용추가가 필요합니다. |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
사이트 로딩 속도 최적화를 위한 노력들
부제: 캐싱 전략 및 콘텐츠 최적화를 통한 성능 개선
이 프로젝트는 현재 진행 중인 사이트의 로딩 속도를 최적화하기 위해 다양한 기법을 연구하고, Next.js 13 이상의 최신 기능을 활용하여 성능을 개선하는 방법을 탐구합니다. 캐싱 처리, 이미지의 미니마이징, 그리고 BFF(Backend for Frontend) 패턴을 통합하여 실무에 적용할 수 있는 방법론을 제시하며, 실제 프로젝트에 적용해 성능 개선의 수치적인 차이와 효과를 검증합니다.
프론트엔드 성능을 극대화하기 위한 실질적인 전략들을 실제 프로젝트에 적용해 성능 개선의 수치적인 차이와 효과를 검증합니다.
서론 : 왜 사이트 로딩 속도 최적화가 중요한가?
오늘날의 디지털 환경에서는 사용자 경험이 곧 비즈니스 성공을 좌우하는 중요한 요소로 자리 잡았습니다. 특히, 동행을 구하는 커뮤니티 플랫폼과 같은 서비스에서는 사용자 간의 빠른 상호작용과 원활한 정보 공유가 필수적입니다. 사용자는 동행을 구하기 위해 게시글을 작성하고, 타인의 게시글을 탐색하며, 실시간으로 채팅을 통해 의사소통을 합니다. 이 모든 과정에서 사이트의 로딩 속도는 사용자 만족도와 직결되며, 나아가 플랫폼의 활성화에도 중요한 영향을 미칩니다.
사용자들은 느린 페이지 로딩이나 지연된 채팅 반응에 쉽게 실망할 수 있습니다. 특히, 동행 매칭과 같은 서비스에서는 즉각적인 반응과 정보 접근이 핵심이기 때문에, 로딩 속도가 저하되면 사용자 이탈로 이어질 위험이 큽니다. 반대로, 최적화된 성능은 더 많은 사용자가 플랫폼을 신뢰하고 적극적으로 활용하게 만듭니다.
이 프로젝트에서는 Next.js 14와 React를 기반으로 구축된 커뮤니티 플랫폼의 로딩 속도를 최적화하기 위한 다양한 기법을 탐구합니다. 특히, 캐싱 전략을 활용해 네트워크 요청을 최소화하고, 콘텐츠 최적화를 통해 이미지 크기를 줄여 로딩 시간을 단축시키며, BFF(Backend for Frontend) 패턴을 적용하여 프론트엔드가 필요로 하는 데이터만을 효율적으로 제공하는 방법을 다룹니다. 이러한 최적화는 실시간 채팅의 반응 속도를 개선하고, 동행 후기 작성과 같은 기능이 원활히 작동하도록 돕습니다.
커뮤니티 플랫폼의 핵심은 빠르고 신뢰성 있는 사용자 경험입니다. 이에 따라 사이트 로딩 속도를 최적화하는 것은 단순히 기술적인 개선을 넘어서, 사용자들이 플랫폼을 지속적으로 이용하도록 유도하고, 궁극적으로 서비스의 성공을 보장하는 중요한 과제입니다. 이번 프로젝트에서는 성능 최적화의 실질적인 효과를 검증하고, 이를 통해 사용자 경험을 극대화하는 방법을 제시할 것입니다.
Next 13버전 이상에서 추가된 캐싱 기능들
Next.js 공식문서에는 네 개의 캐싱 기법이 정리되어 있다. Router Cache, Full Route Cache, Request Memoization, Data Cache 이렇게 네 가지가 공식 문서에 소개가 되어있다.
Next.js에서 서버사이드 fetch API를 직접 만들어서 Next.js 13부터는 Next자체적으로 서버 사이드 캐싱을 할 수 있게 되었다. 그렇기 때문에 공식문서에서도 비동기 API 호출에 있어 Axios가 아닌 fetch를 추천한다.
Web API에서 제공하는 fetch API는 원래 브라우저에서만 사용할 수 있는 브라우저용 API이지만, Next에서 fetch API를 서버사이드 컴포넌트에서 사용할 수 있도록 구현한 것으로 보여진다.
이 캐싱 기법은 유저가 날린 하나의 Request에 대해 현재 서버에서 다른 서버로 날리는 모든 요청에 대해 2개 이상의 같은 HTTP 요청이 있다면 1번의 요청만을 날리게 하여 DB 부하를 직접적으로 줄일 수 있는 수단이 된다.
이러한 이유로, Next.js 서버 사이드에서 fetch를 사용할 때, 주의해야할 점으로는 Opt out이나 수동 revalidation을 안할 시, 자동으로 영원히 첫 요청이 캐싱될 수 있다.
Router Cache
유저가 방문한 모든 페이지의 서버 컴포넌트를 캐싱하여 페이지 재방문 시 로딩을 빠르게 해주고, 새로고침 시 & 주기적으로 데이터를 새로 가져와서 페이지 별 cache가 invalidate(새로고침)된다.
캐시는 브라우저의 임시 메모리에 저장된다.
라우터 캐시가 지속되는 두 가지 요인은 세션과 무효화기간이다.
-> 캐시는 탐색 중에 유지가 되지만, 새로고침시 지워진다.
-> 각 세그먼트(이동하면서 저장된 모든 페이지)마다 prefetch={null} (default)인 경우 dynamic 페이지는 캐싱 되지 않고, static 페이지는 5분간 캐싱된다. prefetch={true}인 경우 dynamic, static 페이지 모두 5분간 캐싱된다. 이후 자동 무효화된다.
참고
⚡️ 성능 개선 사례: 채팅 서비스 링크 썸네일 캐싱 전략
문제 개요
채팅 서비스에서 링크의 OG 태그를 스크래핑하여 썸네일을 표시하는 기능을 구현했으나, 캐싱 전략을 사용하지 않아 성능 저하와 사용자 경험이 크게 떨어지는 문제가 발생했습니다. 주요 문제는 다음과 같습니다.
문제 원인
1. 메시지 내 링크 수만큼 각 링크의 HTML에서 OG 태그 스크래핑 과정의 반복적인 요청
2. 화면 로딩 지연 및 깜빡임 현상
썸네일이 로드될 때까지 빈 화면 또는 깜빡임이 발생해 사용자 경험이 저하됨.
3. 스크롤 위치의 불안정성
4. 동일 링크의 반복 로딩
해결 방안 및 개선 사항
썸네일 크기를 고정해 로딩 전후의 UI 변화를 최소화하여, 로딩 중에도 채팅방의 길이를 유지할 수 있도록 수정했습니다.
썸네일 로딩이 완료되더라도 스크롤의 위치를 하단에 유지시켜, 사용자가 중간에 멈춘 스크롤을 수동으로 조정할 필요를 없앴습니다.
썸네일이 로딩되기 전 빈 컴포넌트 대신 스켈레톤 애니메이션을 적용해, 로딩 시간 동안 사용자에게 자연스러운 화면 전환을 제공했습니다.
이를 통해 빈 화면 노출과 깜빡임 문제를 해소하고, 로딩 중에도 안정적인 사용자 경험을 제공할 수 있었습니다.
캐싱 전략을 적용하여, 동일한 URL에 대해 OG 태그를 여러 번 불러오는 것을 방지했습니다.
클라이언트 캐싱 (sessionStorage): 이미 로드된 OG 태그 정보를 세션 스토리지에 저장하여 동일한 링크에 대한 중복 요청을 방지.
서버 캐싱 (메모리 캐싱): 동일한 URL에 대해 서버가 여러 번 요청하지 않도록 메모리 캐싱을 적용.
예를 들어, 카카오맵의 실시간 위치 전송 링크와 같은 동일한 URL의 OG 태그는 한 번만 로드한 후, 재사용하도록 개선했습니다.
이를 통해 네트워크 요청을 줄이고, 중복된 데이터 로딩 시간을 단축하여 성능을 대폭 개선했습니다.
OG 태그를 스크래핑하는 과정이 느려지는 문제를 해결하기 위해, 캐싱 전략을 도입하여 같은 사이트에 대한 요청을 반복하지 않도록 하고, 이미 한 번 가져온 썸네일 데이터를 캐싱하여 다시 방에 들어왔을 때 이를 재사용하는 방법을 고민해보았다.
이러한 캐싱 전략을 구현하려면 두 가지 요소가 필요합니다:
서버 측 캐싱: 서버에서 동일한 URL에 대한 메타데이터를 여러 번 긁어오지 않도록 캐싱.
클라이언트 측 캐싱: 클라이언트가 이미 받아온 링크의 메타데이터를 저장하고, 같은 링크에 대해 다시 요청할 때 캐싱된 데이터를 사용하는 방식.
캐싱 전략 개요:
클라이언트 캐싱 (sessionStorage):
서버 캐싱 (메모리 캐싱):
Next.js 14에서의 전체 사용 흐름:
클라이언트 측 캐싱: LinkPreview 컴포넌트에서 sessionStorage를 사용해 메타데이터를 캐시합니다.
서버 측 캐싱: scrapeMeta에서 서버 메모리 캐시를 사용하여 일정 시간(10분 동안) 데이터를 유지합니다. 카카오맵 링크의 경우 패턴에 따라 캐싱합니다.
캐싱된 메타데이터 재사용: 클라이언트가 방에 다시 들어올 때 이미 캐시된 메타데이터를 세션 스토리지 또는 서버 메모리에서 불러옵니다.
2. 서버 측 캐싱 구현
서버에서 동일한 URL에 대해 반복 요청을 하지 않도록 캐시를 저장할 수 있습니다. 이를 위해 Redis와 같은 인메모리 데이터베이스를 사용할 수 있지만, 간단하게 로컬 메모리에 캐싱하는 방법도 가능합니다.
성능 개선 결과 (50개 기준)
썸네일 로딩 시간: 기존 4초에서 1.62초로 약 59.5% 개선.
이미지 요청 최적화:
캐싱 전에는 링크 하나당 17~21ms가 소요되었으며, 채팅창에 링크가 50개가 있을 경우 최대 21ms * 50회의 요청이 발생.
캐싱 적용 후에는 동일한 링크에 대해 1회의 요청만 발생하므로, 전체 요청 시간이 1/n로 감소.
요청 수 감소:
예를 들어, 채팅창에 동일한 링크가 50개 있을 경우, 캐싱 적용 전에는 50번의 개별 요청이 발생하였으나, 캐싱 적용 후에는 단 1회의 요청만 발생.
단점: 썸네일 이미지가 다 다른 링크에 대해서는 캐싱의 효과가 덜할 수 있으나, 카카오맵과 같은 자주 사용되는 링크는 캐싱을 통해 성능과 사용자 경험이 크게 개선됨.
특별한 성능 개선 사례:
서비스에서 기본적으로 제공하는 현재 위치 전송 기능의 링크는 항상 동일한 카카오맵 썸네일이기 때문에, 해당 링크에 대해 캐싱을 적용하면 성능과 UX 모두 큰 효과를 볼 수 있음.
scrape 요청 제거:
캐싱 적용 전에는 매번 scrape 요청을 통해 각 링크에서 HTML OG 태그를 긁어왔으나, 캐싱 적용 후에는 단 1회도 요청하지 않음.
결과적으로 OG 태그 fetch 요청이 0회로 줄어들어, 이 부분에서 실질적으로 100% 시간 절감이 이루어짐.
문제해결 전
커넥트립 _ 국내 여행 동행 커뮤니티 (1).webm
https://www.awesomescreenshot.com/video/31443519?key=47cf21beff0e910ac8d7d81b41491d1d
캐싱적용후
커넥트립 _ 국내 여행 동행 커뮤니티 (2).webm
https://www.awesomescreenshot.com/video/31446660?key=6594bb41863f712b84c72bb47032974a
썸네일 뜨는 시간까지 소요되는 시간
약 4초 -> 1.62초로 59.5% 개선되었습니다.
Beta Was this translation helpful? Give feedback.
All reactions